diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..aeae059c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,54 @@
+# See http://help.github.com/ignore-files/ for more about ignoring files.
+
+# compiled output
+/builds
+/release
+/dist
+/tmp
+/out-tsc
+# Only exists if Bazel was run
+/bazel-out
+
+# dependencies
+/node_modules
+
+# profiling files
+chrome-profiler-events.json
+speed-measure-plugin.json
+
+# IDEs and editors
+*.idea
+.project
+.classpath
+.c9/
+*.launch
+.settings/
+*.sublime-workspace
+
+# IDE - VSCode
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+.history/*
+
+# misc
+/.sass-cache
+/connect.lock
+/coverage
+/libpeerconnection.log
+npm-debug.log
+yarn-error.log
+testem.log
+/typings
+*venv
+
+# System Files
+.DS_Store
+Thumbs.db
+
+protos/csharp
+scenario
+bin
+*.pyc
\ No newline at end of file
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 00000000..63a5852b
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,76 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Electron-File",
+ "type": "chrome",
+ "request": "launch",
+ "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
+ "windows": {
+ "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd"
+ },
+ "runtimeArgs": [
+ "${workspaceRoot}/main.js",
+ "--remote-debugging-port=9222",
+ "--open-dev-tools",
+ "--disable-splash",
+ ],
+ "webRoot": "${workspaceRoot}"
+ },
+ {
+ "name": "Electron-Localhost",
+ "type": "chrome",
+ "request": "launch",
+ "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
+ "windows": {
+ "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd"
+ },
+ "runtimeArgs": [
+ "${workspaceRoot}/main.js",
+ "--remote-debugging-port=9222",
+ "--open-dev-tools",
+ "--disable-splash",
+ "--locahost"
+ ],
+ "webRoot": "${workspaceRoot}/src"
+ },
+ {
+ "name": "Attach",
+ "type": "node",
+ "request": "attach",
+ "port": 9222,
+ },
+ {
+ "name": "Karma-Test-Debug",
+ "type": "chrome",
+ "request": "attach",
+ "address": "127.0.0.1",
+ "port": 9333,
+ "webRoot": "${workspaceRoot}",
+ "pathMapping": {
+ "/": "${workspaceRoot}/",
+ "/base/": "${workspaceRoot}/"
+ }
+ },
+ {
+ "name": "Debug Main Process",
+ "type": "node",
+ "request": "launch",
+ "cwd": "${workspaceFolder}",
+ "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
+ "windows": {
+ "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd"
+ },
+ "args" : [
+ ".",
+ "--open-dev-tools",
+ "--disable-splash",
+ "--localhost"
+ ],
+ "outputCapture": "std"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 00000000..8b105763
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,44 @@
+{
+ "git.ignoreLimitWarning": true,
+ "javascript.format.insertSpaceAfterConstructor": true,
+ "javascript.format.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": true,
+ "javascript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": true,
+ "javascript.format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": true,
+ "javascript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": true,
+ "javascript.format.insertSpaceBeforeFunctionParenthesis": true,
+ "typescript.updateImportsOnFileMove.enabled": "always",
+ "typescript.format.insertSpaceAfterConstructor": true,
+ "typescript.format.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": true,
+ "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": true,
+ "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": true,
+ "typescript.format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": true,
+ "typescript.format.insertSpaceBeforeFunctionParenthesis": true,
+ "editor.detectIndentation": false,
+ "files.exclude": {
+ "**/.git": true,
+ "**/.svn": true,
+ "**/.hg": true,
+ "**/CVS": true,
+ "**/.DS_Store": true,
+ "**/*.pyc": true
+ },
+ "python.pythonPath": "/usr/bin/python",
+ "python.testing.unittestArgs": [
+ "-v",
+ "-s",
+ "./python-client/",
+ "-p",
+ "test_*.py"
+ ],
+ "python.testing.pytestEnabled": false,
+ "python.testing.nosetestsEnabled": false,
+ "python.testing.unittestEnabled": true,
+ "editor.rulers": [
+ 80,120
+ ],
+ "typescript.tsdk": "node_modules\\typescript\\lib",
+ "javascript.validate.enable": true,
+ "typescript.validate.enable": true,
+ "html.format.wrapAttributes": "aligned-multiple",
+ "html.format.wrapLineLength": 240
+}
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 00000000..7e6e2d8f
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,22 @@
+# Nodejs Base image
+FROM node:12 as build
+
+WORKDIR /app
+
+ENV PATH /app/node_modules/.bin:$PATH
+
+# install and app dependencies
+
+COPY package.json /app/package.json
+
+RUN npm install
+
+RUN npm install -g @angular/cli
+
+# add app
+COPY . /app
+
+EXPOSE 4200 49153
+
+# start app
+CMD ng serve --host 0.0.0.0 --poll
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 00000000..cbce2ef3
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1 @@
+Use of the Truevision Designer is governed by the terms of the Truevision Designer End User License Agreement, which can be found at https://www.truevision.ai/eula
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..80a9be3d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,39 @@
+# Truevision Designer
+
+Truevision Designer is a 3D tool to design roads, intersections and environments for testing and validating autonomous vehicles and robots.
+
+From this repository you can build the Truevision Designer for Windows and Linux.
+
+[![Video](docs/img/welcome.jpg)](https://www.youtube.com/watch?v=7jej46ALVRE)
+
+
+## Features
+
+- Create and modify roads, lanes
+- Import/Export OpenDrive 1.4
+- Export to CARLA simulator
+- Export 3D environment as GLTF or GLB file
+- Import and place 3d assets in the environment using various prop tools
+
+## Download
+
+Click here to download []
+
+## Documentation
+
+Latest documentation can be found at [https://www.truevision.ai/designer/docs](https://www.truevision.ai/designer/docs)
+
+## Tutorials
+
+Latest tutorials and videos can be found at [https://www.youtube.com/channel/UCv4Gkf0Z0JyWdKQHakXpbAw/videos](https://www.youtube.com/channel/UCv4Gkf0Z0JyWdKQHakXpbAw/videos)
+
+## Recommended System
+- Intel i7 or above
+- 16 GB RAM
+- Ubuntu/Windows
+
+## License
+
+In brief, you are free to use, modify Truevision Designer for all non-commercial purposes only. Please read the license for more details.
+
+Your access to and use of Truevision Designer on GitHub is governed by the Truevision Designer End User License Agreement. If you don't agree to those terms, as amended from time to time, you are not permitted to access or use Truevision Designer.
\ No newline at end of file
diff --git a/angular.json b/angular.json
new file mode 100644
index 00000000..503f8462
--- /dev/null
+++ b/angular.json
@@ -0,0 +1,138 @@
+{
+ "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
+ "version": 1,
+ "newProjectRoot": "projects",
+ "projects": {
+ "truevision": {
+ "root": "",
+ "sourceRoot": "src",
+ "projectType": "application",
+ "prefix": "app",
+ "schematics": {},
+ "architect": {
+ "build": {
+ "builder": "@angular-devkit/build-angular:browser",
+ "options": {
+ "outputPath": "dist",
+ "index": "src/index.html",
+ "main": "src/main.ts",
+ "polyfills": "src/polyfills.ts",
+ "tsConfig": "src/tsconfig.app.json",
+ "assets": [
+ "src/favicon.ico",
+ "src/assets"
+ ],
+ "styles": [
+ "src/assets/styles/app.scss",
+ "src/styles.css"
+ ],
+ "scripts": [],
+ "es5BrowserSupport": true
+ },
+ "configurations": {
+ "production": {
+ "fileReplacements": [
+ {
+ "replace": "src/environments/environment.ts",
+ "with": "src/environments/environment.prod.ts"
+ }
+ ],
+ "optimization": true,
+ "outputHashing": "all",
+ "sourceMap": false,
+ "extractCss": true,
+ "namedChunks": false,
+ "aot": true,
+ "extractLicenses": true,
+ "vendorChunk": false,
+ "buildOptimizer": true,
+ "budgets": [
+ {
+ "type": "initial",
+ "maximumWarning": "5mb",
+ "maximumError": "10mb"
+ }
+ ]
+ }
+ }
+ },
+ "serve": {
+ "builder": "@angular-devkit/build-angular:dev-server",
+ "options": {
+ "browserTarget": "truevision:build"
+ },
+ "configurations": {
+ "production": {
+ "browserTarget": "truevision:build:production"
+ }
+ }
+ },
+ "extract-i18n": {
+ "builder": "@angular-devkit/build-angular:extract-i18n",
+ "options": {
+ "browserTarget": "truevision:build"
+ }
+ },
+ "test": {
+ "builder": "@angular-devkit/build-angular:karma",
+ "options": {
+ "main": "src/test.ts",
+ "polyfills": "src/polyfills.ts",
+ "tsConfig": "src/tsconfig.spec.json",
+ "karmaConfig": "src/karma.conf.js",
+ "sourceMap": true,
+ "styles": [
+ "src/assets/styles/app.scss"
+ ],
+ "scripts": [],
+ "assets": [
+ "src/favicon.ico",
+ "src/assets"
+ ]
+ }
+ },
+ "lint": {
+ "builder": "@angular-devkit/build-angular:tslint",
+ "options": {
+ "tsConfig": [
+ "src/tsconfig.app.json",
+ "src/tsconfig.spec.json"
+ ],
+ "exclude": [
+ "**/node_modules/**"
+ ]
+ }
+ }
+ }
+ },
+ "truevision-e2e": {
+ "root": "e2e/",
+ "projectType": "application",
+ "prefix": "",
+ "architect": {
+ "e2e": {
+ "builder": "@angular-devkit/build-angular:protractor",
+ "options": {
+ "protractorConfig": "e2e/protractor.conf.js",
+ "devServerTarget": "truevision:serve"
+ },
+ "configurations": {
+ "production": {
+ "devServerTarget": "truevision:serve:production"
+ }
+ }
+ },
+ "lint": {
+ "builder": "@angular-devkit/build-angular:tslint",
+ "options": {
+ "tsConfig": "e2e/tsconfig.e2e.json",
+ "exclude": [
+ "**/node_modules/**"
+ ]
+ }
+ }
+ }
+ }
+ },
+ "defaultProject": "truevision"
+}
diff --git a/build/icon.png b/build/icon.png
new file mode 100644
index 00000000..1da2ab11
Binary files /dev/null and b/build/icon.png differ
diff --git a/build/icons/256x256.png b/build/icons/256x256.png
new file mode 100644
index 00000000..bddbe38b
Binary files /dev/null and b/build/icons/256x256.png differ
diff --git a/build/icons/512x512.png b/build/icons/512x512.png
new file mode 100644
index 00000000..3a39c146
Binary files /dev/null and b/build/icons/512x512.png differ
diff --git a/build/icons/icon.png b/build/icons/icon.png
new file mode 100644
index 00000000..1da2ab11
Binary files /dev/null and b/build/icons/icon.png differ
diff --git a/buildspec.yml b/buildspec.yml
new file mode 100644
index 00000000..795995ce
--- /dev/null
+++ b/buildspec.yml
@@ -0,0 +1,30 @@
+version: 0.2
+
+phases:
+ install:
+ commands:
+ # install dependencies
+ - echo installng source NPM dependencies...
+ - npm install npm@latest -g
+ - npm install -g @angular/cli
+
+ pre_build:
+ commands:
+ - echo prebuild steps
+ - npm install
+ - ng test --progress=false --browsers=ChromeHeadless --watch=false
+
+ build:
+ commands:
+ # build angular app
+ - ng build --prod
+
+
+ post_build:
+ commands:
+ # clear S3 bucket
+ # - aws s3 rm s3://${S3_BUCKET} --recursive
+ # - echo S3 bucket cleared
+ # copy files from dist folder into S3 bucket
+ # - aws s3 cp dist s3://${S3_BUCKET} --recursive
+ - echo Build completed on `date`
\ No newline at end of file
diff --git a/default-project/Materials/Asphalt-01.jpg b/default-project/Materials/Asphalt-01.jpg
new file mode 100644
index 00000000..0711bd7a
Binary files /dev/null and b/default-project/Materials/Asphalt-01.jpg differ
diff --git a/default-project/Materials/Asphalt-01.jpg.meta b/default-project/Materials/Asphalt-01.jpg.meta
new file mode 100644
index 00000000..c3a593b6
--- /dev/null
+++ b/default-project/Materials/Asphalt-01.jpg.meta
@@ -0,0 +1,40 @@
+{
+ "guid": "AAC88E06-1805-4B33-9D03-D6BA62031E38",
+ "version": 4.5,
+ "type": "Texture",
+ "importer": "TextureImporter",
+ "data": {
+ "metadata": null,
+ "uuid": "7DD16D3C-C6F1-46E7-B33D-266217C5000E",
+ "name": "",
+ "mapping": 300,
+ "repeat": [
+ 1,
+ 1
+ ],
+ "offset": [
+ 0,
+ 0
+ ],
+ "center": [
+ 0,
+ 0
+ ],
+ "rotation": 0,
+ "wrap": [
+ 1000,
+ 1000
+ ],
+ "format": 1023,
+ "type": 1009,
+ "encoding": 3000,
+ "minFilter": 1008,
+ "magFilter": 1006,
+ "anisotropy": 1,
+ "flipY": true,
+ "premultiplyAlpha": false,
+ "unpackAlignment": 4
+ },
+ "path": "/home/himanshu/Documents/Truevision/Materials/Asphalt-01.jpg",
+ "preview": null
+}
\ No newline at end of file
diff --git a/default-project/Materials/Asphalt-01.material b/default-project/Materials/Asphalt-01.material
new file mode 100644
index 00000000..beb96f63
--- /dev/null
+++ b/default-project/Materials/Asphalt-01.material
@@ -0,0 +1,14 @@
+{
+ "version": 0.1,
+ "uuid": "3C52858A-B32B-4927-87CF-814E8E5FFFAE",
+ "type": "MeshStandardMaterial",
+ "name": "Asphalt-01",
+ "color": 16777215,
+ "roughness": 1,
+ "metalness": 0.3,
+ "emissive": 0,
+ "mapGuid": "AAC88E06-1805-4B33-9D03-D6BA62031E38",
+ "depthFunc": 3,
+ "depthTest": true,
+ "depthWrite": true
+}
\ No newline at end of file
diff --git a/default-project/Materials/Asphalt-01.material.meta b/default-project/Materials/Asphalt-01.material.meta
new file mode 100644
index 00000000..12ea94a4
--- /dev/null
+++ b/default-project/Materials/Asphalt-01.material.meta
@@ -0,0 +1,7 @@
+{
+ "guid": "09B39764-2409-4A58-B9AB-D9C18AD5485C",
+ "importer": "MaterialImporter",
+ "data": {},
+ "path": "/home/himanshu/Documents/Truevision/Materials/Asphalt-01.material",
+ "preview": null
+}
\ No newline at end of file
diff --git a/default-project/Materials/Grass-01.jpg b/default-project/Materials/Grass-01.jpg
new file mode 100644
index 00000000..ed6c7f85
Binary files /dev/null and b/default-project/Materials/Grass-01.jpg differ
diff --git a/default-project/Materials/Grass-01.jpg.meta b/default-project/Materials/Grass-01.jpg.meta
new file mode 100644
index 00000000..f9cddb7b
--- /dev/null
+++ b/default-project/Materials/Grass-01.jpg.meta
@@ -0,0 +1,40 @@
+{
+ "guid": "DB932B7B-8B5E-4BB0-B092-25D31FD7DFF3",
+ "version": 4.5,
+ "type": "Texture",
+ "importer": "TextureImporter",
+ "data": {
+ "metadata": null,
+ "uuid": "5AABBD8D-7F38-4141-A857-3E38F65BB682",
+ "name": "",
+ "mapping": 300,
+ "repeat": [
+ 1,
+ 1
+ ],
+ "offset": [
+ 0,
+ 0
+ ],
+ "center": [
+ 0,
+ 0
+ ],
+ "rotation": 0,
+ "wrap": [
+ 1000,
+ 1000
+ ],
+ "format": 1023,
+ "type": 1009,
+ "encoding": 3000,
+ "minFilter": 1008,
+ "magFilter": 1006,
+ "anisotropy": 1,
+ "flipY": true,
+ "premultiplyAlpha": false,
+ "unpackAlignment": 4
+ },
+ "path": "/home/himanshu/Documents/Truevision/Materials/Grass-01.jpg",
+ "preview": null
+}
\ No newline at end of file
diff --git a/default-project/Materials/Grass-01.material b/default-project/Materials/Grass-01.material
new file mode 100644
index 00000000..92d79e69
--- /dev/null
+++ b/default-project/Materials/Grass-01.material
@@ -0,0 +1,14 @@
+{
+ "version": 0.1,
+ "uuid": "EC4297BB-7C10-4E49-BCEF-F90C54D68F6F",
+ "type": "MeshStandardMaterial",
+ "name": "Grass-01",
+ "color": 16777215,
+ "roughness": 1,
+ "metalness": 0.2,
+ "emissive": 0,
+ "mapGuid": "DB932B7B-8B5E-4BB0-B092-25D31FD7DFF3",
+ "depthFunc": 3,
+ "depthTest": true,
+ "depthWrite": true
+}
\ No newline at end of file
diff --git a/default-project/Materials/Grass-01.material.meta b/default-project/Materials/Grass-01.material.meta
new file mode 100644
index 00000000..fa5d5339
--- /dev/null
+++ b/default-project/Materials/Grass-01.material.meta
@@ -0,0 +1,7 @@
+{
+ "guid": "E9FF8597-DB71-4615-B55F-A01800C9B68D",
+ "importer": "MaterialImporter",
+ "data": {},
+ "path": "/home/himanshu/Documents/Truevision/Materials/Grass-01.material",
+ "preview": null
+}
\ No newline at end of file
diff --git a/default-project/Materials/Sidewalk-01.jpg b/default-project/Materials/Sidewalk-01.jpg
new file mode 100644
index 00000000..d8938e66
Binary files /dev/null and b/default-project/Materials/Sidewalk-01.jpg differ
diff --git a/default-project/Materials/Sidewalk-01.jpg.meta b/default-project/Materials/Sidewalk-01.jpg.meta
new file mode 100644
index 00000000..423af1d0
--- /dev/null
+++ b/default-project/Materials/Sidewalk-01.jpg.meta
@@ -0,0 +1,40 @@
+{
+ "guid": "D6871CAD-6304-4584-BDF2-CD1780B4F6D3",
+ "version": 4.5,
+ "type": "Texture",
+ "importer": "TextureImporter",
+ "data": {
+ "metadata": null,
+ "uuid": "71A9E97C-C4E1-444D-B334-5D087E52AE7A",
+ "name": "",
+ "mapping": 300,
+ "repeat": [
+ 1,
+ 1
+ ],
+ "offset": [
+ 0,
+ 0
+ ],
+ "center": [
+ 0,
+ 0
+ ],
+ "rotation": 0,
+ "wrap": [
+ 1000,
+ 1000
+ ],
+ "format": 1023,
+ "type": 1009,
+ "encoding": 3000,
+ "minFilter": 1008,
+ "magFilter": 1006,
+ "anisotropy": 1,
+ "flipY": true,
+ "premultiplyAlpha": false,
+ "unpackAlignment": 4
+ },
+ "path": "/home/himanshu/Documents/Truevision/Materials/Sidewalk-01.jpg",
+ "preview": null
+}
\ No newline at end of file
diff --git a/default-project/Materials/Sidewalk-01.material b/default-project/Materials/Sidewalk-01.material
new file mode 100644
index 00000000..189b936f
--- /dev/null
+++ b/default-project/Materials/Sidewalk-01.material
@@ -0,0 +1,14 @@
+{
+ "version": 0.1,
+ "uuid": "3F5A41C8-AF34-4A26-A0E6-55DE6E6077CF",
+ "type": "MeshStandardMaterial",
+ "name": "Sidewalk-01",
+ "color": 16777215,
+ "roughness": 1,
+ "metalness": 0.3,
+ "emissive": 0,
+ "mapGuid": "D6871CAD-6304-4584-BDF2-CD1780B4F6D3",
+ "depthFunc": 3,
+ "depthTest": true,
+ "depthWrite": true
+}
\ No newline at end of file
diff --git a/default-project/Materials/Sidewalk-01.material.meta b/default-project/Materials/Sidewalk-01.material.meta
new file mode 100644
index 00000000..6926be0d
--- /dev/null
+++ b/default-project/Materials/Sidewalk-01.material.meta
@@ -0,0 +1,7 @@
+{
+ "guid": "87B8CB52-7E11-4F22-9CF6-285EC8FE9218",
+ "importer": "MaterialImporter",
+ "data": {},
+ "path": "/home/himanshu/Documents/Truevision/Materials/Sidewalk-01.material",
+ "preview": null
+}
\ No newline at end of file
diff --git a/default-project/Props/Bush-01.glb b/default-project/Props/Bush-01.glb
new file mode 100644
index 00000000..1aeacbe4
Binary files /dev/null and b/default-project/Props/Bush-01.glb differ
diff --git a/default-project/Props/Bush-01.glb.meta b/default-project/Props/Bush-01.glb.meta
new file mode 100644
index 00000000..bf3edd05
--- /dev/null
+++ b/default-project/Props/Bush-01.glb.meta
@@ -0,0 +1,18 @@
+{
+ "guid": "42076FCA-53E8-4616-AA8B-EF694F834BD7",
+ "importer": "ModelImporter",
+ "data": {
+ "name": "Bush-01.glb",
+ "rotationVariance": {
+ "x": 0,
+ "y": 0,
+ "z": 0
+ },
+ "scaleVariance": {
+ "x": 0,
+ "y": 0,
+ "z": 0
+ }
+ },
+ "path": "/home/himanshu/Documents/Truevision/Props/Bush-01.glb"
+}
\ No newline at end of file
diff --git a/default-project/Props/Tree-01.glb b/default-project/Props/Tree-01.glb
new file mode 100644
index 00000000..37946c24
Binary files /dev/null and b/default-project/Props/Tree-01.glb differ
diff --git a/default-project/Props/Tree-01.glb.meta b/default-project/Props/Tree-01.glb.meta
new file mode 100644
index 00000000..0ab27a60
--- /dev/null
+++ b/default-project/Props/Tree-01.glb.meta
@@ -0,0 +1,18 @@
+{
+ "guid": "97654775-41C1-4C02-A0C1-26A7292B6831",
+ "importer": "ModelImporter",
+ "data": {
+ "name": "Tree-01.glb",
+ "rotationVariance": {
+ "x": 0,
+ "y": 0,
+ "z": 0
+ },
+ "scaleVariance": {
+ "x": 0,
+ "y": 0,
+ "z": 0
+ }
+ },
+ "path": "/home/himanshu/Documents/Truevision/Props/Tree-01.glb"
+}
\ No newline at end of file
diff --git a/default-project/RoadMarkings/NewRoadMarking.roadmarking b/default-project/RoadMarkings/NewRoadMarking.roadmarking
new file mode 100644
index 00000000..41782ed9
--- /dev/null
+++ b/default-project/RoadMarkings/NewRoadMarking.roadmarking
@@ -0,0 +1,5 @@
+{
+ "name": "NewRoadMarking",
+ "type": "point",
+ "textureGuid": "ED2FD2A5-4784-444D-A355-6979065397B7"
+}
\ No newline at end of file
diff --git a/default-project/RoadMarkings/NewRoadMarking.roadmarking.meta b/default-project/RoadMarkings/NewRoadMarking.roadmarking.meta
new file mode 100644
index 00000000..a100b247
--- /dev/null
+++ b/default-project/RoadMarkings/NewRoadMarking.roadmarking.meta
@@ -0,0 +1,6 @@
+{
+ "guid": "71F79C7A-7326-45A0-B1BA-EE836D278C94",
+ "importer": "RoadMarkingImporter",
+ "data": {},
+ "path": "/home/himanshu/Documents/Truevision/RoadMarkings/NewRoadMarking.roadmarking"
+}
\ No newline at end of file
diff --git a/default-project/RoadMarkings/STOP.svg b/default-project/RoadMarkings/STOP.svg
new file mode 100644
index 00000000..2c956987
--- /dev/null
+++ b/default-project/RoadMarkings/STOP.svg
@@ -0,0 +1,30 @@
+
+
+
diff --git a/default-project/RoadMarkings/STOP.svg.meta b/default-project/RoadMarkings/STOP.svg.meta
new file mode 100644
index 00000000..9de91d59
--- /dev/null
+++ b/default-project/RoadMarkings/STOP.svg.meta
@@ -0,0 +1,39 @@
+{
+ "guid": "ED2FD2A5-4784-444D-A355-6979065397B7",
+ "version": 4.5,
+ "type": "Texture",
+ "importer": "TextureImporter",
+ "data": {
+ "metadata": null,
+ "uuid": "37C49ED2-D1E3-4A8C-99D7-DFD38BFD289A",
+ "name": "",
+ "mapping": 300,
+ "repeat": [
+ 1,
+ 1
+ ],
+ "offset": [
+ 0,
+ 0
+ ],
+ "center": [
+ 0,
+ 0
+ ],
+ "rotation": 0,
+ "wrap": [
+ 1000,
+ 1000
+ ],
+ "format": 1023,
+ "type": 1009,
+ "encoding": 3000,
+ "minFilter": 1008,
+ "magFilter": 1006,
+ "anisotropy": 1,
+ "flipY": true,
+ "premultiplyAlpha": false,
+ "unpackAlignment": 4
+ },
+ "path": "STOP.svg"
+}
diff --git a/default-project/RoadStyles/Country.roadstyle b/default-project/RoadStyles/Country.roadstyle
new file mode 100644
index 00000000..d0bc58d9
--- /dev/null
+++ b/default-project/RoadStyles/Country.roadstyle
@@ -0,0 +1 @@
+{"version":1,"laneOffset":{"attr_s":0,"attr_a":0,"attr_b":0,"attr_c":0,"attr_d":0},"laneSection":{"attr_s":0,"left":{"lane":[{"attr_id":2,"attr_type":"shoulder","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":0.5,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[]},{"attr_id":1,"attr_type":"driving","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":3.6,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[{"attr_sOffset":0,"attr_type":"solid","attr_weight":"standard","attr_color":"standard","attr_width":0.15,"attr_laneChange":"none","attr_height":0}]}]},"center":{"lane":[{"attr_id":0,"attr_type":"driving","attr_level":true,"width":[],"roadMark":[{"attr_sOffset":0,"attr_type":"none","attr_weight":"standard","attr_color":"standard","attr_width":0.15,"attr_laneChange":"none","attr_height":0}]}]},"right":{"lane":[{"attr_id":-1,"attr_type":"driving","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":3.6,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[{"attr_sOffset":0,"attr_type":"solid","attr_weight":"standard","attr_color":"standard","attr_width":0.15,"attr_laneChange":"none","attr_height":0}]},{"attr_id":-2,"attr_type":"shoulder","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":0.5,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[]}]}}}
\ No newline at end of file
diff --git a/default-project/RoadStyles/Country.roadstyle.meta b/default-project/RoadStyles/Country.roadstyle.meta
new file mode 100644
index 00000000..9b1a1448
--- /dev/null
+++ b/default-project/RoadStyles/Country.roadstyle.meta
@@ -0,0 +1,6 @@
+{
+ "guid": "34902B1A-270C-4DEB-BD0A-CCCE340D00BF",
+ "importer": "RoadStyleImporter",
+ "data": {},
+ "path": "/home/himanshu/Documents/Truevision/RoadStyles/Country.roadstyle"
+}
\ No newline at end of file
diff --git a/default-project/RoadStyles/Default.roadstyle b/default-project/RoadStyles/Default.roadstyle
new file mode 100644
index 00000000..8c439b0d
--- /dev/null
+++ b/default-project/RoadStyles/Default.roadstyle
@@ -0,0 +1 @@
+{"version":1,"laneOffset":{"attr_s":0,"attr_a":0,"attr_b":0,"attr_c":0,"attr_d":0},"laneSection":{"attr_s":0,"left":{"lane":[{"attr_id":3,"attr_type":"sidewalk","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":2,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[]},{"attr_id":2,"attr_type":"shoulder","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":0.5,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[]},{"attr_id":1,"attr_type":"driving","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":3.6,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[{"attr_sOffset":0,"attr_type":"none","attr_weight":"standard","attr_color":"standard","attr_width":0.15,"attr_laneChange":"none","attr_height":0}]}]},"center":{"lane":[{"attr_id":0,"attr_type":"driving","attr_level":true,"width":[],"roadMark":[{"attr_sOffset":0,"attr_type":"broken","attr_weight":"standard","attr_color":"standard","attr_width":0.15,"attr_laneChange":"none","attr_height":0}]}]},"right":{"lane":[{"attr_id":-1,"attr_type":"driving","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":3.6,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[{"attr_sOffset":0,"attr_type":"none","attr_weight":"standard","attr_color":"standard","attr_width":0.15,"attr_laneChange":"none","attr_height":0}]},{"attr_id":-2,"attr_type":"shoulder","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":0.5,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[]},{"attr_id":-3,"attr_type":"sidewalk","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":2,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[]}]}}}
\ No newline at end of file
diff --git a/default-project/RoadStyles/Default.roadstyle.meta b/default-project/RoadStyles/Default.roadstyle.meta
new file mode 100644
index 00000000..a08cc717
--- /dev/null
+++ b/default-project/RoadStyles/Default.roadstyle.meta
@@ -0,0 +1,6 @@
+{
+ "guid": "BC6945AF-45EE-4B32-B42A-23E755B3E27E",
+ "importer": "RoadStyleImporter",
+ "data": {},
+ "path": "/home/himanshu/Documents/Truevision/RoadStyles/Default.roadstyle"
+}
\ No newline at end of file
diff --git a/default-project/RoadStyles/Freeway.roadstyle b/default-project/RoadStyles/Freeway.roadstyle
new file mode 100644
index 00000000..ddcf0a44
--- /dev/null
+++ b/default-project/RoadStyles/Freeway.roadstyle
@@ -0,0 +1 @@
+{"version":1,"laneOffset":{"attr_s":0,"attr_a":0,"attr_b":0,"attr_c":0,"attr_d":0},"laneSection":{"attr_s":0,"left":{"lane":[{"attr_id":6,"attr_type":"shoulder","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":3.6,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[]},{"attr_id":5,"attr_type":"driving","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":3.6,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[{"attr_sOffset":0,"attr_type":"solid","attr_weight":"standard","attr_color":"standard","attr_width":0.15,"attr_laneChange":"none","attr_height":0}]},{"attr_id":4,"attr_type":"driving","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":3.6,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[{"attr_sOffset":0,"attr_type":"broken","attr_weight":"standard","attr_color":"standard","attr_width":0.15,"attr_laneChange":"none","attr_height":0}]},{"attr_id":3,"attr_type":"driving","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":3.6,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[{"attr_sOffset":0,"attr_type":"broken","attr_weight":"standard","attr_color":"standard","attr_width":0.15,"attr_laneChange":"none","attr_height":0}]},{"attr_id":2,"attr_type":"driving","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":3.6,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[{"attr_sOffset":0,"attr_type":"broken","attr_weight":"standard","attr_color":"standard","attr_width":0.15,"attr_laneChange":"none","attr_height":0}]},{"attr_id":1,"attr_type":"shoulder","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":3.6,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[{"attr_sOffset":0,"attr_type":"solid","attr_weight":"standard","attr_color":"yellow","attr_width":0.15,"attr_laneChange":"none","attr_height":0}]}]},"center":{"lane":[{"attr_id":0,"attr_type":"none","attr_level":true,"width":[],"roadMark":[{"attr_sOffset":0,"attr_type":"none","attr_weight":"standard","attr_color":"standard","attr_width":0.15,"attr_laneChange":"none","attr_height":0}]}]},"right":{"lane":[{"attr_id":-1,"attr_type":"shoulder","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":3.6,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[{"attr_sOffset":0,"attr_type":"solid","attr_weight":"standard","attr_color":"yellow","attr_width":0.15,"attr_laneChange":"none","attr_height":0}]},{"attr_id":-2,"attr_type":"driving","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":3.6,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[{"attr_sOffset":0,"attr_type":"broken","attr_weight":"standard","attr_color":"standard","attr_width":0.15,"attr_laneChange":"none","attr_height":0}]},{"attr_id":-3,"attr_type":"driving","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":3.6,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[{"attr_sOffset":0,"attr_type":"broken","attr_weight":"standard","attr_color":"standard","attr_width":0.15,"attr_laneChange":"none","attr_height":0}]},{"attr_id":-4,"attr_type":"driving","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":3.6,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[{"attr_sOffset":0,"attr_type":"broken","attr_weight":"standard","attr_color":"standard","attr_width":0.15,"attr_laneChange":"none","attr_height":0}]},{"attr_id":-5,"attr_type":"driving","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":3.6,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[{"attr_sOffset":0,"attr_type":"solid","attr_weight":"standard","attr_color":"standard","attr_width":0.15,"attr_laneChange":"none","attr_height":0}]},{"attr_id":-6,"attr_type":"shoulder","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":3.6,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[]}]}}}
\ No newline at end of file
diff --git a/default-project/RoadStyles/Freeway.roadstyle.meta b/default-project/RoadStyles/Freeway.roadstyle.meta
new file mode 100644
index 00000000..11dadb20
--- /dev/null
+++ b/default-project/RoadStyles/Freeway.roadstyle.meta
@@ -0,0 +1,6 @@
+{
+ "guid": "DB2A86F7-B07B-4051-B718-F8FA4F391F0C",
+ "importer": "RoadStyleImporter",
+ "data": {},
+ "path": "/home/himanshu/Documents/Truevision/RoadStyles/Freeway.roadstyle"
+}
\ No newline at end of file
diff --git a/default-project/RoadStyles/FreewayOneWay.roadstyle b/default-project/RoadStyles/FreewayOneWay.roadstyle
new file mode 100644
index 00000000..18e48852
--- /dev/null
+++ b/default-project/RoadStyles/FreewayOneWay.roadstyle
@@ -0,0 +1 @@
+{"version":1,"laneOffset":{"attr_s":0,"attr_a":0,"attr_b":0,"attr_c":0,"attr_d":0},"laneSection":{"attr_s":0,"center":{"lane":[{"attr_id":0,"attr_type":"none","attr_level":true,"width":[],"roadMark":[{"attr_sOffset":0,"attr_type":"none","attr_weight":"standard","attr_color":"standard","attr_width":0.15,"attr_laneChange":"none","attr_height":0}]}]},"right":{"lane":[{"attr_id":-1,"attr_type":"shoulder","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":3.6,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[{"attr_sOffset":0,"attr_type":"solid","attr_weight":"standard","attr_color":"yellow","attr_width":0.15,"attr_laneChange":"none","attr_height":0}]},{"attr_id":-2,"attr_type":"driving","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":3.6,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[{"attr_sOffset":0,"attr_type":"broken","attr_weight":"standard","attr_color":"standard","attr_width":0.15,"attr_laneChange":"none","attr_height":0}]},{"attr_id":-3,"attr_type":"driving","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":3.6,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[{"attr_sOffset":0,"attr_type":"broken","attr_weight":"standard","attr_color":"standard","attr_width":0.15,"attr_laneChange":"none","attr_height":0}]},{"attr_id":-4,"attr_type":"driving","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":3.6,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[{"attr_sOffset":0,"attr_type":"broken","attr_weight":"standard","attr_color":"standard","attr_width":0.15,"attr_laneChange":"none","attr_height":0}]},{"attr_id":-5,"attr_type":"driving","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":3.6,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[{"attr_sOffset":0,"attr_type":"solid","attr_weight":"standard","attr_color":"standard","attr_width":0.15,"attr_laneChange":"none","attr_height":0}]},{"attr_id":-6,"attr_type":"shoulder","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":3.6,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[]}]}}}
\ No newline at end of file
diff --git a/default-project/RoadStyles/FreewayOneWay.roadstyle.meta b/default-project/RoadStyles/FreewayOneWay.roadstyle.meta
new file mode 100644
index 00000000..2ef7e6a6
--- /dev/null
+++ b/default-project/RoadStyles/FreewayOneWay.roadstyle.meta
@@ -0,0 +1,6 @@
+{
+ "guid": "4EC98151-FD5E-4541-9C67-D11DED22B22A",
+ "importer": "RoadStyleImporter",
+ "data": {},
+ "path": "/home/himanshu/Documents/Truevision/RoadStyles/FreewayOneWay.roadstyle"
+}
\ No newline at end of file
diff --git a/default-project/RoadStyles/Highway.roadstyle b/default-project/RoadStyles/Highway.roadstyle
new file mode 100644
index 00000000..a2a12a9a
--- /dev/null
+++ b/default-project/RoadStyles/Highway.roadstyle
@@ -0,0 +1 @@
+{"version":1,"laneOffset":{"attr_s":0,"attr_a":0,"attr_b":0,"attr_c":0,"attr_d":0},"laneSection":{"attr_s":0,"left":{"lane":[{"attr_id":2,"attr_type":"shoulder","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":0.5,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[]},{"attr_id":1,"attr_type":"driving","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":3.6,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[{"attr_sOffset":0,"attr_type":"solid","attr_weight":"standard","attr_color":"standard","attr_width":0.15,"attr_laneChange":"none","attr_height":0}]}]},"center":{"lane":[{"attr_id":0,"attr_type":"driving","attr_level":true,"width":[],"roadMark":[{"attr_sOffset":0,"attr_type":"solid solid","attr_weight":"standard","attr_color":"yellow","attr_width":0.15,"attr_laneChange":"none","attr_height":0}]}]},"right":{"lane":[{"attr_id":-1,"attr_type":"driving","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":3.6,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[{"attr_sOffset":0,"attr_type":"solid","attr_weight":"standard","attr_color":"standard","attr_width":0.15,"attr_laneChange":"none","attr_height":0}]},{"attr_id":-2,"attr_type":"shoulder","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":0.5,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[]}]}}}
\ No newline at end of file
diff --git a/default-project/RoadStyles/Highway.roadstyle.meta b/default-project/RoadStyles/Highway.roadstyle.meta
new file mode 100644
index 00000000..4d7aa753
--- /dev/null
+++ b/default-project/RoadStyles/Highway.roadstyle.meta
@@ -0,0 +1,6 @@
+{
+ "guid": "6B579982-9DDB-4566-8346-3FE687C0C5C4",
+ "importer": "RoadStyleImporter",
+ "data": {},
+ "path": "/home/himanshu/Documents/Truevision/RoadStyles/Highway.roadstyle"
+}
\ No newline at end of file
diff --git a/default-project/RoadStyles/HighwayPassing.roadstyle b/default-project/RoadStyles/HighwayPassing.roadstyle
new file mode 100644
index 00000000..508c4f2f
--- /dev/null
+++ b/default-project/RoadStyles/HighwayPassing.roadstyle
@@ -0,0 +1 @@
+{"version":1,"laneOffset":{"attr_s":0,"attr_a":0,"attr_b":0,"attr_c":0,"attr_d":0},"laneSection":{"attr_s":0,"left":{"lane":[{"attr_id":2,"attr_type":"shoulder","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":0.5,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[]},{"attr_id":1,"attr_type":"driving","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":3.6,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[{"attr_sOffset":0,"attr_type":"solid","attr_weight":"standard","attr_color":"standard","attr_width":0.15,"attr_laneChange":"none","attr_height":0}]}]},"center":{"lane":[{"attr_id":0,"attr_type":"driving","attr_level":true,"width":[],"roadMark":[{"attr_sOffset":0,"attr_type":"broken","attr_weight":"standard","attr_color":"yellow","attr_width":0.15,"attr_laneChange":"none","attr_height":0}]}]},"right":{"lane":[{"attr_id":-1,"attr_type":"driving","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":3.6,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[{"attr_sOffset":0,"attr_type":"solid","attr_weight":"standard","attr_color":"standard","attr_width":0.15,"attr_laneChange":"none","attr_height":0}]},{"attr_id":-2,"attr_type":"shoulder","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":0.5,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[]}]}}}
\ No newline at end of file
diff --git a/default-project/RoadStyles/HighwayPassing.roadstyle.meta b/default-project/RoadStyles/HighwayPassing.roadstyle.meta
new file mode 100644
index 00000000..2369d7b3
--- /dev/null
+++ b/default-project/RoadStyles/HighwayPassing.roadstyle.meta
@@ -0,0 +1,6 @@
+{
+ "guid": "717A351A-EC6B-4928-B8CC-932E599B8DAA",
+ "importer": "RoadStyleImporter",
+ "data": {},
+ "path": "/home/himanshu/Documents/Truevision/RoadStyles/HighwayPassing.roadstyle"
+}
\ No newline at end of file
diff --git a/default-project/RoadStyles/OneWay.roadstyle b/default-project/RoadStyles/OneWay.roadstyle
new file mode 100644
index 00000000..7314c9df
--- /dev/null
+++ b/default-project/RoadStyles/OneWay.roadstyle
@@ -0,0 +1 @@
+{"version":1,"laneOffset":{"attr_s":0,"attr_a":0,"attr_b":0,"attr_c":0,"attr_d":0},"laneSection":{"attr_s":0,"left":{"lane":[{"attr_id":1,"attr_type":"sidewalk","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":2,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[]}]},"center":{"lane":[{"attr_id":0,"attr_type":"none","attr_level":true,"width":[],"roadMark":[]}]},"right":{"lane":[{"attr_id":-1,"attr_type":"driving","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":3.6,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[]},{"attr_id":-2,"attr_type":"sidewalk","attr_level":true,"width":[{"attr_sOffset":0,"attr_a":2,"attr_b":0,"attr_c":0,"attr_d":0}],"roadMark":[]}]}}}
\ No newline at end of file
diff --git a/default-project/RoadStyles/OneWay.roadstyle.meta b/default-project/RoadStyles/OneWay.roadstyle.meta
new file mode 100644
index 00000000..0fc27349
--- /dev/null
+++ b/default-project/RoadStyles/OneWay.roadstyle.meta
@@ -0,0 +1,6 @@
+{
+ "guid": "620C96B5-1F19-4F90-B7C1-BD9EF5FD69F5",
+ "importer": "RoadStyleImporter",
+ "data": {},
+ "path": "/home/himanshu/Documents/Truevision/RoadStyles/OneWay.roadstyle"
+}
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 00000000..586aad10
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,21 @@
+version: '2.0'
+
+services:
+ app:
+ container_name: truevision_designer
+ build:
+ context: .
+ dockerfile: Dockerfile
+ ports:
+ - '4200:4200'
+ - '49153:49153'
+ environment:
+ - NODE_ENV=production
+ working_dir: /app
+ volumes:
+ - ./:/app
+ - /app/node_modules
+#Volumes
+volumes:
+ dbdata:
+ driver: local
\ No newline at end of file
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 00000000..606966e4
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,7 @@
+# Truevision Designer Documentation
+
+## Contribution Guidelines
+
+If you are submitting a pull-request for documentation, please submit it to the corresponding branch. For example, documentation for version 1.0 should be submitted to the 1.0 branch. Documentation intended for the next release of Simulator should be submitted to the master branch.
+
+You can also create a github issue in case you find any bugs or errors in the documentation.
diff --git a/docs/documentation.md b/docs/documentation.md
new file mode 100644
index 00000000..fc23df46
--- /dev/null
+++ b/docs/documentation.md
@@ -0,0 +1,10 @@
+- ## Getting Started
+ - [User Guide](/designer/docs/{{version}}/user-guide)
+ - [Installation](/designer/docs/{{version}}/installation)
+ - [Overview](/designer/docs/{{version}}/overview)
+- ## Tools
+ - [Introduction](/designer/docs/{{version}}/tools)
+ - [Road Tool](/designer/docs/{{version}}/road-tool)
+ - [Lane Width Tool](/designer/docs/{{version}}/lane-width-tool)
+- ## OpenDrive
+ - [Introduction](/designer/docs/{{version}}/open-drive)
diff --git a/docs/img/welcome.jpg b/docs/img/welcome.jpg
new file mode 100644
index 00000000..56fefc12
Binary files /dev/null and b/docs/img/welcome.jpg differ
diff --git a/docs/installation.md b/docs/installation.md
new file mode 100644
index 00000000..d5f477da
--- /dev/null
+++ b/docs/installation.md
@@ -0,0 +1,51 @@
+# Getting Started: Installation
+
+- [Installation](#installation)
+ - [System Requirements](#system-requirements)
+- [Installing On Ubuntu](#installing-on-ubuntu)
+- [Launching](#launching)
+ - [Sign In](#sign-in)
+
+
+## Installation
+
+
+### System Requirements
+
+Truevision Simulator has a few system requirements.
+
+- i7 Processor or above
+- Alteast 16GB RAM
+- GTX 1040 GPU or above
+- ~4GB of Disk space
+- Active Internet Connection while running simulator
+
+
+
+## Installing On Ubuntu
+
+Follow the simple steps mentioned below
+
+- Download the latest .deb file from https://www.truevision.ai/download
+- Double click the .deb file which should open the installation screen
+- Click `Install` to proceed. You should now have Truevision installed on your system
+
+You can also download via command line
+
+- Open Terminal
+- cd into folder that contains the .deb file
+- Run the below command `sudo dpkg -i truevision-simulator-latest.deb`
+
+
+## Launching Truevision
+
+After installing, simply search for `Truevision` on search and find the application
+
+
+
+### Sign-In
+
+Simple sign-in with your credentials and you will be able to proceed.
+
+
+
diff --git a/docs/lane-add-tool.md b/docs/lane-add-tool.md
new file mode 100644
index 00000000..684411dd
--- /dev/null
+++ b/docs/lane-add-tool.md
@@ -0,0 +1,14 @@
+# Lane Add Tool
+
+- [Overview](#overview)
+- [Add Lane](#add-lane)
+
+
+
+## Overview
+
+This tool helps you add new lanes on a road.
+
+
+## Add Lane
+
diff --git a/docs/lane-width-tool.md b/docs/lane-width-tool.md
new file mode 100644
index 00000000..0e5925d7
--- /dev/null
+++ b/docs/lane-width-tool.md
@@ -0,0 +1,21 @@
+# Lane Width Tool
+
+- [Overview](#overview)
+- [Create Width Node](#create-width-node)
+
+
+
+## Overview
+
+This tool helps you modify the width of a lane on the road. A single road lane can contain multiple nodes to control the width of the lane.
+
+
+## Create Width Node
+
+- Select Lane Width Tool from toolbar using `left click`
+- Select Road you want to modify using `left click`
+- Selecting/Unselecting a road will show/hide the existing lane width nodes
+- Take pointer over a lane and use `shift + left click` to create new node
+- When the node is selected you will the node info in the inspector, modify the width value as desired
+
+
\ No newline at end of file
diff --git a/docs/open-drive.md b/docs/open-drive.md
new file mode 100644
index 00000000..2d336b36
--- /dev/null
+++ b/docs/open-drive.md
@@ -0,0 +1,76 @@
+# OpenDRIVE: Introduction
+
+- [Introduction](#getting-started)
+ - [What is OpenDRIVE?](#open-drive)
+ - [Hello Road](#hello-road)
+- [Using Designer](#using-designer)
+
+
+
+## What is OpenDRIVE
+Truevision provides support for importing & describing road networks via OpenDRIVE. OpenDRIVE® files are designed to describe entire road networks with respect to all data belonging to the road environment. They do not describe the entities acting on or interacting with the road.
+
+
+The OpenDRIVE file format provides the following features:
+- XML format
+- hierarchical structure
+- analytical definition of road geometry (plane elements, lateral / vertical profile, lane width etc.)
+- various types of lanes
+- junctions and junction groups
+- logical inter-connection of lanes
+- signs and signals incl. dependencies
+- signal controllers (e.g. for junctions)
+- road and road-side objects
+- user-defineable data beads
+- and more
+
+### Road Layout
+
+The following figure depicts the principles of road layout:
+
+
+
+> Note: Support for OpenDRIVE version 1.4 & above is provided. Some of the OpenDRIVE tags are not supported.
+
+
+## Hello Road
+
+In the section, we'll look to create our very first simple road using OpenDRIVE. You can create OpenDRIVE in two ways
+- Using Truevision ScenarioDesigner
+- Manually writing or modifying the XML
+
+
+The ScenarioDesigner is a much easier way to design roads as you'll not need to know anythign about OpenDRIVE and you can just visually create road by clicking and changing and few properties etc.
+
+Manually writing the XML file means you'll need to understand all the tags and element in OpenDRIVE, which can be quite a long excercise depending on how deeply you want to understand the format. Instead of covering everything, we'll be covering some of the most important elements which will improve your speed to iterate without spending too much time.
+
+
+## Using Designer
+
+Follow the screenshots below to create a simple road with muliple lanes.
+
+Open Designer
+
+
+Create Road
+
+
+Add Lanes
+
+
+Modify Lane Type
+
+
+Modify Lane Width
+
+
+Add Roadmarks
+
+
+Modify Roadmarks
+
+
+Export File
+
+
+Now, you can just import the file in the simulator with [import command](import-commands.md#import-open-drive)
\ No newline at end of file
diff --git a/docs/overview.md b/docs/overview.md
new file mode 100644
index 00000000..d14d29c0
--- /dev/null
+++ b/docs/overview.md
@@ -0,0 +1,46 @@
+# Overview
+
+- [Overview](#overview)
+ - [Coordinate System](#coordinate-system)
+ - [Mouse Interaction](#mouse)
+ - [2D Navigation](#2d-navigation)
+ - [3D Navigation](#3d-navigation)
+
+
+## Overview
+
+This section explains how to use the Road Editor to create different kinds of road geometries.
+
+
+## Coordinate System
+
+- x+ forward
+- y+ left
+- z+ up
+
+
+
+
+
+
+## Mouse Interation
+
+- `left-click` is used to select objects for inspecting
+- `shift + left-click` is used to add objects or controls points or some other kind of action
+- `right-click` is used for either context-menu
+
+
+
+## 2D Nagivation
+
+- `right-click` and drag to move the scene around
+- `middle-click` and move to zoom-in and out
+- `mouse-scroll` for zooming-in and out
+
+
+## 3D Nagivation
+
+- `left-click` and drag to panning the camera
+- `right-click` and drag to move the scene around
+- `middle-click` and move to zoom-in and out
+- `mouse-scroll` for zooming-in and out
diff --git a/docs/road-tool.md b/docs/road-tool.md
new file mode 100644
index 00000000..776de787
--- /dev/null
+++ b/docs/road-tool.md
@@ -0,0 +1,34 @@
+# Road Tool
+
+- [Overview](#overview)
+- [Create Roads](#create-roads)
+- [Select Road](#select-road)
+- [Connect Roads](#connect-roads)
+
+
+
+## Overview
+
+Road tool helps you create different types of road geometries and shapes to fit you needs.
+
+
+## Create Roads
+
+Use `shift + left click` to create control points. You can then drag the control points to change the shape of the road.
+
+If you want to create a new road then first unselect the previous road by `left click` and then `shift + left click` again to start creating a new road.
+
+
+
+## Select Road
+
+You can select a road for editing and modification by taking the pointer above the road and using `left click`
+
+
+## Connect Roads
+
+Two roads can be connected using road nodes.
+- Select a node from road A using `left click`
+- Select another node from road B using `left click`
+- Two road will now be connect through a new road C which also be modified if required
+
diff --git a/docs/tools.md b/docs/tools.md
new file mode 100644
index 00000000..cb849413
--- /dev/null
+++ b/docs/tools.md
@@ -0,0 +1,98 @@
+# Road Editor
+
+- [Overview](#overview)
+ - [Coordinate System](#coordinate-system)
+ - [Mouse Interaction](#mouse)
+ - [2D Navigation](#2d-navigation)
+ - [3D Navigation](#3d-navigation)
+- [Toolbar](#toolbars)
+ - [RoadGeometryTool](#road-geometry-tool)
+ - [LineGeometryTool](#line-geometry-tool)
+ - [ArcGeometryTool](#arc-geometry-tool)
+ - [LaneWidthTool](#lane-width-tool)
+ - [AddLaneTool](#add-lane-tool)
+ - [LaneMarkingTool](#lane-marking-tool)
+ - [LaneTool](#lane-tool)
+ - [RoadSignTool](#road-sign-tool)
+ - [VehicleTool](#vehicle-tool)
+
+
+
+## Overview
+
+{{product}} provides a number of different tools for displaying and modifying different aspects of road and environment.
+
+
+## Coordinate System
+
+- x+ forward
+- y+ left
+- z+ up
+
+
+
+
+
+
+## Mouse Interation
+
+- `left-click` is used to select objects for inspecting
+- `shift + left-click` is used to add objects or controls points or some other kind of action
+- `right-click` is used for either context-menu
+
+
+
+## 2D Nagivation
+
+- `right-click` and drag to move the scene around
+- `middle-click` and move to zoom-in and out
+- `mouse-scroll` for zooming-in and out
+
+
+## 3D Nagivation
+
+- `left-click` and drag to panning the camera
+- `right-click` and drag to move the scene around
+- `middle-click` and move to zoom-in and out
+- `mouse-scroll` for zooming-in and out
+
+
+## Toolbar
+
+
+
+## Road Geometry Tool
+
+[![Watch the video](https://i9.ytimg.com/vi/KiL_G9_chhA/mq2.jpg?sqp=CNfKqewF&rs=AOn4CLBclmEXkWT-sgw89-HmIl7lnBsERw)](https://youtu.be/KiL_G9_chhA)
+
+
+
+## Lane Width Tool
+
+Use `shift` + `left-click` to select a lane you want to duplicate. This tool will basically add a new lane next to the selected lane.
+
+
+[![Watch the video](https://i9.ytimg.com/vi/acloOOuakuk/mq2.jpg?sqp=CLLVqewF&rs=AOn4CLCZwQoJsxmSTWxorV_h_Q-OgL3pdA)](https://youtu.be/acloOOuakuk)
+
+
+
+## Lane Add Tool
+
+[![Watch the video](https://i9.ytimg.com/vi/Rnp0rGHCy2I/mq2.jpg?sqp=CJTWqewF&rs=AOn4CLCz5_MS7gHCl3fFI3vqinK83CVltw)](https://youtu.be/Rnp0rGHCy2I)
+
+
+
+## Lane Marking Tool
+
+[![Watch the video](https://i9.ytimg.com/vi/hCV1P3ZviVk/mq2.jpg?sqp=CKHZqewF&rs=AOn4CLDqc_afrOaQpUq9aaPR0jyG2m5bCg)](https://youtu.be/hCV1P3ZviVk)
+
+
+
+## Lane Tool
+
+[![Watch the video](https://i9.ytimg.com/vi/bw_6MQwBY38/mq2.jpg?sqp=CJDaqewF&rs=AOn4CLDRPdWx2wXgGAloaWhMurJGWzFv6w)](https://youtu.be/bw_6MQwBY38)
+
+
+## Road Sign Tool
+
+[![Watch the video](https://i9.ytimg.com/vi/bzGq73cKlNM/mq2.jpg?sqp=CNjaqewF&rs=AOn4CLDG7cJWKcop_eZ2mO6jvlo4lwaqlw)](https://youtu.be/bzGq73cKlNM)
diff --git a/docs/user-guide.md b/docs/user-guide.md
new file mode 100644
index 00000000..b48eb8ba
--- /dev/null
+++ b/docs/user-guide.md
@@ -0,0 +1,8 @@
+# Welcome User Guide {{version}}
+
+
+## Truevision Designer User Guide {{version}}
+
+Use this guide to learn how Truevision Designer can help you preview, organize, edit and publish multiple road and environement quickly and easily.
+
+This Guide helps you learn how to use the Truevision Designer and its associated services. You can read it from start to finish, or use it as a reference.
\ No newline at end of file
diff --git a/electron-builder.json b/electron-builder.json
new file mode 100644
index 00000000..ae420849
--- /dev/null
+++ b/electron-builder.json
@@ -0,0 +1,45 @@
+{
+ "productName": "TruevisionDesigner",
+ "directories": {
+ "output": "release/",
+ "buildResources": "build/"
+ },
+ "files": [
+ "**/*",
+ "!**/*.ts",
+ "!*.code-workspace",
+ "!LICENSE.md",
+ "!buildspec.yml",
+ "!package.json",
+ "!package-lock.json",
+ "!python-client/",
+ "!scenario/",
+ "!scripts/",
+ "!samples/",
+ "!src/",
+ "!e2e/",
+ "!hooks/",
+ "!angular.json",
+ "!_config.yml",
+ "!karma.conf.js",
+ "!tsconfig.json",
+ "!tslint.json",
+ "!third-party",
+ "!docs",
+ "!.vscode",
+ "!.idea",
+ "!.protos"
+ ],
+ "extraResources": [
+ "bin",
+ "default-project"
+ ],
+ "extraFiles": [],
+ "linux": {
+ "category": "Utility",
+ "target": [
+ "deb",
+ "dir"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/main.js b/main.js
new file mode 100644
index 00000000..2937f6e4
--- /dev/null
+++ b/main.js
@@ -0,0 +1,165 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+// Modules to control application life and create native browser window
+const { app, BrowserWindow, ipcMain, Menu } = require( 'electron' );
+const execFile = require( 'child_process' ).execFile;
+const path = require( 'path' );
+
+// Keep a global reference of the window object, if you don't, the window will
+// be closed automatically when the JavaScript object is garbage collected.
+let editorWindow;
+
+const TITLE = "Truevision"
+const MIN_WIDTH = 1280;
+const MIN_HEIGHT = 980;
+
+let openDevTools = false;
+let showSplashScreen = true;
+let loadLocahost = false;
+
+// load opengl to fix line rendering issues
+app.commandLine.appendSwitch( "use-angle", "gl" );
+
+process.argv.forEach( function ( arg, index, array ) {
+
+ if ( arg.includes( "open-dev-tools" ) ) {
+ openDevTools = true;
+ } else if ( arg.includes( "disable-splash" ) ) {
+ showSplashScreen = false;
+ } else if ( arg.includes( "localhost" ) ) {
+ loadLocahost = true;
+ }
+
+} );
+
+function openEditorWindow () {
+
+ // Create the browser window.
+ editorWindow = new BrowserWindow( {
+ title: TITLE,
+ width: MIN_WIDTH,
+ height: MIN_HEIGHT,
+ minWidth: MIN_WIDTH,
+ minHeight: MIN_HEIGHT,
+ backgroundColor: '#ffffff',
+ icon: `file://${ __dirname }/dist/assets/icon.png`,
+ webPreferences: {
+ nodeIntegration: true
+ }
+ } );
+
+ if ( loadLocahost ) {
+
+ editorWindow.loadURL( `http://localhost:4200` );
+
+ } else {
+
+ editorWindow.loadURL( `file://${ __dirname }/dist/index.html` );
+
+ }
+
+ // Open the DevTools.
+ if ( openDevTools ) editorWindow.webContents.openDevTools();
+
+ // Emitted when the window is closed.
+ editorWindow.on( 'closed', function () {
+ // Dereference the window object, usually you would store windows
+ // in an array if your app supports multi windows, this is the time
+ // when you should delete the corresponding element.
+ editorWindow = null
+ } )
+}
+
+function openSplashWindow () {
+
+ // Create the browser window.
+ let splashWindow = new BrowserWindow( {
+ title: TITLE,
+ width: 720,
+ height: 405,
+ backgroundColor: '#ffffff',
+ resizable: false,
+ movable: false,
+ minimizable: false,
+ maximizable: false,
+ closable: true,
+ alwaysOnTop: true,
+ fullscreenable: false,
+ frame: false,
+ icon: `file://${ __dirname }/dist/assets/icon.png`,
+ webPreferences: {
+ nodeIntegration: true
+ }
+ } );
+
+ splashWindow.loadURL( `file://${ __dirname }/src/splash.html` );
+
+ // Open the DevTools.
+ if ( openDevTools ) splashWindow.webContents.openDevTools();
+
+ // Emitted when the window is closed.
+ splashWindow.on( 'closed', function () {
+ // Dereference the window object, usually you would store windows
+ // in an array if your app supports multi windows, this is the time
+ // when you should delete the corresponding element.
+ splashWindow = null;
+ } );
+
+
+ setTimeout( () => {
+
+ openEditorWindow();
+
+ splashWindow.close();
+
+ }, 5000 );
+}
+
+function createWindow () {
+
+ if ( showSplashScreen == true ) {
+
+ openSplashWindow();
+
+ } else {
+
+ openEditorWindow();
+
+ }
+
+}
+
+// This method will be called when Electron has finished
+// initialization and is ready to create browser windows.
+// Some APIs can only be used after this event occurs.
+app.on( 'ready', createWindow )
+
+// app.commandLine.appendSwitch( 'remote-debugging-port', '9222' )
+
+// Quit when all windows are closed.
+app.on( 'window-all-closed', function () {
+ // On macOS it is common for applications and their menu bar
+ // to stay active until the user quits explicitly with Cmd + Q
+ if ( process.platform !== 'darwin' ) {
+ app.quit()
+ }
+} );
+
+app.on( 'activate', function () {
+ // On macOS it's common to re-create a window in the app when the
+ // dock icon is clicked and there are no other windows open.
+ if ( editorWindow === null ) {
+ createWindow()
+ }
+} );
+
+// helpers method to read current directory in angular
+ipcMain.on( 'current-directory', ( event, arg ) => {
+
+ event.returnValue = __dirname;
+
+} );
+
+Menu.setApplicationMenu( null );
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 00000000..05905e5b
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,14472 @@
+{
+ "name": "truevision-designer",
+ "version": "2022.3.01",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "7zip-bin": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.0.3.tgz",
+ "integrity": "sha512-GLyWIFBbGvpKPGo55JyRZAo4lVbnBiD52cKlw/0Vt+wnmKvWJkpZvsjVoaIolyBXDeAQKSicRtqFNPem9w0WYA==",
+ "dev": true
+ },
+ "7zip-bin-linux": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/7zip-bin-linux/-/7zip-bin-linux-1.3.1.tgz",
+ "integrity": "sha512-Wv1uEEeHbTiS1+ycpwUxYNuIcyohU6Y6vEqY3NquBkeqy0YhVdsNUGsj0XKSRciHR6LoJSEUuqYUexmws3zH7Q==",
+ "dev": true,
+ "optional": true
+ },
+ "7zip-bin-mac": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/7zip-bin-mac/-/7zip-bin-mac-1.0.1.tgz",
+ "integrity": "sha1-Pmh3i78JJq3GgVlCcHRQXUdVXAI=",
+ "dev": true,
+ "optional": true
+ },
+ "7zip-bin-win": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/7zip-bin-win/-/7zip-bin-win-2.1.1.tgz",
+ "integrity": "sha512-6VGEW7PXGroTsoI2QW3b0ea95HJmbVBHvfANKLLMzSzFA1zKqVX5ybNuhmeGpf6vA0x8FJTt6twpprDANsY5WQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@agm/core": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/@agm/core/-/core-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3bdfvkWDmJszpj/F6Fzgv7sks0cs/cUEQPfs37tcJFz3jc62SsXy4TGb/WJT8FpH2nSGE6DonP8lXuFxB0lblQ=="
+ },
+ "@angular-devkit/architect": {
+ "version": "0.13.3",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.13.3.tgz",
+ "integrity": "sha512-89VL75bq3+h3m0jhzWNqXqW+HQcrihnM3i6eiUE6P81LcllP159JMlusAvB1LHLNc6Cc62wTq4BJr7KDILkPOA==",
+ "dev": true,
+ "requires": {
+ "@angular-devkit/core": "7.3.3",
+ "rxjs": "6.3.3"
+ },
+ "dependencies": {
+ "@angular-devkit/core": {
+ "version": "7.3.3",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.3.tgz",
+ "integrity": "sha512-fosULDtMoDWrOyUzTmBkJccOy7zodo02kENyKai7vOv9EWfv9jytkVdNc+jl0ys9OE2QadvSYBo49jhnZxFXfQ==",
+ "dev": true,
+ "requires": {
+ "ajv": "6.9.1",
+ "chokidar": "2.0.4",
+ "fast-json-stable-stringify": "2.0.0",
+ "rxjs": "6.3.3",
+ "source-map": "0.7.3"
+ }
+ }
+ }
+ },
+ "@angular-devkit/build-angular": {
+ "version": "0.13.3",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.13.3.tgz",
+ "integrity": "sha512-UxD6UR/tXypMA4lqCiXLtcStI4wuIHLOJLwADmazndFjg1oLqH1onO6UQPHJ1drAUl+AzA5zTQZHzWYokxaLtg==",
+ "dev": true,
+ "requires": {
+ "@angular-devkit/architect": "0.13.3",
+ "@angular-devkit/build-optimizer": "0.13.3",
+ "@angular-devkit/build-webpack": "0.13.3",
+ "@angular-devkit/core": "7.3.3",
+ "@ngtools/webpack": "7.3.3",
+ "ajv": "6.9.1",
+ "autoprefixer": "9.4.6",
+ "circular-dependency-plugin": "5.0.2",
+ "clean-css": "4.2.1",
+ "copy-webpack-plugin": "4.6.0",
+ "file-loader": "3.0.1",
+ "glob": "7.1.3",
+ "istanbul-instrumenter-loader": "3.0.1",
+ "karma-source-map-support": "1.3.0",
+ "less": "3.9.0",
+ "less-loader": "4.1.0",
+ "license-webpack-plugin": "2.1.0",
+ "loader-utils": "1.2.3",
+ "mini-css-extract-plugin": "0.5.0",
+ "minimatch": "3.0.4",
+ "node-sass": "4.11.0",
+ "opn": "5.4.0",
+ "parse5": "4.0.0",
+ "postcss": "7.0.14",
+ "postcss-import": "12.0.1",
+ "postcss-loader": "3.0.0",
+ "raw-loader": "1.0.0",
+ "rxjs": "6.3.3",
+ "sass-loader": "7.1.0",
+ "semver": "5.6.0",
+ "source-map-loader": "0.2.4",
+ "source-map-support": "0.5.10",
+ "speed-measure-webpack-plugin": "1.3.0",
+ "stats-webpack-plugin": "0.7.0",
+ "style-loader": "0.23.1",
+ "stylus": "0.54.5",
+ "stylus-loader": "3.0.2",
+ "terser-webpack-plugin": "1.2.2",
+ "tree-kill": "1.2.1",
+ "webpack": "4.29.0",
+ "webpack-dev-middleware": "3.5.1",
+ "webpack-dev-server": "3.1.14",
+ "webpack-merge": "4.2.1",
+ "webpack-sources": "1.3.0",
+ "webpack-subresource-integrity": "1.1.0-rc.6"
+ },
+ "dependencies": {
+ "@angular-devkit/core": {
+ "version": "7.3.3",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.3.tgz",
+ "integrity": "sha512-fosULDtMoDWrOyUzTmBkJccOy7zodo02kENyKai7vOv9EWfv9jytkVdNc+jl0ys9OE2QadvSYBo49jhnZxFXfQ==",
+ "dev": true,
+ "requires": {
+ "ajv": "6.9.1",
+ "chokidar": "2.0.4",
+ "fast-json-stable-stringify": "2.0.0",
+ "rxjs": "6.3.3",
+ "source-map": "0.7.3"
+ }
+ },
+ "bluebird": {
+ "version": "3.7.2",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
+ "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
+ "dev": true
+ },
+ "copy-webpack-plugin": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.6.0.tgz",
+ "integrity": "sha512-Y+SQCF+0NoWQryez2zXn5J5knmr9z/9qSQt7fbL78u83rxmigOy8X5+BFn8CFSuX+nKT8gpYwJX68ekqtQt6ZA==",
+ "dev": true,
+ "requires": {
+ "cacache": "^10.0.4",
+ "find-cache-dir": "^1.0.0",
+ "globby": "^7.1.1",
+ "is-glob": "^4.0.0",
+ "loader-utils": "^1.1.0",
+ "minimatch": "^3.0.4",
+ "p-limit": "^1.0.0",
+ "serialize-javascript": "^1.4.0"
+ }
+ },
+ "find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^3.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "requires": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "make-dir": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
+ "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+ "dev": true,
+ "requires": {
+ "pify": "^4.0.1",
+ "semver": "^5.6.0"
+ }
+ },
+ "mississippi": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
+ "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==",
+ "dev": true,
+ "requires": {
+ "concat-stream": "^1.5.0",
+ "duplexify": "^3.4.2",
+ "end-of-stream": "^1.1.0",
+ "flush-write-stream": "^1.0.0",
+ "from2": "^2.1.0",
+ "parallel-transform": "^1.1.0",
+ "pump": "^3.0.0",
+ "pumpify": "^1.3.3",
+ "stream-each": "^1.1.0",
+ "through2": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.0.0"
+ },
+ "dependencies": {
+ "p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ }
+ }
+ },
+ "p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true
+ },
+ "pify": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+ "dev": true
+ },
+ "pkg-dir": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz",
+ "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
+ "dev": true,
+ "requires": {
+ "find-up": "^3.0.0"
+ }
+ },
+ "pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "ssri": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz",
+ "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==",
+ "dev": true,
+ "requires": {
+ "figgy-pudding": "^3.5.1"
+ }
+ },
+ "terser-webpack-plugin": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.2.2.tgz",
+ "integrity": "sha512-1DMkTk286BzmfylAvLXwpJrI7dWa5BnFmscV/2dCr8+c56egFcbaeFAl7+sujAjdmpLam21XRdhA4oifLyiWWg==",
+ "dev": true,
+ "requires": {
+ "cacache": "^11.0.2",
+ "find-cache-dir": "^2.0.0",
+ "schema-utils": "^1.0.0",
+ "serialize-javascript": "^1.4.0",
+ "source-map": "^0.6.1",
+ "terser": "^3.16.1",
+ "webpack-sources": "^1.1.0",
+ "worker-farm": "^1.5.2"
+ },
+ "dependencies": {
+ "cacache": {
+ "version": "11.3.3",
+ "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.3.tgz",
+ "integrity": "sha512-p8WcneCytvzPxhDvYp31PD039vi77I12W+/KfR9S8AZbaiARFBCpsPJS+9uhWfeBfeAtW7o/4vt3MUqLkbY6nA==",
+ "dev": true,
+ "requires": {
+ "bluebird": "^3.5.5",
+ "chownr": "^1.1.1",
+ "figgy-pudding": "^3.5.1",
+ "glob": "^7.1.4",
+ "graceful-fs": "^4.1.15",
+ "lru-cache": "^5.1.1",
+ "mississippi": "^3.0.0",
+ "mkdirp": "^0.5.1",
+ "move-concurrently": "^1.0.1",
+ "promise-inflight": "^1.0.1",
+ "rimraf": "^2.6.3",
+ "ssri": "^6.0.1",
+ "unique-filename": "^1.1.1",
+ "y18n": "^4.0.0"
+ }
+ },
+ "find-cache-dir": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz",
+ "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==",
+ "dev": true,
+ "requires": {
+ "commondir": "^1.0.1",
+ "make-dir": "^2.0.0",
+ "pkg-dir": "^3.0.0"
+ }
+ },
+ "glob": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
+ "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true
+ }
+ }
+ },
+ "@angular-devkit/build-optimizer": {
+ "version": "0.13.3",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.13.3.tgz",
+ "integrity": "sha512-lxM1icVFy3jyoQfWEGW8TG1M7LTl/Djc98MFBYp/lXoVo2JZoLxy7eo51sRuJFaB7/0mgMP2gs0FcU/Lr4gK+Q==",
+ "dev": true,
+ "requires": {
+ "loader-utils": "1.2.3",
+ "source-map": "0.5.6",
+ "typescript": "3.2.4",
+ "webpack-sources": "1.3.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz",
+ "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=",
+ "dev": true
+ }
+ }
+ },
+ "@angular-devkit/build-webpack": {
+ "version": "0.13.3",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.13.3.tgz",
+ "integrity": "sha512-o2ymctVCuz5GhKJH3LO1sl3AUbA4j7zlrqSGB5ToVRBn3GckJJnmfCZzr2SX5Ya4VofxVsIidsiZcawy4FpB2w==",
+ "dev": true,
+ "requires": {
+ "@angular-devkit/architect": "0.13.3",
+ "@angular-devkit/core": "7.3.3",
+ "rxjs": "6.3.3"
+ },
+ "dependencies": {
+ "@angular-devkit/core": {
+ "version": "7.3.3",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.3.tgz",
+ "integrity": "sha512-fosULDtMoDWrOyUzTmBkJccOy7zodo02kENyKai7vOv9EWfv9jytkVdNc+jl0ys9OE2QadvSYBo49jhnZxFXfQ==",
+ "dev": true,
+ "requires": {
+ "ajv": "6.9.1",
+ "chokidar": "2.0.4",
+ "fast-json-stable-stringify": "2.0.0",
+ "rxjs": "6.3.3",
+ "source-map": "0.7.3"
+ }
+ }
+ }
+ },
+ "@angular-devkit/core": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.0.6.tgz",
+ "integrity": "sha512-RPSXUtLrpYDTqAEL0rCyDKxES76EomsPBvUUZTD6UkE2pihoh9ZIxkzhzlE+HU/xdqm28+smQYFhvvEAXFWwSQ==",
+ "dev": true,
+ "requires": {
+ "ajv": "6.5.3",
+ "chokidar": "2.0.4",
+ "fast-json-stable-stringify": "2.0.0",
+ "rxjs": "6.3.3",
+ "source-map": "0.7.3"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "6.5.3",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz",
+ "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^2.0.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ }
+ }
+ },
+ "@angular-devkit/schematics": {
+ "version": "7.3.3",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-7.3.3.tgz",
+ "integrity": "sha512-SdDq9eKwceb6WLwci1fywtZ/kARR5CYyzi5dZIR1lOxrz00682uUBqH/X39mKdqc6eVqR7rtPceqNm6nQpOIMg==",
+ "dev": true,
+ "requires": {
+ "@angular-devkit/core": "7.3.3",
+ "rxjs": "6.3.3"
+ },
+ "dependencies": {
+ "@angular-devkit/core": {
+ "version": "7.3.3",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.3.tgz",
+ "integrity": "sha512-fosULDtMoDWrOyUzTmBkJccOy7zodo02kENyKai7vOv9EWfv9jytkVdNc+jl0ys9OE2QadvSYBo49jhnZxFXfQ==",
+ "dev": true,
+ "requires": {
+ "ajv": "6.9.1",
+ "chokidar": "2.0.4",
+ "fast-json-stable-stringify": "2.0.0",
+ "rxjs": "6.3.3",
+ "source-map": "0.7.3"
+ }
+ }
+ }
+ },
+ "@angular/animations": {
+ "version": "7.2.6",
+ "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-7.2.6.tgz",
+ "integrity": "sha512-ICKPS+bKabhQNqnPoVZegUAhgNPbVFlrxHoJ+ZZeVGxw5iBE8TnP3a2sRvakdMTKhykDlwVVGMKLxu2Y34uhmg==",
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "@angular/cdk": {
+ "version": "7.3.3",
+ "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-7.3.3.tgz",
+ "integrity": "sha512-0M3nwq+c9+d+p0XeU12I9djutlsajRrxcu7AvHQXUs/5grYFsXsX0f468qXDiKmcgqGUBNtN2fBOUhGNlispuQ==",
+ "requires": {
+ "parse5": "^5.0.0",
+ "tslib": "^1.7.1"
+ },
+ "dependencies": {
+ "parse5": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz",
+ "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==",
+ "optional": true
+ }
+ }
+ },
+ "@angular/cli": {
+ "version": "7.3.3",
+ "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-7.3.3.tgz",
+ "integrity": "sha512-dw1iBOYbQRN2l/BH21zDItDFC9KXgqeK0A/koDLDukjrUAnW/XVATjxGi+7EPlTpABTFhqu/rHZDy8aBglLDXQ==",
+ "dev": true,
+ "requires": {
+ "@angular-devkit/architect": "0.13.3",
+ "@angular-devkit/core": "7.3.3",
+ "@angular-devkit/schematics": "7.3.3",
+ "@schematics/angular": "7.3.3",
+ "@schematics/update": "0.13.3",
+ "@yarnpkg/lockfile": "1.1.0",
+ "ini": "1.3.5",
+ "inquirer": "6.2.1",
+ "npm-package-arg": "6.1.0",
+ "opn": "5.4.0",
+ "pacote": "9.4.0",
+ "semver": "5.6.0",
+ "symbol-observable": "1.2.0"
+ },
+ "dependencies": {
+ "@angular-devkit/core": {
+ "version": "7.3.3",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.3.tgz",
+ "integrity": "sha512-fosULDtMoDWrOyUzTmBkJccOy7zodo02kENyKai7vOv9EWfv9jytkVdNc+jl0ys9OE2QadvSYBo49jhnZxFXfQ==",
+ "dev": true,
+ "requires": {
+ "ajv": "6.9.1",
+ "chokidar": "2.0.4",
+ "fast-json-stable-stringify": "2.0.0",
+ "rxjs": "6.3.3",
+ "source-map": "0.7.3"
+ }
+ },
+ "ini": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
+ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
+ "dev": true
+ }
+ }
+ },
+ "@angular/common": {
+ "version": "7.2.6",
+ "resolved": "https://registry.npmjs.org/@angular/common/-/common-7.2.6.tgz",
+ "integrity": "sha512-jzWUgsgS0dmPy7yDHX4qCqVpt7ZZmHhApgkg5RkzTAlp+0cvZ/KsDpBgHXnZUIfmk/5g1/EtTbkbClgp1kCkIg==",
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "@angular/compiler": {
+ "version": "7.2.6",
+ "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-7.2.6.tgz",
+ "integrity": "sha512-GXdvgH8oxK8HRh/FelN3U5p0tsTUwGh8b/iuuJKaunBSSDDjIy7pPnn3zT+lN4YeEi6qN1XWudt+HpWHYHyymg==",
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "@angular/compiler-cli": {
+ "version": "7.2.6",
+ "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-7.2.6.tgz",
+ "integrity": "sha512-sB0Bc5hE1zoXKwK4E9pC/UblCEHi3CwLBT/1nmVMYBdGzSSUxO4FaioJi+SCeGJJ+kk85Vny2up08gnupmLKqA==",
+ "dev": true,
+ "requires": {
+ "canonical-path": "1.0.0",
+ "chokidar": "^2.1.1",
+ "convert-source-map": "^1.5.1",
+ "dependency-graph": "^0.7.2",
+ "magic-string": "^0.25.0",
+ "minimist": "^1.2.0",
+ "reflect-metadata": "^0.1.2",
+ "shelljs": "^0.8.1",
+ "source-map": "^0.6.1",
+ "tslib": "^1.9.0",
+ "yargs": "9.0.1"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
+ },
+ "camelcase": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
+ "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
+ "dev": true
+ },
+ "chokidar": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.2.tgz",
+ "integrity": "sha512-IwXUx0FXc5ibYmPC2XeEj5mpXoV66sR+t3jqu2NS2GYwCktt3KF1/Qqjws/NkegajBA4RbZ5+DDwlOiJsxDHEg==",
+ "dev": true,
+ "requires": {
+ "anymatch": "^2.0.0",
+ "async-each": "^1.0.1",
+ "braces": "^2.3.2",
+ "fsevents": "^1.2.7",
+ "glob-parent": "^3.1.0",
+ "inherits": "^2.0.3",
+ "is-binary-path": "^1.0.0",
+ "is-glob": "^4.0.0",
+ "normalize-path": "^3.0.0",
+ "path-is-absolute": "^1.0.0",
+ "readdirp": "^2.2.1",
+ "upath": "^1.1.0"
+ }
+ },
+ "cross-spawn": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
+ "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^4.0.1",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ }
+ },
+ "execa": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz",
+ "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^5.0.1",
+ "get-stream": "^3.0.0",
+ "is-stream": "^1.1.0",
+ "npm-run-path": "^2.0.0",
+ "p-finally": "^1.0.0",
+ "signal-exit": "^3.0.0",
+ "strip-eof": "^1.0.0"
+ }
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
+ },
+ "load-json-file": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
+ "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "parse-json": "^2.2.0",
+ "pify": "^2.0.0",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "mem": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz",
+ "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=",
+ "dev": true,
+ "requires": {
+ "mimic-fn": "^1.0.0"
+ }
+ },
+ "normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true
+ },
+ "os-locale": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz",
+ "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==",
+ "dev": true,
+ "requires": {
+ "execa": "^0.7.0",
+ "lcid": "^1.0.0",
+ "mem": "^1.1.0"
+ }
+ },
+ "path-type": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
+ "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
+ "dev": true,
+ "requires": {
+ "pify": "^2.0.0"
+ }
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ },
+ "read-pkg": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
+ "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
+ "dev": true,
+ "requires": {
+ "load-json-file": "^2.0.0",
+ "normalize-package-data": "^2.3.2",
+ "path-type": "^2.0.0"
+ }
+ },
+ "read-pkg-up": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
+ "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
+ "dev": true,
+ "requires": {
+ "find-up": "^2.0.0",
+ "read-pkg": "^2.0.0"
+ }
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "string-width": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "dev": true,
+ "requires": {
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^4.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ },
+ "strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+ "dev": true
+ },
+ "which-module": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
+ "dev": true
+ },
+ "y18n": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
+ "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
+ "dev": true
+ },
+ "yargs": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-9.0.1.tgz",
+ "integrity": "sha1-UqzCP+7Kw0BCB47njAwAf1CF20w=",
+ "dev": true,
+ "requires": {
+ "camelcase": "^4.1.0",
+ "cliui": "^3.2.0",
+ "decamelize": "^1.1.1",
+ "get-caller-file": "^1.0.1",
+ "os-locale": "^2.0.0",
+ "read-pkg-up": "^2.0.0",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^1.0.1",
+ "set-blocking": "^2.0.0",
+ "string-width": "^2.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^3.2.1",
+ "yargs-parser": "^7.0.0"
+ }
+ },
+ "yargs-parser": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz",
+ "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=",
+ "dev": true,
+ "requires": {
+ "camelcase": "^4.1.0"
+ }
+ }
+ }
+ },
+ "@angular/core": {
+ "version": "7.2.6",
+ "resolved": "https://registry.npmjs.org/@angular/core/-/core-7.2.6.tgz",
+ "integrity": "sha512-MZg17DWH1KUoDa9wFYK9Z+3F7DnUW2DjSwGyIi9U4cB54IWFhgt1JsA0mcuSYuRSRpvwaArCDC2AN90f+0/EFA==",
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "@angular/flex-layout": {
+ "version": "7.0.0-beta.23",
+ "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-7.0.0-beta.23.tgz",
+ "integrity": "sha512-jH2i3i/M7SbK6scVlj2urVL5OhzwavbQ7KwvUjyc/UwccKnnzuOuWEGCINLja/aoaUO3I35LluCLv6a6VN0olA==",
+ "requires": {
+ "tslib": "^1.7.1"
+ }
+ },
+ "@angular/forms": {
+ "version": "7.2.10",
+ "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-7.2.10.tgz",
+ "integrity": "sha512-fQccon0Yuni13QJt16npSRlkitPZBLXfWXDFwCEybo/QqtSar3BOJAQFW2yqokrfW5lbO5VDFJ7Pj2dDyBXEAA==",
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "@angular/http": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@angular/http/-/http-7.2.0.tgz",
+ "integrity": "sha512-CoEwfh9LSpyXUh/077nvy1Hheo6mmIFKOmnyo9V7JaesGJYkYpEIVFYFP1hYg7f8snKHclT3gqfQX+BynNhExQ==",
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "@angular/language-service": {
+ "version": "7.2.6",
+ "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-7.2.6.tgz",
+ "integrity": "sha512-iFKgaat5MZlixsO5dqy1Km3tb4q3iAU+ZPMJCk7DN419aizF38UFnQY1gCYkw3dxZLZGIkXnwy8szGXUEhra/A==",
+ "dev": true
+ },
+ "@angular/material": {
+ "version": "7.3.3",
+ "resolved": "https://registry.npmjs.org/@angular/material/-/material-7.3.3.tgz",
+ "integrity": "sha512-DZdJaVpXsd5QlfpN5P871llw8AKh5QvRiyro3QRmEajYN85Xiawqpbt7O60TrwcFM6DzYLI3UeyWq8LFdmi/+Q==",
+ "requires": {
+ "tslib": "^1.7.1"
+ }
+ },
+ "@angular/platform-browser": {
+ "version": "7.2.6",
+ "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-7.2.6.tgz",
+ "integrity": "sha512-VE4yS4l8Cdx6DlvrbOFOZDKmQuyz1RhVcshgSt9hKlkehvAXMtX8Sqnp6po7z0aPykTh0TZZtMtLEerkFEe+DA==",
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "@angular/platform-browser-dynamic": {
+ "version": "7.2.6",
+ "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-7.2.6.tgz",
+ "integrity": "sha512-/co/q4v11nKin2MFscCMZyixbW103I2FxbPgCAYBN5NSvfIwTrt5J6xWmDoKJ8HkZBqL3R9B+uhYdzsRN/pQxg==",
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "@angular/router": {
+ "version": "7.2.6",
+ "resolved": "https://registry.npmjs.org/@angular/router/-/router-7.2.6.tgz",
+ "integrity": "sha512-ayMVor4Mu4wk7JKpt51UxHovnLB4munZ8ELR1CA4w+s0rJsSSwyB4WXElC+DbgCyl7BYLAaGui2c5DbTAJ9jlw==",
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "@auth0/angular-jwt": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@auth0/angular-jwt/-/angular-jwt-3.0.0.tgz",
+ "integrity": "sha512-Ky8hghnEx+CtCd097YXji08/LvLTG98IAEX/j1UgnutRDhQ31eczOohDn98v3i3MHNfLjfI3HdyxPK1Qc0IkZw==",
+ "requires": {
+ "url": "^0.11.0"
+ }
+ },
+ "@babel/helper-environment-visitor": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz",
+ "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.16.7"
+ },
+ "dependencies": {
+ "@babel/types": {
+ "version": "7.16.8",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.8.tgz",
+ "integrity": "sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.16.7",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "to-fast-properties": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
+ "dev": true
+ }
+ }
+ },
+ "@babel/helper-hoist-variables": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz",
+ "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.16.7"
+ },
+ "dependencies": {
+ "@babel/types": {
+ "version": "7.16.8",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.8.tgz",
+ "integrity": "sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.16.7",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "to-fast-properties": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
+ "dev": true
+ }
+ }
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz",
+ "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==",
+ "dev": true
+ },
+ "@ctrl/tinycolor": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.4.0.tgz",
+ "integrity": "sha512-JZButFdZ1+/xAfpguQHoabIXkcqRRKpMrWKBkpEZZyxfY9C1DpADFB8PEqGSTeFr135SaTRfKqGKx5xSCLI7ZQ==",
+ "dev": true
+ },
+ "@develar/schema-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.1.0.tgz",
+ "integrity": "sha512-qjCqB4ctMig9Gz5bd6lkdFr3bO6arOdQqptdBSpF1ZpCnjofieCciEzkoS9ujY9cMGyllYSCSmBJ3x9OKHXzoA==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.1.0",
+ "ajv-keywords": "^3.1.0"
+ }
+ },
+ "@electron/get": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.3.1.tgz",
+ "integrity": "sha512-FozkPRcRbHmuXh/qnry+s/GJ84hRAaUuEYYfZHvEl8n9X7a4ndhh1tVF+w4gky69XP+KHlLEO+OR95WqDy2mfg==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.1",
+ "env-paths": "^2.2.0",
+ "fs-extra": "^7.0.1",
+ "got": "^9.6.0",
+ "sumchecker": "^3.0.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "env-paths": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz",
+ "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==",
+ "dev": true
+ },
+ "fs-extra": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
+ "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "sumchecker": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.0.tgz",
+ "integrity": "sha512-yreseuC/z4iaodVoq07XULEOO9p4jnQazO7mbrnDSvWAU/y2cbyIKs+gWJptfcGu9R+1l27K8Rkj0bfvqnBpgQ==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.0"
+ }
+ }
+ }
+ },
+ "@grpc/proto-loader": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.1.0.tgz",
+ "integrity": "sha512-GHoZBR5N+65AWazsCiJDxeMSbUpp2qNYbjeD3mP27L2PT25+GvIupIIJiCgULH87NAiAd9SmWMRBVz+uSiHpgA==",
+ "requires": {
+ "@types/lodash": "^4.14.104",
+ "@types/node": "^9.4.6",
+ "lodash": "^4.17.5",
+ "protobufjs": "^6.8.6"
+ },
+ "dependencies": {
+ "@types/node": {
+ "version": "9.6.49",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.49.tgz",
+ "integrity": "sha512-YY0Okyn4QXC4ugJI+Kng5iWjK8A6eIHiQVaGIhJkyn0YL6Iqo0E0tBC8BuhvYcBK87vykBijM5FtMnCqaa5anA=="
+ },
+ "long": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+ "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
+ },
+ "protobufjs": {
+ "version": "6.8.8",
+ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz",
+ "integrity": "sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw==",
+ "requires": {
+ "@protobufjs/aspromise": "^1.1.2",
+ "@protobufjs/base64": "^1.1.2",
+ "@protobufjs/codegen": "^2.0.4",
+ "@protobufjs/eventemitter": "^1.1.0",
+ "@protobufjs/fetch": "^1.1.0",
+ "@protobufjs/float": "^1.0.2",
+ "@protobufjs/inquire": "^1.1.0",
+ "@protobufjs/path": "^1.1.2",
+ "@protobufjs/pool": "^1.1.0",
+ "@protobufjs/utf8": "^1.1.0",
+ "@types/long": "^4.0.0",
+ "@types/node": "^10.1.0",
+ "long": "^4.0.0"
+ },
+ "dependencies": {
+ "@types/node": {
+ "version": "10.14.9",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.9.tgz",
+ "integrity": "sha512-NelG/dSahlXYtSoVPErrp06tYFrvzj8XLWmKA+X8x0W//4MqbUyZu++giUG/v0bjAT6/Qxa8IjodrfdACyb0Fg=="
+ }
+ }
+ }
+ }
+ },
+ "@ncstate/sat-popover": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@ncstate/sat-popover/-/sat-popover-4.0.0.tgz",
+ "integrity": "sha512-JWEvphJ5BSOQLn5E8f9F/dKVrgVozHCKc930iLL7xbTa+nCP9jBFNi5MWjlbU81AXLtBweSlPHnIyqF7bmjy4A==",
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "@ngtools/webpack": {
+ "version": "7.3.3",
+ "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-7.3.3.tgz",
+ "integrity": "sha512-G/1P00XHWVrKT3qoSyy7yAPT5/fuja84YifcGg/2SwmNNo4hTXxWhqec0/uHwgQr6nYhGDyzwwXYeKKyQkcfgw==",
+ "dev": true,
+ "requires": {
+ "@angular-devkit/core": "7.3.3",
+ "enhanced-resolve": "4.1.0",
+ "rxjs": "6.3.3",
+ "tree-kill": "1.2.1",
+ "webpack-sources": "1.3.0"
+ },
+ "dependencies": {
+ "@angular-devkit/core": {
+ "version": "7.3.3",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.3.tgz",
+ "integrity": "sha512-fosULDtMoDWrOyUzTmBkJccOy7zodo02kENyKai7vOv9EWfv9jytkVdNc+jl0ys9OE2QadvSYBo49jhnZxFXfQ==",
+ "dev": true,
+ "requires": {
+ "ajv": "6.9.1",
+ "chokidar": "2.0.4",
+ "fast-json-stable-stringify": "2.0.0",
+ "rxjs": "6.3.3",
+ "source-map": "0.7.3"
+ }
+ }
+ }
+ },
+ "@ngu/carousel": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/@ngu/carousel/-/carousel-1.5.4.tgz",
+ "integrity": "sha512-Wf4vowWhitpptLNeCdO/LrBA+EDHT7E9OFftV78JeCrPQ7MEJ6w9RjdtHqC5QotqrMVSXenaoeifH6LZGYsGsw==",
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "@ngx-translate/core": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/@ngx-translate/core/-/core-10.0.1.tgz",
+ "integrity": "sha1-nzo+0HfoR90NKVGmZNpu40igbSI=",
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "@ngx-translate/http-loader": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@ngx-translate/http-loader/-/http-loader-3.0.1.tgz",
+ "integrity": "sha1-ILD5i8bCUyESnT4zAqs8xInApCo=",
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "@protobufjs/aspromise": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+ "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78="
+ },
+ "@protobufjs/base64": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+ "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="
+ },
+ "@protobufjs/codegen": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
+ "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="
+ },
+ "@protobufjs/eventemitter": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
+ "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A="
+ },
+ "@protobufjs/fetch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
+ "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=",
+ "requires": {
+ "@protobufjs/aspromise": "^1.1.1",
+ "@protobufjs/inquire": "^1.1.0"
+ }
+ },
+ "@protobufjs/float": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+ "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E="
+ },
+ "@protobufjs/inquire": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
+ "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik="
+ },
+ "@protobufjs/path": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+ "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0="
+ },
+ "@protobufjs/pool": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+ "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q="
+ },
+ "@protobufjs/utf8": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
+ "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA="
+ },
+ "@schematics/angular": {
+ "version": "7.3.3",
+ "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-7.3.3.tgz",
+ "integrity": "sha512-HbH8vajYPka0xGcFAN5IUBx8n8SFMQLFb9di2dJCOBaEakbKVkk8qtOpil54oFQbx7DFCvutq/p0u42JfEbuMQ==",
+ "dev": true,
+ "requires": {
+ "@angular-devkit/core": "7.3.3",
+ "@angular-devkit/schematics": "7.3.3",
+ "typescript": "3.2.4"
+ },
+ "dependencies": {
+ "@angular-devkit/core": {
+ "version": "7.3.3",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.3.tgz",
+ "integrity": "sha512-fosULDtMoDWrOyUzTmBkJccOy7zodo02kENyKai7vOv9EWfv9jytkVdNc+jl0ys9OE2QadvSYBo49jhnZxFXfQ==",
+ "dev": true,
+ "requires": {
+ "ajv": "6.9.1",
+ "chokidar": "2.0.4",
+ "fast-json-stable-stringify": "2.0.0",
+ "rxjs": "6.3.3",
+ "source-map": "0.7.3"
+ }
+ }
+ }
+ },
+ "@schematics/update": {
+ "version": "0.13.3",
+ "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.13.3.tgz",
+ "integrity": "sha512-sCOFQ62dd7VdEGiSUJNZshNI31ODwpJjn2WIvFgZLt6sdHHun67s/JOvOUq4mxx6I74oD6RPJPF4AP5sigVxxg==",
+ "dev": true,
+ "requires": {
+ "@angular-devkit/core": "7.3.3",
+ "@angular-devkit/schematics": "7.3.3",
+ "@yarnpkg/lockfile": "1.1.0",
+ "ini": "1.3.5",
+ "pacote": "9.4.0",
+ "rxjs": "6.3.3",
+ "semver": "5.6.0",
+ "semver-intersect": "1.4.0"
+ },
+ "dependencies": {
+ "@angular-devkit/core": {
+ "version": "7.3.3",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.3.3.tgz",
+ "integrity": "sha512-fosULDtMoDWrOyUzTmBkJccOy7zodo02kENyKai7vOv9EWfv9jytkVdNc+jl0ys9OE2QadvSYBo49jhnZxFXfQ==",
+ "dev": true,
+ "requires": {
+ "ajv": "6.9.1",
+ "chokidar": "2.0.4",
+ "fast-json-stable-stringify": "2.0.0",
+ "rxjs": "6.3.3",
+ "source-map": "0.7.3"
+ }
+ },
+ "ini": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
+ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
+ "dev": true
+ }
+ }
+ },
+ "@sindresorhus/is": {
+ "version": "0.14.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
+ "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==",
+ "dev": true
+ },
+ "@szmarczak/http-timer": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz",
+ "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==",
+ "dev": true,
+ "requires": {
+ "defer-to-connect": "^1.0.1"
+ }
+ },
+ "@types/debug": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz",
+ "integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==",
+ "dev": true
+ },
+ "@types/jasmine": {
+ "version": "2.8.16",
+ "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.8.16.tgz",
+ "integrity": "sha512-056oRlBBp7MDzr+HoU5su099s/s7wjZ3KcHxLfv+Byqb9MwdLUvsfLgw1VS97hsh3ddxSPyQu+olHMnoVTUY6g==",
+ "dev": true
+ },
+ "@types/jasminewd2": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.6.tgz",
+ "integrity": "sha512-2ZOKrxb8bKRmP/po5ObYnRDgFE4i+lQiEB27bAMmtMWLgJSqlIDqlLx6S0IRorpOmOPRQ6O80NujTmQAtBkeNw==",
+ "dev": true,
+ "requires": {
+ "@types/jasmine": "*"
+ }
+ },
+ "@types/lodash": {
+ "version": "4.14.134",
+ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.134.tgz",
+ "integrity": "sha512-2/O0khFUCFeDlbi7sZ7ZFRCcT812fAeOLm7Ev4KbwASkZ575TDrDcY7YyaoHdTOzKcNbfiwLYZqPmoC4wadrsw=="
+ },
+ "@types/long": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.0.tgz",
+ "integrity": "sha512-1w52Nyx4Gq47uuu0EVcsHBxZFJgurQ+rTKS3qMHxR1GY2T8c2AJYd6vZoZ9q1rupaDjU0yT+Jc2XTyXkjeMA+Q=="
+ },
+ "@types/mixpanel-browser": {
+ "version": "2.23.1",
+ "resolved": "https://registry.npmjs.org/@types/mixpanel-browser/-/mixpanel-browser-2.23.1.tgz",
+ "integrity": "sha512-wt6ZkMWxWSpGcIK1p3Mvu5XwqmQw7U1gE+rL/2ZilWIG4M5Lyy11CNNE2iA6cirbxN3Qe7LoACiyybYRuAbLRw=="
+ },
+ "@types/node": {
+ "version": "8.9.5",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-8.9.5.tgz",
+ "integrity": "sha512-jRHfWsvyMtXdbhnz5CVHxaBgnV6duZnPlQuRSo/dm/GnmikNcmZhxIES4E9OZjUmQ8C+HCl4KJux+cXN/ErGDQ==",
+ "dev": true
+ },
+ "@types/q": {
+ "version": "0.0.32",
+ "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz",
+ "integrity": "sha1-vShOV8hPEyXacCur/IKlMoGQwMU=",
+ "dev": true
+ },
+ "@types/quill": {
+ "version": "1.3.10",
+ "resolved": "https://registry.npmjs.org/@types/quill/-/quill-1.3.10.tgz",
+ "integrity": "sha512-IhW3fPW+bkt9MLNlycw8u8fWb7oO7W5URC9MfZYHBlA24rex9rs23D5DETChu1zvgVdc5ka64ICjJOgQMr6Shw==",
+ "requires": {
+ "parchment": "^1.1.2"
+ }
+ },
+ "@types/selenium-webdriver": {
+ "version": "3.0.15",
+ "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.15.tgz",
+ "integrity": "sha512-5nh8/K2u9p4bk95GGCJB7KBvewaB0TUziZ9DTr+mR2I6RoO4OJVqx7rxK83hs2J1tomwtCGkhiW+Dy8EUnfB+Q==",
+ "dev": true
+ },
+ "@types/source-list-map": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz",
+ "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==",
+ "dev": true
+ },
+ "@types/three": {
+ "version": "0.93.31",
+ "resolved": "https://registry.npmjs.org/@types/three/-/three-0.93.31.tgz",
+ "integrity": "sha512-lR9eLY3gnrUDAdW4ujITgVTPEv1Oy2yoL3LlfKAnjQuzyjGSR+PvQuXuWmaDCD1IyWhkqbnol1nJwaW0MGe35A==",
+ "dev": true
+ },
+ "@types/webpack-sources": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.5.tgz",
+ "integrity": "sha512-zfvjpp7jiafSmrzJ2/i3LqOyTYTuJ7u1KOXlKgDlvsj9Rr0x7ZiYu5lZbXwobL7lmsRNtPXlBfmaUD8eU2Hu8w==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*",
+ "@types/source-list-map": "*",
+ "source-map": "^0.6.1"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "@webassemblyjs/ast": {
+ "version": "1.7.11",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.7.11.tgz",
+ "integrity": "sha512-ZEzy4vjvTzScC+SH8RBssQUawpaInUdMTYwYYLh54/s8TuT0gBLuyUnppKsVyZEi876VmmStKsUs28UxPgdvrA==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/helper-module-context": "1.7.11",
+ "@webassemblyjs/helper-wasm-bytecode": "1.7.11",
+ "@webassemblyjs/wast-parser": "1.7.11"
+ }
+ },
+ "@webassemblyjs/floating-point-hex-parser": {
+ "version": "1.7.11",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.7.11.tgz",
+ "integrity": "sha512-zY8dSNyYcgzNRNT666/zOoAyImshm3ycKdoLsyDw/Bwo6+/uktb7p4xyApuef1dwEBo/U/SYQzbGBvV+nru2Xg==",
+ "dev": true
+ },
+ "@webassemblyjs/helper-api-error": {
+ "version": "1.7.11",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.7.11.tgz",
+ "integrity": "sha512-7r1qXLmiglC+wPNkGuXCvkmalyEstKVwcueZRP2GNC2PAvxbLYwLLPr14rcdJaE4UtHxQKfFkuDFuv91ipqvXg==",
+ "dev": true
+ },
+ "@webassemblyjs/helper-buffer": {
+ "version": "1.7.11",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.7.11.tgz",
+ "integrity": "sha512-MynuervdylPPh3ix+mKZloTcL06P8tenNH3sx6s0qE8SLR6DdwnfgA7Hc9NSYeob2jrW5Vql6GVlsQzKQCa13w==",
+ "dev": true
+ },
+ "@webassemblyjs/helper-code-frame": {
+ "version": "1.7.11",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.7.11.tgz",
+ "integrity": "sha512-T8ESC9KMXFTXA5urJcyor5cn6qWeZ4/zLPyWeEXZ03hj/x9weSokGNkVCdnhSabKGYWxElSdgJ+sFa9G/RdHNw==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/wast-printer": "1.7.11"
+ }
+ },
+ "@webassemblyjs/helper-fsm": {
+ "version": "1.7.11",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.7.11.tgz",
+ "integrity": "sha512-nsAQWNP1+8Z6tkzdYlXT0kxfa2Z1tRTARd8wYnc/e3Zv3VydVVnaeePgqUzFrpkGUyhUUxOl5ML7f1NuT+gC0A==",
+ "dev": true
+ },
+ "@webassemblyjs/helper-module-context": {
+ "version": "1.7.11",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.7.11.tgz",
+ "integrity": "sha512-JxfD5DX8Ygq4PvXDucq0M+sbUFA7BJAv/GGl9ITovqE+idGX+J3QSzJYz+LwQmL7fC3Rs+utvWoJxDb6pmC0qg==",
+ "dev": true
+ },
+ "@webassemblyjs/helper-wasm-bytecode": {
+ "version": "1.7.11",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.7.11.tgz",
+ "integrity": "sha512-cMXeVS9rhoXsI9LLL4tJxBgVD/KMOKXuFqYb5oCJ/opScWpkCMEz9EJtkonaNcnLv2R3K5jIeS4TRj/drde1JQ==",
+ "dev": true
+ },
+ "@webassemblyjs/helper-wasm-section": {
+ "version": "1.7.11",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.7.11.tgz",
+ "integrity": "sha512-8ZRY5iZbZdtNFE5UFunB8mmBEAbSI3guwbrsCl4fWdfRiAcvqQpeqd5KHhSWLL5wuxo53zcaGZDBU64qgn4I4Q==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.7.11",
+ "@webassemblyjs/helper-buffer": "1.7.11",
+ "@webassemblyjs/helper-wasm-bytecode": "1.7.11",
+ "@webassemblyjs/wasm-gen": "1.7.11"
+ }
+ },
+ "@webassemblyjs/ieee754": {
+ "version": "1.7.11",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.7.11.tgz",
+ "integrity": "sha512-Mmqx/cS68K1tSrvRLtaV/Lp3NZWzXtOHUW2IvDvl2sihAwJh4ACE0eL6A8FvMyDG9abes3saB6dMimLOs+HMoQ==",
+ "dev": true,
+ "requires": {
+ "@xtuc/ieee754": "^1.2.0"
+ }
+ },
+ "@webassemblyjs/leb128": {
+ "version": "1.7.11",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.7.11.tgz",
+ "integrity": "sha512-vuGmgZjjp3zjcerQg+JA+tGOncOnJLWVkt8Aze5eWQLwTQGNgVLcyOTqgSCxWTR4J42ijHbBxnuRaL1Rv7XMdw==",
+ "dev": true,
+ "requires": {
+ "@xtuc/long": "4.2.1"
+ }
+ },
+ "@webassemblyjs/utf8": {
+ "version": "1.7.11",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.7.11.tgz",
+ "integrity": "sha512-C6GFkc7aErQIAH+BMrIdVSmW+6HSe20wg57HEC1uqJP8E/xpMjXqQUxkQw07MhNDSDcGpxI9G5JSNOQCqJk4sA==",
+ "dev": true
+ },
+ "@webassemblyjs/wasm-edit": {
+ "version": "1.7.11",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.7.11.tgz",
+ "integrity": "sha512-FUd97guNGsCZQgeTPKdgxJhBXkUbMTY6hFPf2Y4OedXd48H97J+sOY2Ltaq6WGVpIH8o/TGOVNiVz/SbpEMJGg==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.7.11",
+ "@webassemblyjs/helper-buffer": "1.7.11",
+ "@webassemblyjs/helper-wasm-bytecode": "1.7.11",
+ "@webassemblyjs/helper-wasm-section": "1.7.11",
+ "@webassemblyjs/wasm-gen": "1.7.11",
+ "@webassemblyjs/wasm-opt": "1.7.11",
+ "@webassemblyjs/wasm-parser": "1.7.11",
+ "@webassemblyjs/wast-printer": "1.7.11"
+ }
+ },
+ "@webassemblyjs/wasm-gen": {
+ "version": "1.7.11",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.7.11.tgz",
+ "integrity": "sha512-U/KDYp7fgAZX5KPfq4NOupK/BmhDc5Kjy2GIqstMhvvdJRcER/kUsMThpWeRP8BMn4LXaKhSTggIJPOeYHwISA==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.7.11",
+ "@webassemblyjs/helper-wasm-bytecode": "1.7.11",
+ "@webassemblyjs/ieee754": "1.7.11",
+ "@webassemblyjs/leb128": "1.7.11",
+ "@webassemblyjs/utf8": "1.7.11"
+ }
+ },
+ "@webassemblyjs/wasm-opt": {
+ "version": "1.7.11",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.7.11.tgz",
+ "integrity": "sha512-XynkOwQyiRidh0GLua7SkeHvAPXQV/RxsUeERILmAInZegApOUAIJfRuPYe2F7RcjOC9tW3Cb9juPvAC/sCqvg==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.7.11",
+ "@webassemblyjs/helper-buffer": "1.7.11",
+ "@webassemblyjs/wasm-gen": "1.7.11",
+ "@webassemblyjs/wasm-parser": "1.7.11"
+ }
+ },
+ "@webassemblyjs/wasm-parser": {
+ "version": "1.7.11",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.7.11.tgz",
+ "integrity": "sha512-6lmXRTrrZjYD8Ng8xRyvyXQJYUQKYSXhJqXOBLw24rdiXsHAOlvw5PhesjdcaMadU/pyPQOJ5dHreMjBxwnQKg==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.7.11",
+ "@webassemblyjs/helper-api-error": "1.7.11",
+ "@webassemblyjs/helper-wasm-bytecode": "1.7.11",
+ "@webassemblyjs/ieee754": "1.7.11",
+ "@webassemblyjs/leb128": "1.7.11",
+ "@webassemblyjs/utf8": "1.7.11"
+ }
+ },
+ "@webassemblyjs/wast-parser": {
+ "version": "1.7.11",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.7.11.tgz",
+ "integrity": "sha512-lEyVCg2np15tS+dm7+JJTNhNWq9yTZvi3qEhAIIOaofcYlUp0UR5/tVqOwa/gXYr3gjwSZqw+/lS9dscyLelbQ==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.7.11",
+ "@webassemblyjs/floating-point-hex-parser": "1.7.11",
+ "@webassemblyjs/helper-api-error": "1.7.11",
+ "@webassemblyjs/helper-code-frame": "1.7.11",
+ "@webassemblyjs/helper-fsm": "1.7.11",
+ "@xtuc/long": "4.2.1"
+ }
+ },
+ "@webassemblyjs/wast-printer": {
+ "version": "1.7.11",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.7.11.tgz",
+ "integrity": "sha512-m5vkAsuJ32QpkdkDOUPGSltrg8Cuk3KBx4YrmAGQwCZPRdUHXxG4phIOuuycLemHFr74sWL9Wthqss4fzdzSwg==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.7.11",
+ "@webassemblyjs/wast-parser": "1.7.11",
+ "@xtuc/long": "4.2.1"
+ }
+ },
+ "@xtuc/ieee754": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
+ "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
+ "dev": true
+ },
+ "@xtuc/long": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.1.tgz",
+ "integrity": "sha512-FZdkNBDqBRHKQ2MEbSC17xnPFOhZxeJ2YGSfr2BKf3sujG49Qe3bB+rGCwQfIaA7WHnGeGkSijX4FuBCdrzW/g==",
+ "dev": true
+ },
+ "@yarnpkg/lockfile": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz",
+ "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==",
+ "dev": true
+ },
+ "JSONStream": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz",
+ "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==",
+ "dev": true,
+ "requires": {
+ "jsonparse": "^1.2.0",
+ "through": ">=2.2.7 <3"
+ }
+ },
+ "abbrev": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+ "dev": true,
+ "optional": true
+ },
+ "accepts": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
+ "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
+ "dev": true,
+ "requires": {
+ "mime-types": "~2.1.18",
+ "negotiator": "0.6.1"
+ }
+ },
+ "acorn": {
+ "version": "6.4.2",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz",
+ "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==",
+ "dev": true
+ },
+ "acorn-dynamic-import": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz",
+ "integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==",
+ "dev": true
+ },
+ "adm-zip": {
+ "version": "0.4.13",
+ "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.13.tgz",
+ "integrity": "sha512-fERNJX8sOXfel6qCBCMPvZLzENBEhZTzKqg6vrOW5pvoEaQuJhRU4ndTAh6lHOxn1I6jnz2NHra56ZODM751uw==",
+ "dev": true
+ },
+ "after": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
+ "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=",
+ "dev": true
+ },
+ "agent-base": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz",
+ "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==",
+ "dev": true,
+ "requires": {
+ "es6-promisify": "^5.0.0"
+ }
+ },
+ "agentkeepalive": {
+ "version": "3.5.2",
+ "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz",
+ "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==",
+ "dev": true,
+ "requires": {
+ "humanize-ms": "^1.2.1"
+ }
+ },
+ "ajv": {
+ "version": "6.9.1",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz",
+ "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^2.0.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ajv-errors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz",
+ "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==",
+ "dev": true
+ },
+ "ajv-keywords": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.0.tgz",
+ "integrity": "sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw==",
+ "dev": true
+ },
+ "amdefine": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
+ "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
+ "dev": true
+ },
+ "angular-calendar": {
+ "version": "0.26.4",
+ "resolved": "https://registry.npmjs.org/angular-calendar/-/angular-calendar-0.26.4.tgz",
+ "integrity": "sha512-Bb1ZVE3N6moXCOOYJar1nF+ZqSdVX05HkgmnG/gRImjPsUxXnWnE2BjIjzzgQalE5LinbG2GPYNk8AhE1kah3Q==",
+ "requires": {
+ "angular-draggable-droppable": "^4.0.2",
+ "angular-resizable-element": "^3.2.2",
+ "calendar-utils": "^0.2.2",
+ "positioning": "^1.4.0",
+ "tslib": "^1.9.0"
+ }
+ },
+ "angular-draggable-droppable": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/angular-draggable-droppable/-/angular-draggable-droppable-4.1.0.tgz",
+ "integrity": "sha512-cWLYEHt20nxpNA8wqzrfCDWh0o09Q7jY2sXuEFHx3Pjl5tBBg8BOZ3KYe+RV22yuoQMrfLPizuG7U8iB406ZMg==",
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "angular-file-saver": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/angular-file-saver/-/angular-file-saver-1.1.3.tgz",
+ "integrity": "sha1-3K7AaVIU8iakyq/IwW0hqaYffRs=",
+ "requires": {
+ "blob-tmp": "^1.0.0",
+ "file-saver": "^1.3.3"
+ },
+ "dependencies": {
+ "file-saver": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-1.3.8.tgz",
+ "integrity": "sha512-spKHSBQIxxS81N/O21WmuXA2F6wppUCsutpzenOeZzOCCJ5gEfcbqJP983IrpLXzYmXnMUa6J03SubcNPdKrlg=="
+ }
+ }
+ },
+ "angular-resizable-element": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/angular-resizable-element/-/angular-resizable-element-3.2.4.tgz",
+ "integrity": "sha512-Qd/WRTaJgmDJoeWzmK0F52I3X0nLtFPirmsVC2ceic4EQ/3X2yYFCOWgCtH+PvMZct6Whoky3OQLqCpFXQsKHQ==",
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "ansi-align": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
+ "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==",
+ "dev": true,
+ "requires": {
+ "string-width": "^4.1.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true
+ },
+ "string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.1"
+ }
+ }
+ }
+ },
+ "ansi-colors": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz",
+ "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==",
+ "dev": true
+ },
+ "ansi-escapes": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
+ "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==",
+ "dev": true
+ },
+ "ansi-html": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz",
+ "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=",
+ "dev": true
+ },
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "anymatch": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
+ "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+ "dev": true,
+ "requires": {
+ "micromatch": "^3.1.4",
+ "normalize-path": "^2.1.1"
+ }
+ },
+ "app-builder-bin": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-3.4.3.tgz",
+ "integrity": "sha512-qMhayIwi3juerQEVJMQ76trObEbfQT0nhUdxZz9a26/3NLT3pE6awmQ8S1cEnrGugaaM5gYqR8OElcDezfmEsg==",
+ "dev": true
+ },
+ "app-builder-lib": {
+ "version": "21.2.0",
+ "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-21.2.0.tgz",
+ "integrity": "sha512-aOX/nv77/Bti6NymJDg7p9T067xD8m1ipIEJR7B4Mm1GsJWpMm9PZdXtCRiMNRjHtQS5KIljT0g17781y6qn5A==",
+ "dev": true,
+ "requires": {
+ "7zip-bin": "~5.0.3",
+ "@develar/schema-utils": "~2.1.0",
+ "async-exit-hook": "^2.0.1",
+ "bluebird-lst": "^1.0.9",
+ "builder-util": "21.2.0",
+ "builder-util-runtime": "8.3.0",
+ "chromium-pickle-js": "^0.2.0",
+ "debug": "^4.1.1",
+ "ejs": "^2.6.2",
+ "electron-publish": "21.2.0",
+ "fs-extra": "^8.1.0",
+ "hosted-git-info": "^2.7.1",
+ "is-ci": "^2.0.0",
+ "isbinaryfile": "^4.0.2",
+ "js-yaml": "^3.13.1",
+ "lazy-val": "^1.0.4",
+ "minimatch": "^3.0.4",
+ "normalize-package-data": "^2.5.0",
+ "read-config-file": "5.0.0",
+ "sanitize-filename": "^1.6.2",
+ "semver": "^6.3.0",
+ "temp-file": "^3.3.4"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "fs-extra": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.1.tgz",
+ "integrity": "sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw==",
+ "dev": true
+ },
+ "isbinaryfile": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.2.tgz",
+ "integrity": "sha512-C3FSxJdNrEr2F4z6uFtNzECDM5hXk+46fxaa+cwBe5/XrWSmzdG8DDgyjfX6/NRdBB21q2JXuRAzPCUs+fclnQ==",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.13.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+ "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "sanitize-filename": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.2.tgz",
+ "integrity": "sha512-cmTzND7RMxUB+f7gI+4+KAVHWEg0lfXvQJdko+FXDP5bNbGIdx4KMP5pX6lv5jfT9jSf6OBbjyxjFtZQwYA/ig==",
+ "dev": true,
+ "requires": {
+ "truncate-utf8-bytes": "^1.0.0"
+ }
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ }
+ }
+ },
+ "app-root-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.1.0.tgz",
+ "integrity": "sha1-mL9lmTJ+zqGZMJhm6BQDaP0uZGo=",
+ "dev": true
+ },
+ "append-transform": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz",
+ "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==",
+ "dev": true,
+ "requires": {
+ "default-require-extensions": "^2.0.0"
+ }
+ },
+ "aproba": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+ "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
+ "dev": true
+ },
+ "are-we-there-yet": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
+ "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "delegates": "^1.0.0",
+ "readable-stream": "^2.0.6"
+ }
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "arr-diff": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+ "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
+ "dev": true
+ },
+ "arr-flatten": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
+ "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
+ "dev": true
+ },
+ "arr-union": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
+ "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
+ "dev": true
+ },
+ "array-find-index": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
+ "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=",
+ "dev": true
+ },
+ "array-flatten": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz",
+ "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==",
+ "dev": true
+ },
+ "array-slice": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz",
+ "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=",
+ "dev": true
+ },
+ "array-union": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+ "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
+ "dev": true,
+ "requires": {
+ "array-uniq": "^1.0.1"
+ }
+ },
+ "array-uniq": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
+ "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=",
+ "dev": true
+ },
+ "array-unique": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
+ "dev": true
+ },
+ "arraybuffer.slice": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
+ "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==",
+ "dev": true
+ },
+ "arrify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
+ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
+ "dev": true
+ },
+ "asap": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+ "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=",
+ "dev": true,
+ "optional": true
+ },
+ "asar": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/asar/-/asar-2.0.1.tgz",
+ "integrity": "sha512-Vo9yTuUtyFahkVMFaI6uMuX6N7k5DWa6a/8+7ov0/f8Lq9TVR0tUjzSzxQSxT1Y+RJIZgnP7BVb6Uhi+9cjxqA==",
+ "dev": true,
+ "requires": {
+ "chromium-pickle-js": "^0.2.0",
+ "commander": "^2.20.0",
+ "cuint": "^0.2.2",
+ "glob": "^7.1.3",
+ "minimatch": "^3.0.4",
+ "mkdirp": "^0.5.1",
+ "tmp-promise": "^1.0.5"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.20.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
+ "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
+ "dev": true
+ }
+ }
+ },
+ "ascli": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/ascli/-/ascli-1.0.1.tgz",
+ "integrity": "sha1-vPpZdKYvGOgcq660lzKrSoj5Brw=",
+ "requires": {
+ "colour": "~0.7.1",
+ "optjs": "~3.2.2"
+ }
+ },
+ "asn1": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
+ "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": "~2.1.0"
+ }
+ },
+ "asn1.js": {
+ "version": "4.10.1",
+ "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz",
+ "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.0.0",
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "assert": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz",
+ "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=",
+ "dev": true,
+ "requires": {
+ "util": "0.10.3"
+ },
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
+ "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=",
+ "dev": true
+ },
+ "util": {
+ "version": "0.10.3",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
+ "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
+ "dev": true,
+ "requires": {
+ "inherits": "2.0.1"
+ }
+ }
+ }
+ },
+ "assert-plus": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+ "dev": true
+ },
+ "assign-symbols": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
+ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
+ "dev": true
+ },
+ "async": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
+ "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
+ },
+ "async-each": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz",
+ "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=",
+ "dev": true
+ },
+ "async-exit-hook": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz",
+ "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==",
+ "dev": true
+ },
+ "async-foreach": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz",
+ "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=",
+ "dev": true,
+ "optional": true
+ },
+ "async-limiter": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
+ "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==",
+ "dev": true
+ },
+ "asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
+ "dev": true
+ },
+ "atoa": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/atoa/-/atoa-1.0.0.tgz",
+ "integrity": "sha1-DMDpGkgOc4+SPrwQNnZHF3mzSkk="
+ },
+ "atob": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
+ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
+ "dev": true
+ },
+ "author-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/author-regex/-/author-regex-1.0.0.tgz",
+ "integrity": "sha1-0IiFvmubv5Q5/gh8dihyRfCoFFA=",
+ "dev": true
+ },
+ "autoprefixer": {
+ "version": "9.4.6",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.4.6.tgz",
+ "integrity": "sha512-Yp51mevbOEdxDUy5WjiKtpQaecqYq9OqZSL04rSoCiry7Tc5I9FEyo3bfxiTJc1DfHeKwSFCUYbBAiOQ2VGfiw==",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.4.1",
+ "caniuse-lite": "^1.0.30000929",
+ "normalize-range": "^0.1.2",
+ "num2fraction": "^1.2.2",
+ "postcss": "^7.0.13",
+ "postcss-value-parser": "^3.3.1"
+ }
+ },
+ "aws-sign2": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
+ "dev": true
+ },
+ "aws4": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
+ "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==",
+ "dev": true
+ },
+ "babel-code-frame": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
+ "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
+ "dev": true,
+ "requires": {
+ "chalk": "^1.1.3",
+ "esutils": "^2.0.2",
+ "js-tokens": "^3.0.2"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+ "dev": true
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+ "dev": true
+ }
+ }
+ },
+ "babel-generator": {
+ "version": "6.26.1",
+ "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz",
+ "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==",
+ "dev": true,
+ "requires": {
+ "babel-messages": "^6.23.0",
+ "babel-runtime": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "detect-indent": "^4.0.0",
+ "jsesc": "^1.3.0",
+ "lodash": "^4.17.4",
+ "source-map": "^0.5.7",
+ "trim-right": "^1.0.1"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ }
+ }
+ },
+ "babel-messages": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz",
+ "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "babel-runtime": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
+ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
+ "requires": {
+ "core-js": "^2.4.0",
+ "regenerator-runtime": "^0.11.0"
+ }
+ },
+ "babel-template": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz",
+ "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.26.0",
+ "babel-traverse": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "babylon": "^6.18.0",
+ "lodash": "^4.17.4"
+ }
+ },
+ "babel-traverse": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz",
+ "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=",
+ "dev": true,
+ "requires": {
+ "babel-code-frame": "^6.26.0",
+ "babel-messages": "^6.23.0",
+ "babel-runtime": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "babylon": "^6.18.0",
+ "debug": "^2.6.8",
+ "globals": "^9.18.0",
+ "invariant": "^2.2.2",
+ "lodash": "^4.17.4"
+ }
+ },
+ "babel-types": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz",
+ "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.26.0",
+ "esutils": "^2.0.2",
+ "lodash": "^4.17.4",
+ "to-fast-properties": "^1.0.3"
+ }
+ },
+ "babylon": {
+ "version": "6.18.0",
+ "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
+ "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==",
+ "dev": true
+ },
+ "backo2": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
+ "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=",
+ "dev": true
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
+ },
+ "base": {
+ "version": "0.11.2",
+ "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
+ "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+ "dev": true,
+ "requires": {
+ "cache-base": "^1.0.1",
+ "class-utils": "^0.3.5",
+ "component-emitter": "^1.2.1",
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.1",
+ "mixin-deep": "^1.2.0",
+ "pascalcase": "^0.1.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ }
+ }
+ },
+ "base64-arraybuffer": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
+ "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=",
+ "dev": true
+ },
+ "base64-js": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz",
+ "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==",
+ "dev": true
+ },
+ "base64id": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz",
+ "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=",
+ "dev": true
+ },
+ "batch": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
+ "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=",
+ "dev": true
+ },
+ "bcrypt-pbkdf": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+ "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
+ "dev": true,
+ "requires": {
+ "tweetnacl": "^0.14.3"
+ }
+ },
+ "better-assert": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
+ "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=",
+ "dev": true,
+ "requires": {
+ "callsite": "1.0.0"
+ }
+ },
+ "big.js": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
+ "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
+ "dev": true
+ },
+ "binary-extensions": {
+ "version": "1.13.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.0.tgz",
+ "integrity": "sha512-EgmjVLMn22z7eGGv3kcnHwSnJXmFHjISTY9E/S5lIcTD3Oxw05QTcBLNkJFzcb3cNueUdF/IN4U+d78V0zO8Hw==",
+ "dev": true
+ },
+ "blob": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
+ "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==",
+ "dev": true
+ },
+ "blob-tmp": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/blob-tmp/-/blob-tmp-1.0.0.tgz",
+ "integrity": "sha1-3oJJHiIv8TVMd6k+6OTqLIlUQnM="
+ },
+ "block-stream": {
+ "version": "0.0.9",
+ "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
+ "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "inherits": "~2.0.0"
+ }
+ },
+ "blocking-proxy": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-1.0.1.tgz",
+ "integrity": "sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.0"
+ }
+ },
+ "bluebird": {
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz",
+ "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw=="
+ },
+ "bluebird-lst": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/bluebird-lst/-/bluebird-lst-1.0.9.tgz",
+ "integrity": "sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw==",
+ "dev": true,
+ "requires": {
+ "bluebird": "^3.5.5"
+ },
+ "dependencies": {
+ "bluebird": {
+ "version": "3.5.5",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz",
+ "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==",
+ "dev": true
+ }
+ }
+ },
+ "bn.js": {
+ "version": "4.11.8",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
+ "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
+ "dev": true
+ },
+ "body-parser": {
+ "version": "1.18.3",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
+ "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=",
+ "dev": true,
+ "requires": {
+ "bytes": "3.0.0",
+ "content-type": "~1.0.4",
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "http-errors": "~1.6.3",
+ "iconv-lite": "0.4.23",
+ "on-finished": "~2.3.0",
+ "qs": "6.5.2",
+ "raw-body": "2.3.3",
+ "type-is": "~1.6.16"
+ }
+ },
+ "bonjour": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz",
+ "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=",
+ "dev": true,
+ "requires": {
+ "array-flatten": "^2.1.0",
+ "deep-equal": "^1.0.1",
+ "dns-equal": "^1.0.0",
+ "dns-txt": "^2.0.2",
+ "multicast-dns": "^6.0.1",
+ "multicast-dns-service-types": "^1.1.0"
+ }
+ },
+ "boxen": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/boxen/-/boxen-3.2.0.tgz",
+ "integrity": "sha512-cU4J/+NodM3IHdSL2yN8bqYqnmlBTidDR4RC7nJs61ZmtGz8VZzM3HLQX0zY5mrSmPtR3xWwsq2jOUQqFZN8+A==",
+ "dev": true,
+ "requires": {
+ "ansi-align": "^3.0.0",
+ "camelcase": "^5.3.1",
+ "chalk": "^2.4.2",
+ "cli-boxes": "^2.2.0",
+ "string-width": "^3.0.0",
+ "term-size": "^1.2.0",
+ "type-fest": "^0.3.0",
+ "widest-line": "^2.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ }
+ }
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "braces": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "dev": true,
+ "requires": {
+ "arr-flatten": "^1.1.0",
+ "array-unique": "^0.3.2",
+ "extend-shallow": "^2.0.1",
+ "fill-range": "^4.0.0",
+ "isobject": "^3.0.1",
+ "repeat-element": "^1.1.2",
+ "snapdragon": "^0.8.1",
+ "snapdragon-node": "^2.0.1",
+ "split-string": "^3.0.2",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "brorand": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
+ "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
+ "dev": true
+ },
+ "browserify-aes": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
+ "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
+ "dev": true,
+ "requires": {
+ "buffer-xor": "^1.0.3",
+ "cipher-base": "^1.0.0",
+ "create-hash": "^1.1.0",
+ "evp_bytestokey": "^1.0.3",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "browserify-cipher": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz",
+ "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==",
+ "dev": true,
+ "requires": {
+ "browserify-aes": "^1.0.4",
+ "browserify-des": "^1.0.0",
+ "evp_bytestokey": "^1.0.0"
+ }
+ },
+ "browserify-des": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz",
+ "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==",
+ "dev": true,
+ "requires": {
+ "cipher-base": "^1.0.1",
+ "des.js": "^1.0.0",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "browserify-rsa": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
+ "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.1.0",
+ "randombytes": "^2.0.1"
+ }
+ },
+ "browserify-sign": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz",
+ "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.1.1",
+ "browserify-rsa": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "create-hmac": "^1.1.2",
+ "elliptic": "^6.0.0",
+ "inherits": "^2.0.1",
+ "parse-asn1": "^5.0.0"
+ }
+ },
+ "browserify-zlib": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
+ "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
+ "dev": true,
+ "requires": {
+ "pako": "~1.0.5"
+ }
+ },
+ "browserslist": {
+ "version": "4.19.1",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz",
+ "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==",
+ "dev": true,
+ "requires": {
+ "caniuse-lite": "^1.0.30001286",
+ "electron-to-chromium": "^1.4.17",
+ "escalade": "^3.1.1",
+ "node-releases": "^2.0.1",
+ "picocolors": "^1.0.0"
+ },
+ "dependencies": {
+ "caniuse-lite": {
+ "version": "1.0.30001303",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001303.tgz",
+ "integrity": "sha512-/Mqc1oESndUNszJP0kx0UaQU9kEv9nNtJ7Kn8AdA0mNnH8eR1cj0kG+NbNuC1Wq/b21eA8prhKRA3bbkjONegQ==",
+ "dev": true
+ },
+ "electron-to-chromium": {
+ "version": "1.4.57",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.57.tgz",
+ "integrity": "sha512-FNC+P5K1n6pF+M0zIK+gFCoXcJhhzDViL3DRIGy2Fv5PohuSES1JHR7T+GlwxSxlzx4yYbsuzCZvHxcBSRCIOw==",
+ "dev": true
+ },
+ "node-releases": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz",
+ "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==",
+ "dev": true
+ }
+ }
+ },
+ "browserstack": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.5.2.tgz",
+ "integrity": "sha512-+6AFt9HzhKykcPF79W6yjEUJcdvZOV0lIXdkORXMJftGrDl0OKWqRF4GHqpDNkxiceDT/uB7Fb/aDwktvXX7dg==",
+ "dev": true,
+ "requires": {
+ "https-proxy-agent": "^2.2.1"
+ }
+ },
+ "buffer": {
+ "version": "4.9.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
+ "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
+ "dev": true,
+ "requires": {
+ "base64-js": "^1.0.2",
+ "ieee754": "^1.1.4",
+ "isarray": "^1.0.0"
+ }
+ },
+ "buffer-alloc": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
+ "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
+ "dev": true,
+ "requires": {
+ "buffer-alloc-unsafe": "^1.1.0",
+ "buffer-fill": "^1.0.0"
+ }
+ },
+ "buffer-alloc-unsafe": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz",
+ "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==",
+ "dev": true
+ },
+ "buffer-equal": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz",
+ "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=",
+ "dev": true
+ },
+ "buffer-fill": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
+ "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=",
+ "dev": true
+ },
+ "buffer-from": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
+ "dev": true
+ },
+ "buffer-indexof": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz",
+ "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==",
+ "dev": true
+ },
+ "buffer-xor": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
+ "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=",
+ "dev": true
+ },
+ "builder-util": {
+ "version": "21.2.0",
+ "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-21.2.0.tgz",
+ "integrity": "sha512-Nd6CUb6YgDY8EXAXEIegx+1kzKqyFQ5ZM5BoYkeunAlwz/zDJoH1UCyULjoS5wQe5czNClFQy07zz2bzYD0Z4A==",
+ "dev": true,
+ "requires": {
+ "7zip-bin": "~5.0.3",
+ "@types/debug": "^4.1.4",
+ "app-builder-bin": "3.4.3",
+ "bluebird-lst": "^1.0.9",
+ "builder-util-runtime": "8.3.0",
+ "chalk": "^2.4.2",
+ "debug": "^4.1.1",
+ "fs-extra": "^8.1.0",
+ "is-ci": "^2.0.0",
+ "js-yaml": "^3.13.1",
+ "source-map-support": "^0.5.13",
+ "stat-mode": "^0.3.0",
+ "temp-file": "^3.3.4"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "fs-extra": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.1.tgz",
+ "integrity": "sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw==",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.13.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+ "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "source-map-support": {
+ "version": "0.5.13",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
+ "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
+ "dev": true,
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ }
+ }
+ },
+ "builder-util-runtime": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-8.3.0.tgz",
+ "integrity": "sha512-CSOdsYqf4RXIHh1HANPbrZHlZ9JQJXSuDDloblZPcWQVN62inyYoTQuSmY3KrgefME2Sv3Kn2MxHvbGQHRf8Iw==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.1",
+ "sax": "^1.2.4"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "sax": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
+ "dev": true
+ }
+ }
+ },
+ "builtin-modules": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+ "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
+ "dev": true
+ },
+ "builtin-status-codes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
+ "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=",
+ "dev": true
+ },
+ "builtins": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz",
+ "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=",
+ "dev": true
+ },
+ "bytebuffer": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz",
+ "integrity": "sha1-WC7qSxqHO20CCkjVjfhfC7ps/d0=",
+ "requires": {
+ "long": "~3"
+ }
+ },
+ "bytes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+ "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=",
+ "dev": true
+ },
+ "cacache": {
+ "version": "10.0.4",
+ "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz",
+ "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==",
+ "dev": true,
+ "requires": {
+ "bluebird": "^3.5.1",
+ "chownr": "^1.0.1",
+ "glob": "^7.1.2",
+ "graceful-fs": "^4.1.11",
+ "lru-cache": "^4.1.1",
+ "mississippi": "^2.0.0",
+ "mkdirp": "^0.5.1",
+ "move-concurrently": "^1.0.1",
+ "promise-inflight": "^1.0.1",
+ "rimraf": "^2.6.2",
+ "ssri": "^5.2.4",
+ "unique-filename": "^1.1.0",
+ "y18n": "^4.0.0"
+ }
+ },
+ "cache-base": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
+ "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
+ "dev": true,
+ "requires": {
+ "collection-visit": "^1.0.0",
+ "component-emitter": "^1.2.1",
+ "get-value": "^2.0.6",
+ "has-value": "^1.0.0",
+ "isobject": "^3.0.1",
+ "set-value": "^2.0.0",
+ "to-object-path": "^0.3.0",
+ "union-value": "^1.0.0",
+ "unset-value": "^1.0.0"
+ }
+ },
+ "cacheable-request": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz",
+ "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==",
+ "dev": true,
+ "requires": {
+ "clone-response": "^1.0.2",
+ "get-stream": "^5.1.0",
+ "http-cache-semantics": "^4.0.0",
+ "keyv": "^3.0.0",
+ "lowercase-keys": "^2.0.0",
+ "normalize-url": "^4.1.0",
+ "responselike": "^1.0.2"
+ },
+ "dependencies": {
+ "get-stream": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz",
+ "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==",
+ "dev": true,
+ "requires": {
+ "pump": "^3.0.0"
+ }
+ },
+ "http-cache-semantics": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz",
+ "integrity": "sha512-TcIMG3qeVLgDr1TEd2XvHaTnMPwYQUQMIBLy+5pLSDKYFc7UIqj39w8EGzZkaxoLv/l2K8HaI0t5AVA+YYgUew==",
+ "dev": true
+ },
+ "lowercase-keys": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
+ "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
+ "dev": true
+ },
+ "pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ }
+ }
+ },
+ "calendar-utils": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/calendar-utils/-/calendar-utils-0.2.3.tgz",
+ "integrity": "sha512-wXxvJX/fdVZqrHeyxEWq5kbwALXrjifbY2L3fXvuS815cCwoWv+Uuiu0jkfqZNTtOSKaTXHdMkIhmLRqcC0E5g=="
+ },
+ "callsite": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
+ "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=",
+ "dev": true
+ },
+ "camelcase": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
+ "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8="
+ },
+ "camelcase-keys": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
+ "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
+ "dev": true,
+ "requires": {
+ "camelcase": "^2.0.0",
+ "map-obj": "^1.0.0"
+ }
+ },
+ "caniuse-lite": {
+ "version": "1.0.30000939",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000939.tgz",
+ "integrity": "sha512-oXB23ImDJOgQpGjRv1tCtzAvJr4/OvrHi5SO2vUgB0g0xpdZZoA/BxfImiWfdwoYdUTtQrPsXsvYU/dmCSM8gg==",
+ "dev": true
+ },
+ "canonical-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/canonical-path/-/canonical-path-1.0.0.tgz",
+ "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==",
+ "dev": true
+ },
+ "caseless": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
+ "dev": true
+ },
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "dependencies": {
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ }
+ }
+ },
+ "chardet": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
+ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
+ "dev": true
+ },
+ "chart.js": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.5.0.tgz",
+ "integrity": "sha1-/m51Gok3afVucr7lrZEgfhxZKVc=",
+ "requires": {
+ "chartjs-color": "^2.0.0",
+ "moment": "^2.10.6"
+ }
+ },
+ "chartjs-color": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.3.0.tgz",
+ "integrity": "sha512-hEvVheqczsoHD+fZ+tfPUE+1+RbV6b+eksp2LwAhwRTVXEjCSEavvk+Hg3H6SZfGlPh/UfmWKGIvZbtobOEm3g==",
+ "requires": {
+ "chartjs-color-string": "^0.6.0",
+ "color-convert": "^0.5.3"
+ },
+ "dependencies": {
+ "color-convert": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz",
+ "integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0="
+ }
+ }
+ },
+ "chartjs-color-string": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz",
+ "integrity": "sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==",
+ "requires": {
+ "color-name": "^1.0.0"
+ }
+ },
+ "chokidar": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz",
+ "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==",
+ "dev": true,
+ "requires": {
+ "anymatch": "^2.0.0",
+ "async-each": "^1.0.0",
+ "braces": "^2.3.0",
+ "fsevents": "^1.2.2",
+ "glob-parent": "^3.1.0",
+ "inherits": "^2.0.1",
+ "is-binary-path": "^1.0.0",
+ "is-glob": "^4.0.0",
+ "lodash.debounce": "^4.0.8",
+ "normalize-path": "^2.1.1",
+ "path-is-absolute": "^1.0.0",
+ "readdirp": "^2.0.0",
+ "upath": "^1.0.5"
+ }
+ },
+ "chownr": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz",
+ "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==",
+ "dev": true
+ },
+ "chrome-trace-event": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz",
+ "integrity": "sha512-xDbVgyfDTT2piup/h8dK/y4QZfJRSa73bw1WZ8b4XM1o7fsFubUVGYcE+1ANtOzJJELGpYoG2961z0Z6OAld9A==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "chromium-pickle-js": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz",
+ "integrity": "sha1-BKEGZywYsIWrd02YPfo+oTjyIgU=",
+ "dev": true
+ },
+ "ci-info": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
+ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
+ "dev": true
+ },
+ "cipher-base": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
+ "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "circular-dependency-plugin": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.0.2.tgz",
+ "integrity": "sha512-oC7/DVAyfcY3UWKm0sN/oVoDedQDQiw/vIiAnuTWTpE5s0zWf7l3WY417Xw/Fbi/QbAjctAkxgMiS9P0s3zkmA==",
+ "dev": true
+ },
+ "circular-json": {
+ "version": "0.5.9",
+ "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.9.tgz",
+ "integrity": "sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ==",
+ "dev": true
+ },
+ "class-utils": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
+ "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
+ "dev": true,
+ "requires": {
+ "arr-union": "^3.1.0",
+ "define-property": "^0.2.5",
+ "isobject": "^3.0.0",
+ "static-extend": "^0.1.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ }
+ }
+ },
+ "classlist.js": {
+ "version": "1.1.20150312",
+ "resolved": "https://registry.npmjs.org/classlist.js/-/classlist.js-1.1.20150312.tgz",
+ "integrity": "sha1-HXCEL3Ai8I2awIbOaeWyUPLFd4k="
+ },
+ "clean-css": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz",
+ "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==",
+ "dev": true,
+ "requires": {
+ "source-map": "~0.6.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "cli-boxes": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.0.tgz",
+ "integrity": "sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w==",
+ "dev": true
+ },
+ "cli-cursor": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
+ "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
+ "dev": true,
+ "requires": {
+ "restore-cursor": "^2.0.0"
+ }
+ },
+ "cli-width": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
+ "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
+ "dev": true
+ },
+ "cliui": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
+ "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
+ "requires": {
+ "string-width": "^1.0.1",
+ "strip-ansi": "^3.0.1",
+ "wrap-ansi": "^2.0.0"
+ }
+ },
+ "clone": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
+ "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18="
+ },
+ "clone-deep": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-2.0.2.tgz",
+ "integrity": "sha512-SZegPTKjCgpQH63E+eN6mVEEPdQBOUzjyJm5Pora4lrwWRFS8I0QAxV/KD6vV/i0WuijHZWQC1fMsPEdxfdVCQ==",
+ "dev": true,
+ "requires": {
+ "for-own": "^1.0.0",
+ "is-plain-object": "^2.0.4",
+ "kind-of": "^6.0.0",
+ "shallow-clone": "^1.0.0"
+ }
+ },
+ "clone-response": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz",
+ "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=",
+ "dev": true,
+ "requires": {
+ "mimic-response": "^1.0.0"
+ }
+ },
+ "co": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
+ "dev": true
+ },
+ "code-point-at": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
+ },
+ "codelyzer": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-4.5.0.tgz",
+ "integrity": "sha512-oO6vCkjqsVrEsmh58oNlnJkRXuA30hF8cdNAQV9DytEalDwyOFRvHMnlKFzmOStNerOmPGZU9GAHnBo4tGvtiQ==",
+ "dev": true,
+ "requires": {
+ "app-root-path": "^2.1.0",
+ "css-selector-tokenizer": "^0.7.0",
+ "cssauron": "^1.4.0",
+ "semver-dsl": "^1.0.1",
+ "source-map": "^0.5.7",
+ "sprintf-js": "^1.1.1"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ },
+ "sprintf-js": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
+ "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==",
+ "dev": true
+ }
+ }
+ },
+ "collection-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
+ "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
+ "dev": true,
+ "requires": {
+ "map-visit": "^1.0.0",
+ "object-visit": "^1.0.0"
+ }
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
+ },
+ "colors": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
+ "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=",
+ "dev": true
+ },
+ "colour": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/colour/-/colour-0.7.1.tgz",
+ "integrity": "sha1-nLFpkX7F0SwHNtPoaFdG3xyt93g="
+ },
+ "combine-lists": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/combine-lists/-/combine-lists-1.0.1.tgz",
+ "integrity": "sha1-RYwH4J4NkA/Ci3Cj/sLazR0st/Y=",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.5.0"
+ }
+ },
+ "combined-stream": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz",
+ "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==",
+ "dev": true,
+ "requires": {
+ "delayed-stream": "~1.0.0"
+ }
+ },
+ "commander": {
+ "version": "2.17.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
+ "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==",
+ "dev": true
+ },
+ "commondir": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
+ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
+ "dev": true
+ },
+ "compare-version": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz",
+ "integrity": "sha1-AWLsLZNR9d3VmpICy6k1NmpyUIA=",
+ "dev": true
+ },
+ "compare-versions": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.4.0.tgz",
+ "integrity": "sha512-tK69D7oNXXqUW3ZNo/z7NXTEz22TCF0pTE+YF9cxvaAM9XnkLo1fV621xCLrRR6aevJlKxExkss0vWqUCUpqdg==",
+ "dev": true
+ },
+ "component-bind": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
+ "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=",
+ "dev": true
+ },
+ "component-emitter": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+ "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=",
+ "dev": true
+ },
+ "component-inherit": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
+ "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=",
+ "dev": true
+ },
+ "compressible": {
+ "version": "2.0.16",
+ "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.16.tgz",
+ "integrity": "sha512-JQfEOdnI7dASwCuSPWIeVYwc/zMsu/+tRhoUvEfXz2gxOA2DNjmG5vhtFdBlhWPPGo+RdT9S3tgc/uH5qgDiiA==",
+ "dev": true,
+ "requires": {
+ "mime-db": ">= 1.38.0 < 2"
+ }
+ },
+ "compression": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.3.tgz",
+ "integrity": "sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==",
+ "dev": true,
+ "requires": {
+ "accepts": "~1.3.5",
+ "bytes": "3.0.0",
+ "compressible": "~2.0.14",
+ "debug": "2.6.9",
+ "on-headers": "~1.0.1",
+ "safe-buffer": "5.1.2",
+ "vary": "~1.1.2"
+ }
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+ },
+ "concat-stream": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+ "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+ "dev": true,
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.2.2",
+ "typedarray": "^0.0.6"
+ }
+ },
+ "configstore": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/configstore/-/configstore-4.0.0.tgz",
+ "integrity": "sha512-CmquAXFBocrzaSM8mtGPMM/HiWmyIpr4CcJl/rgY2uCObZ/S7cKU0silxslqJejl+t/T9HS8E0PUNQD81JGUEQ==",
+ "requires": {
+ "dot-prop": "^4.1.0",
+ "graceful-fs": "^4.1.2",
+ "make-dir": "^1.0.0",
+ "unique-string": "^1.0.0",
+ "write-file-atomic": "^2.0.0",
+ "xdg-basedir": "^3.0.0"
+ }
+ },
+ "connect": {
+ "version": "3.6.6",
+ "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz",
+ "integrity": "sha1-Ce/2xVr3I24TcTWnJXSFi2eG9SQ=",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "finalhandler": "1.1.0",
+ "parseurl": "~1.3.2",
+ "utils-merge": "1.0.1"
+ },
+ "dependencies": {
+ "finalhandler": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz",
+ "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "encodeurl": "~1.0.1",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.2",
+ "statuses": "~1.3.1",
+ "unpipe": "~1.0.0"
+ }
+ },
+ "statuses": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz",
+ "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=",
+ "dev": true
+ }
+ }
+ },
+ "connect-history-api-fallback": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz",
+ "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==",
+ "dev": true
+ },
+ "console-browserify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
+ "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=",
+ "dev": true,
+ "requires": {
+ "date-now": "^0.1.4"
+ }
+ },
+ "console-control-strings": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
+ "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
+ "dev": true,
+ "optional": true
+ },
+ "constants-browserify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
+ "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=",
+ "dev": true
+ },
+ "content-disposition": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
+ "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=",
+ "dev": true
+ },
+ "content-type": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
+ "dev": true
+ },
+ "contra": {
+ "version": "1.9.4",
+ "resolved": "https://registry.npmjs.org/contra/-/contra-1.9.4.tgz",
+ "integrity": "sha1-9TveQtfltZhcrk2ZqNYQUm3o8o0=",
+ "requires": {
+ "atoa": "1.0.0",
+ "ticky": "1.0.1"
+ }
+ },
+ "convert-source-map": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz",
+ "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.1"
+ }
+ },
+ "cookie": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
+ "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=",
+ "dev": true
+ },
+ "cookie-signature": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+ "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=",
+ "dev": true
+ },
+ "copy-concurrently": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz",
+ "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==",
+ "dev": true,
+ "requires": {
+ "aproba": "^1.1.1",
+ "fs-write-stream-atomic": "^1.0.8",
+ "iferr": "^0.1.5",
+ "mkdirp": "^0.5.1",
+ "rimraf": "^2.5.4",
+ "run-queue": "^1.0.0"
+ }
+ },
+ "copy-descriptor": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
+ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
+ "dev": true
+ },
+ "copy-webpack-plugin": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.3.0.tgz",
+ "integrity": "sha512-5o1/xyWm8OYDmLFKAWMuPU3A/jZ4Z6kZSZGh36KD2XmtxnRa8lQyLx7bCNQm08BPaR/oqUdtJOr9jWfnYINp9g==",
+ "dev": true,
+ "requires": {
+ "cacache": "^10.0.1",
+ "find-cache-dir": "^1.0.0",
+ "globby": "^7.1.1",
+ "is-glob": "^4.0.0",
+ "loader-utils": "^0.2.15",
+ "lodash": "^4.3.0",
+ "minimatch": "^3.0.4",
+ "p-limit": "^1.0.0",
+ "pify": "^3.0.0",
+ "serialize-javascript": "^1.4.0"
+ },
+ "dependencies": {
+ "big.js": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz",
+ "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==",
+ "dev": true
+ },
+ "json5": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
+ "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
+ "dev": true
+ },
+ "loader-utils": {
+ "version": "0.2.17",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz",
+ "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=",
+ "dev": true,
+ "requires": {
+ "big.js": "^3.1.3",
+ "emojis-list": "^2.0.0",
+ "json5": "^0.5.0",
+ "object-assign": "^4.0.1"
+ }
+ }
+ }
+ },
+ "core-js": {
+ "version": "2.6.5",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz",
+ "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A=="
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
+ "dev": true
+ },
+ "cosmiconfig": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-4.0.0.tgz",
+ "integrity": "sha512-6e5vDdrXZD+t5v0L8CrurPeybg4Fmf+FCSYxXKYVAqLUtyCSbuyqE059d0kDthTNRzKVjL7QMgNpEUlsoYH3iQ==",
+ "dev": true,
+ "requires": {
+ "is-directory": "^0.3.1",
+ "js-yaml": "^3.9.0",
+ "parse-json": "^4.0.0",
+ "require-from-string": "^2.0.1"
+ },
+ "dependencies": {
+ "parse-json": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+ "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
+ "dev": true,
+ "requires": {
+ "error-ex": "^1.3.1",
+ "json-parse-better-errors": "^1.0.1"
+ }
+ }
+ }
+ },
+ "create-ecdh": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz",
+ "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.1.0",
+ "elliptic": "^6.0.0"
+ }
+ },
+ "create-hash": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
+ "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
+ "dev": true,
+ "requires": {
+ "cipher-base": "^1.0.1",
+ "inherits": "^2.0.1",
+ "md5.js": "^1.3.4",
+ "ripemd160": "^2.0.1",
+ "sha.js": "^2.4.0"
+ }
+ },
+ "create-hmac": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
+ "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
+ "dev": true,
+ "requires": {
+ "cipher-base": "^1.0.3",
+ "create-hash": "^1.1.0",
+ "inherits": "^2.0.1",
+ "ripemd160": "^2.0.0",
+ "safe-buffer": "^5.0.1",
+ "sha.js": "^2.4.8"
+ }
+ },
+ "cross-spawn": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz",
+ "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "lru-cache": "^4.0.1",
+ "which": "^1.2.9"
+ }
+ },
+ "cross-zip": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/cross-zip/-/cross-zip-2.1.5.tgz",
+ "integrity": "sha1-xdGgaksaEqAzBk+UbrWubUvc8+E=",
+ "dev": true,
+ "requires": {
+ "rimraf": "^2.5.2"
+ }
+ },
+ "crossvent": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/crossvent/-/crossvent-1.5.4.tgz",
+ "integrity": "sha1-2ixPj0DJR4JRe/K+7BBEFIGUq5I=",
+ "requires": {
+ "custom-event": "1.0.0"
+ },
+ "dependencies": {
+ "custom-event": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.0.tgz",
+ "integrity": "sha1-LkYovhncSyFLXAJjDFlx6BFhgGI="
+ }
+ }
+ },
+ "crypto-browserify": {
+ "version": "3.12.0",
+ "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
+ "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
+ "dev": true,
+ "requires": {
+ "browserify-cipher": "^1.0.0",
+ "browserify-sign": "^4.0.0",
+ "create-ecdh": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "create-hmac": "^1.1.0",
+ "diffie-hellman": "^5.0.0",
+ "inherits": "^2.0.1",
+ "pbkdf2": "^3.0.3",
+ "public-encrypt": "^4.0.0",
+ "randombytes": "^2.0.0",
+ "randomfill": "^1.0.3"
+ }
+ },
+ "crypto-random-string": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz",
+ "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4="
+ },
+ "css-parse": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-1.7.0.tgz",
+ "integrity": "sha1-Mh9s9zeCpv91ERE5D8BeLGV9jJs=",
+ "dev": true
+ },
+ "css-selector-tokenizer": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz",
+ "integrity": "sha512-xYL0AMZJ4gFzJQsHUKa5jiWWi2vH77WVNg7JYRyewwj6oPh4yb/y6Y9ZCw9dsj/9UauMhtuxR+ogQd//EdEVNA==",
+ "dev": true,
+ "requires": {
+ "cssesc": "^0.1.0",
+ "fastparse": "^1.1.1",
+ "regexpu-core": "^1.0.0"
+ }
+ },
+ "cssauron": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz",
+ "integrity": "sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg=",
+ "dev": true,
+ "requires": {
+ "through": "X.X.X"
+ }
+ },
+ "cssesc": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz",
+ "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=",
+ "dev": true
+ },
+ "cuint": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/cuint/-/cuint-0.2.2.tgz",
+ "integrity": "sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs=",
+ "dev": true
+ },
+ "currently-unhandled": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
+ "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
+ "dev": true,
+ "requires": {
+ "array-find-index": "^1.0.1"
+ }
+ },
+ "custom-event": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz",
+ "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=",
+ "dev": true
+ },
+ "cyclist": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz",
+ "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=",
+ "dev": true
+ },
+ "dashdash": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+ "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "date-fns": {
+ "version": "1.28.5",
+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.28.5.tgz",
+ "integrity": "sha1-JXz8RdMi30XvVlhmWWfuhBzXP68="
+ },
+ "date-format": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/date-format/-/date-format-1.2.0.tgz",
+ "integrity": "sha1-YV6CjiM90aubua4JUODOzPpuytg=",
+ "dev": true
+ },
+ "date-now": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
+ "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=",
+ "dev": true
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
+ },
+ "decode-uri-component": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
+ "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
+ "dev": true
+ },
+ "decompress-response": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz",
+ "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=",
+ "dev": true,
+ "requires": {
+ "mimic-response": "^1.0.0"
+ }
+ },
+ "deep-equal": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz",
+ "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU="
+ },
+ "deep-extend": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+ "dev": true
+ },
+ "default-gateway": {
+ "version": "2.7.2",
+ "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-2.7.2.tgz",
+ "integrity": "sha512-lAc4i9QJR0YHSDFdzeBQKfZ1SRDG3hsJNEkrpcZa8QhBfidLAilT60BDEIVUUGqosFp425KOgB3uYqcnQrWafQ==",
+ "dev": true,
+ "requires": {
+ "execa": "^0.10.0",
+ "ip-regex": "^2.1.0"
+ }
+ },
+ "default-require-extensions": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz",
+ "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=",
+ "dev": true,
+ "requires": {
+ "strip-bom": "^3.0.0"
+ },
+ "dependencies": {
+ "strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+ "dev": true
+ }
+ }
+ },
+ "defer-to-connect": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.0.2.tgz",
+ "integrity": "sha512-k09hcQcTDY+cwgiwa6PYKLm3jlagNzQ+RSvhjzESOGOx+MNOuXkxTfEvPrO1IOQ81tArCFYQgi631clB70RpQw==",
+ "dev": true
+ },
+ "define-property": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
+ "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.2",
+ "isobject": "^3.0.1"
+ },
+ "dependencies": {
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ }
+ }
+ },
+ "del": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz",
+ "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=",
+ "dev": true,
+ "requires": {
+ "globby": "^6.1.0",
+ "is-path-cwd": "^1.0.0",
+ "is-path-in-cwd": "^1.0.0",
+ "p-map": "^1.1.1",
+ "pify": "^3.0.0",
+ "rimraf": "^2.2.8"
+ },
+ "dependencies": {
+ "globby": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
+ "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=",
+ "dev": true,
+ "requires": {
+ "array-union": "^1.0.1",
+ "glob": "^7.0.3",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ },
+ "dependencies": {
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ }
+ }
+ }
+ }
+ },
+ "delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+ "dev": true
+ },
+ "delegates": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+ "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
+ "dev": true,
+ "optional": true
+ },
+ "depd": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+ "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
+ "dev": true
+ },
+ "dependency-graph": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.7.2.tgz",
+ "integrity": "sha512-KqtH4/EZdtdfWX0p6MGP9jljvxSY6msy/pRUD4jgNwVpv3v1QmNLlsB3LDSSUg79BRVSn7jI1QPRtArGABovAQ==",
+ "dev": true
+ },
+ "des.js": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz",
+ "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "destroy": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+ "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=",
+ "dev": true
+ },
+ "detect-indent": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz",
+ "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=",
+ "dev": true,
+ "requires": {
+ "repeating": "^2.0.0"
+ }
+ },
+ "detect-node": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz",
+ "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==",
+ "dev": true
+ },
+ "di": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz",
+ "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=",
+ "dev": true
+ },
+ "diff": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
+ "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
+ "dev": true
+ },
+ "diffie-hellman": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
+ "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.1.0",
+ "miller-rabin": "^4.0.0",
+ "randombytes": "^2.0.0"
+ }
+ },
+ "dir-compare": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-1.8.0.tgz",
+ "integrity": "sha512-Ork/J37pKE6M+Fvl98OB+iAuZ5CG7d2d8DIMmiCDEZVAbEWn2lp+ghSbc1lgkgVX91p8jMQs2DeTMJvpMeU9+A==",
+ "dev": true,
+ "requires": {
+ "bluebird": "3.4.1",
+ "buffer-equal": "1.0.0",
+ "colors": "1.0.3",
+ "commander": "2.9.0",
+ "minimatch": "3.0.2"
+ },
+ "dependencies": {
+ "bluebird": {
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.1.tgz",
+ "integrity": "sha1-tzHd9I4t077awudeEhWhG8uR+gc=",
+ "dev": true
+ },
+ "colors": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz",
+ "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=",
+ "dev": true
+ },
+ "commander": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz",
+ "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=",
+ "dev": true,
+ "requires": {
+ "graceful-readlink": ">= 1.0.0"
+ }
+ },
+ "minimatch": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.2.tgz",
+ "integrity": "sha1-DzmKcwDqRB6cNIyD2Yq4ydv5xAo=",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.0.0"
+ }
+ }
+ }
+ },
+ "dir-glob": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz",
+ "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==",
+ "dev": true,
+ "requires": {
+ "path-type": "^3.0.0"
+ }
+ },
+ "dmg-builder": {
+ "version": "21.2.0",
+ "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-21.2.0.tgz",
+ "integrity": "sha512-9cJEclnGy7EyKFCoHDYDf54pub/t92CQapyiUxU0w9Bj2vUvfoDagP1PMiX4XD5rPp96141h9A+QN0OB4VgvQg==",
+ "dev": true,
+ "requires": {
+ "app-builder-lib": "~21.2.0",
+ "bluebird-lst": "^1.0.9",
+ "builder-util": "~21.2.0",
+ "fs-extra": "^8.1.0",
+ "iconv-lite": "^0.5.0",
+ "js-yaml": "^3.13.1",
+ "sanitize-filename": "^1.6.2"
+ },
+ "dependencies": {
+ "fs-extra": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.1.tgz",
+ "integrity": "sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw==",
+ "dev": true
+ },
+ "iconv-lite": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.0.tgz",
+ "integrity": "sha512-NnEhI9hIEKHOzJ4f697DMz9IQEXr/MMJ5w64vN2/4Ai+wRnvV7SBrL0KLoRlwaKVghOc7LQ5YkPLuX146b6Ydw==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "js-yaml": {
+ "version": "3.13.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+ "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "sanitize-filename": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.2.tgz",
+ "integrity": "sha512-cmTzND7RMxUB+f7gI+4+KAVHWEg0lfXvQJdko+FXDP5bNbGIdx4KMP5pX6lv5jfT9jSf6OBbjyxjFtZQwYA/ig==",
+ "dev": true,
+ "requires": {
+ "truncate-utf8-bytes": "^1.0.0"
+ }
+ }
+ }
+ },
+ "dns-equal": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
+ "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=",
+ "dev": true
+ },
+ "dns-packet": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz",
+ "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==",
+ "dev": true,
+ "requires": {
+ "ip": "^1.1.0",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "dns-txt": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz",
+ "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=",
+ "dev": true,
+ "requires": {
+ "buffer-indexof": "^1.0.0"
+ }
+ },
+ "dom-serialize": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz",
+ "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=",
+ "dev": true,
+ "requires": {
+ "custom-event": "~1.0.0",
+ "ent": "~2.2.0",
+ "extend": "^3.0.0",
+ "void-elements": "^2.0.0"
+ }
+ },
+ "domain-browser": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
+ "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==",
+ "dev": true
+ },
+ "dot-prop": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.1.tgz",
+ "integrity": "sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==",
+ "requires": {
+ "is-obj": "^1.0.0"
+ }
+ },
+ "dotenv": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.0.0.tgz",
+ "integrity": "sha512-30xVGqjLjiUOArT4+M5q9sYdvuR4riM6yK9wMcas9Vbp6zZa+ocC9dp6QoftuhTPhFAiLK/0C5Ni2nou/Bk8lg==",
+ "dev": true
+ },
+ "dotenv-expand": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz",
+ "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==",
+ "dev": true
+ },
+ "dragula": {
+ "version": "3.7.2",
+ "resolved": "https://registry.npmjs.org/dragula/-/dragula-3.7.2.tgz",
+ "integrity": "sha1-SjXJ05gf+sGpScKcpyhQWOhzk84=",
+ "requires": {
+ "contra": "1.9.4",
+ "crossvent": "1.5.4"
+ }
+ },
+ "duplexer3": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
+ "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=",
+ "dev": true
+ },
+ "duplexify": {
+ "version": "3.7.1",
+ "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
+ "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.0.0",
+ "inherits": "^2.0.1",
+ "readable-stream": "^2.0.0",
+ "stream-shift": "^1.0.0"
+ }
+ },
+ "earcut": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.3.tgz",
+ "integrity": "sha512-iRDI1QeCQIhMCZk48DRDMVgQSSBDmbzzNhnxIo+pwx3swkfjMh6vh0nWLq1NdvGHLKH6wIrAM3vQWeTj6qeoug=="
+ },
+ "ecc-jsbn": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+ "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
+ "dev": true,
+ "requires": {
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.1.0"
+ }
+ },
+ "ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
+ "dev": true
+ },
+ "ejs": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.2.tgz",
+ "integrity": "sha512-PcW2a0tyTuPHz3tWyYqtK6r1fZ3gp+3Sop8Ph+ZYN81Ob5rwmbHEzaqs10N3BEsaGTkh/ooniXK+WwszGlc2+Q==",
+ "dev": true
+ },
+ "electron": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/electron/-/electron-5.0.3.tgz",
+ "integrity": "sha512-kgnxPQoTNzu0lKC8ToAktt93mTimgqHZGFCVmTaFjs1G2Bk9VqKHljbQemH+REKAaw1aEgIRn2JCrTe0Dy7fVQ==",
+ "dev": true,
+ "requires": {
+ "@types/node": "^10.12.18",
+ "electron-download": "^4.1.0",
+ "extract-zip": "^1.0.3"
+ },
+ "dependencies": {
+ "@types/node": {
+ "version": "10.14.9",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.9.tgz",
+ "integrity": "sha512-NelG/dSahlXYtSoVPErrp06tYFrvzj8XLWmKA+X8x0W//4MqbUyZu++giUG/v0bjAT6/Qxa8IjodrfdACyb0Fg==",
+ "dev": true
+ }
+ }
+ },
+ "electron-builder": {
+ "version": "21.2.0",
+ "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-21.2.0.tgz",
+ "integrity": "sha512-x8EXrqFbAb2L3N22YlGar3dGh8vwptbB3ovo3OF6K7NTpcsmM2zEoJv7GhFyX73rNzSG2HaWpXwGAtOp2JWiEw==",
+ "dev": true,
+ "requires": {
+ "app-builder-lib": "21.2.0",
+ "bluebird-lst": "^1.0.9",
+ "builder-util": "21.2.0",
+ "builder-util-runtime": "8.3.0",
+ "chalk": "^2.4.2",
+ "dmg-builder": "21.2.0",
+ "fs-extra": "^8.1.0",
+ "is-ci": "^2.0.0",
+ "lazy-val": "^1.0.4",
+ "read-config-file": "5.0.0",
+ "sanitize-filename": "^1.6.2",
+ "update-notifier": "^3.0.1",
+ "yargs": "^13.3.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
+ },
+ "cliui": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+ "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+ "dev": true,
+ "requires": {
+ "string-width": "^3.1.0",
+ "strip-ansi": "^5.2.0",
+ "wrap-ansi": "^5.1.0"
+ }
+ },
+ "find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^3.0.0"
+ }
+ },
+ "fs-extra": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ },
+ "get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true
+ },
+ "graceful-fs": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.1.tgz",
+ "integrity": "sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw==",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
+ },
+ "locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "p-limit": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz",
+ "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==",
+ "dev": true,
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.0.0"
+ }
+ },
+ "p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true
+ },
+ "require-main-filename": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+ "dev": true
+ },
+ "sanitize-filename": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.2.tgz",
+ "integrity": "sha512-cmTzND7RMxUB+f7gI+4+KAVHWEg0lfXvQJdko+FXDP5bNbGIdx4KMP5pX6lv5jfT9jSf6OBbjyxjFtZQwYA/ig==",
+ "dev": true,
+ "requires": {
+ "truncate-utf8-bytes": "^1.0.0"
+ }
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ },
+ "which-module": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
+ "dev": true
+ },
+ "wrap-ansi": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
+ "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.0",
+ "string-width": "^3.0.0",
+ "strip-ansi": "^5.0.0"
+ }
+ },
+ "yargs": {
+ "version": "13.3.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz",
+ "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==",
+ "dev": true,
+ "requires": {
+ "cliui": "^5.0.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^3.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^13.1.1"
+ }
+ },
+ "yargs-parser": {
+ "version": "13.1.2",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
+ "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ }
+ }
+ },
+ "electron-download": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/electron-download/-/electron-download-4.1.1.tgz",
+ "integrity": "sha512-FjEWG9Jb/ppK/2zToP+U5dds114fM1ZOJqMAR4aXXL5CvyPE9fiqBK/9YcwC9poIFQTEJk/EM/zyRwziziRZrg==",
+ "dev": true,
+ "requires": {
+ "debug": "^3.0.0",
+ "env-paths": "^1.0.0",
+ "fs-extra": "^4.0.1",
+ "minimist": "^1.2.0",
+ "nugget": "^2.0.1",
+ "path-exists": "^3.0.0",
+ "rc": "^1.2.1",
+ "semver": "^5.4.1",
+ "sumchecker": "^2.0.2"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ }
+ }
+ },
+ "electron-notarize": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/electron-notarize/-/electron-notarize-0.1.1.tgz",
+ "integrity": "sha512-TpKfJcz4LXl5jiGvZTs5fbEx+wUFXV5u8voeG5WCHWfY/cdgdD8lDZIZRqLVOtR3VO+drgJ9aiSHIO9TYn/fKg==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.1",
+ "fs-extra": "^8.0.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "fs-extra": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.0.1.tgz",
+ "integrity": "sha512-W+XLrggcDzlle47X/XnS7FXrXu9sDo+Ze9zpndeBxdgv88FHLm1HtmkhEwavruS6koanBjp098rUpHs65EmG7A==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ }
+ }
+ },
+ "electron-osx-sign": {
+ "version": "0.4.11",
+ "resolved": "https://registry.npmjs.org/electron-osx-sign/-/electron-osx-sign-0.4.11.tgz",
+ "integrity": "sha512-VVd40nrnVqymvFrY9ZkOYgHJOvexHHYTR3di/SN+mjJ0OWhR1I8BRVj3U+Yamw6hnkZZNKZp52rqL5EFAAPFkQ==",
+ "dev": true,
+ "requires": {
+ "bluebird": "^3.5.0",
+ "compare-version": "^0.1.2",
+ "debug": "^2.6.8",
+ "isbinaryfile": "^3.0.2",
+ "minimist": "^1.2.0",
+ "plist": "^3.0.1"
+ }
+ },
+ "electron-packager": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/electron-packager/-/electron-packager-14.0.0.tgz",
+ "integrity": "sha512-7GmpTAvMx9OR1/AZfcEmreVszMM0d64nzDyzzQytrjejZqygRyXOlRrQlR/TkLfymx9vZ5BHyo3KXokImdhhvA==",
+ "dev": true,
+ "requires": {
+ "@electron/get": "^1.3.0",
+ "asar": "^2.0.1",
+ "cross-zip": "^2.1.5",
+ "debug": "^4.0.1",
+ "electron-notarize": "^0.1.1",
+ "electron-osx-sign": "^0.4.11",
+ "fs-extra": "^7.0.0",
+ "galactus": "^0.2.1",
+ "get-package-info": "^1.0.0",
+ "junk": "^3.1.0",
+ "parse-author": "^2.0.0",
+ "plist": "^3.0.0",
+ "rcedit": "^2.0.0",
+ "resolve": "^1.1.6",
+ "sanitize-filename": "^1.6.0",
+ "semver": "^6.0.0",
+ "yargs-parser": "^13.0.0"
+ },
+ "dependencies": {
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
+ },
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "fs-extra": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
+ "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "semver": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.1.2.tgz",
+ "integrity": "sha512-z4PqiCpomGtWj8633oeAdXm1Kn1W++3T8epkZYnwiVgIYIJ0QHszhInYSJTYxebByQH7KVCEAn8R9duzZW2PhQ==",
+ "dev": true
+ },
+ "yargs-parser": {
+ "version": "13.1.2",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
+ "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ }
+ }
+ },
+ "electron-publish": {
+ "version": "21.2.0",
+ "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-21.2.0.tgz",
+ "integrity": "sha512-mWavuoWJe87iaeKd0I24dNWIaR+0yRzshjNVqGyK019H766fsPWl3caQJnVKFaEyrZRP397v4JZVG0e7s16AxA==",
+ "dev": true,
+ "requires": {
+ "bluebird-lst": "^1.0.9",
+ "builder-util": "~21.2.0",
+ "builder-util-runtime": "8.3.0",
+ "chalk": "^2.4.2",
+ "fs-extra": "^8.1.0",
+ "lazy-val": "^1.0.4",
+ "mime": "^2.4.4"
+ },
+ "dependencies": {
+ "fs-extra": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.1.tgz",
+ "integrity": "sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw==",
+ "dev": true
+ },
+ "mime": {
+ "version": "2.4.4",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz",
+ "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==",
+ "dev": true
+ }
+ }
+ },
+ "electron-splashscreen": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/electron-splashscreen/-/electron-splashscreen-1.0.0.tgz",
+ "integrity": "sha512-1+Iif7GL1IakTnRT86ee+zcDyWlAu4ggSOK1qWw9h/mcK7pnAy9biXMaeFwrUcYC+eMZM5Zx/2909WzmvxdOjQ=="
+ },
+ "elliptic": {
+ "version": "6.5.4",
+ "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
+ "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.11.9",
+ "brorand": "^1.1.0",
+ "hash.js": "^1.0.0",
+ "hmac-drbg": "^1.0.1",
+ "inherits": "^2.0.4",
+ "minimalistic-assert": "^1.0.1",
+ "minimalistic-crypto-utils": "^1.0.1"
+ },
+ "dependencies": {
+ "bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
+ "dev": true
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ }
+ }
+ },
+ "emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "dev": true
+ },
+ "emojis-list": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
+ "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=",
+ "dev": true
+ },
+ "encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
+ "dev": true
+ },
+ "encoding": {
+ "version": "0.1.12",
+ "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
+ "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
+ "dev": true,
+ "requires": {
+ "iconv-lite": "~0.4.13"
+ }
+ },
+ "end-of-stream": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
+ "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
+ "dev": true,
+ "requires": {
+ "once": "^1.4.0"
+ }
+ },
+ "engine.io": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.1.tgz",
+ "integrity": "sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w==",
+ "dev": true,
+ "requires": {
+ "accepts": "~1.3.4",
+ "base64id": "1.0.0",
+ "cookie": "0.3.1",
+ "debug": "~3.1.0",
+ "engine.io-parser": "~2.1.0",
+ "ws": "~3.3.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ }
+ }
+ },
+ "engine.io-client": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz",
+ "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==",
+ "dev": true,
+ "requires": {
+ "component-emitter": "1.2.1",
+ "component-inherit": "0.0.3",
+ "debug": "~3.1.0",
+ "engine.io-parser": "~2.1.1",
+ "has-cors": "1.1.0",
+ "indexof": "0.0.1",
+ "parseqs": "0.0.5",
+ "parseuri": "0.0.5",
+ "ws": "~3.3.1",
+ "xmlhttprequest-ssl": "~1.5.4",
+ "yeast": "0.1.2"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ }
+ }
+ },
+ "engine.io-parser": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz",
+ "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==",
+ "dev": true,
+ "requires": {
+ "after": "0.8.2",
+ "arraybuffer.slice": "~0.0.7",
+ "base64-arraybuffer": "0.1.5",
+ "blob": "0.0.5",
+ "has-binary2": "~1.0.2"
+ }
+ },
+ "enhanced-resolve": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz",
+ "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "memory-fs": "^0.4.0",
+ "tapable": "^1.0.0"
+ }
+ },
+ "ent": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz",
+ "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=",
+ "dev": true
+ },
+ "env-paths": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-1.0.0.tgz",
+ "integrity": "sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA=",
+ "dev": true
+ },
+ "err-code": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz",
+ "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=",
+ "dev": true
+ },
+ "errno": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz",
+ "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==",
+ "dev": true,
+ "requires": {
+ "prr": "~1.0.1"
+ }
+ },
+ "error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
+ "requires": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "es6-promise": {
+ "version": "4.2.6",
+ "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.6.tgz",
+ "integrity": "sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q==",
+ "dev": true
+ },
+ "es6-promisify": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
+ "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
+ "dev": true,
+ "requires": {
+ "es6-promise": "^4.0.3"
+ }
+ },
+ "escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "dev": true
+ },
+ "escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=",
+ "dev": true
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
+ },
+ "eslint-scope": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz",
+ "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==",
+ "dev": true,
+ "requires": {
+ "esrecurse": "^4.1.0",
+ "estraverse": "^4.1.1"
+ }
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true
+ },
+ "esrecurse": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
+ "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^4.1.0"
+ }
+ },
+ "estraverse": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
+ "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=",
+ "dev": true
+ },
+ "esutils": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
+ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
+ "dev": true
+ },
+ "etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
+ "dev": true
+ },
+ "events": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz",
+ "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==",
+ "dev": true
+ },
+ "eventsource": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz",
+ "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==",
+ "dev": true,
+ "requires": {
+ "original": "^1.0.0"
+ }
+ },
+ "evp_bytestokey": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
+ "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
+ "dev": true,
+ "requires": {
+ "md5.js": "^1.3.4",
+ "safe-buffer": "^5.1.1"
+ }
+ },
+ "execa": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz",
+ "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^6.0.0",
+ "get-stream": "^3.0.0",
+ "is-stream": "^1.1.0",
+ "npm-run-path": "^2.0.0",
+ "p-finally": "^1.0.0",
+ "signal-exit": "^3.0.0",
+ "strip-eof": "^1.0.0"
+ },
+ "dependencies": {
+ "cross-spawn": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+ "dev": true,
+ "requires": {
+ "nice-try": "^1.0.4",
+ "path-key": "^2.0.1",
+ "semver": "^5.5.0",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ }
+ }
+ }
+ },
+ "exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+ "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
+ "dev": true
+ },
+ "expand-braces": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/expand-braces/-/expand-braces-0.1.2.tgz",
+ "integrity": "sha1-SIsdHSRRyz06axks/AMPRMWFX+o=",
+ "dev": true,
+ "requires": {
+ "array-slice": "^0.2.3",
+ "array-unique": "^0.2.1",
+ "braces": "^0.1.2"
+ },
+ "dependencies": {
+ "array-unique": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz",
+ "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=",
+ "dev": true
+ },
+ "braces": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-0.1.5.tgz",
+ "integrity": "sha1-wIVxEIUpHYt1/ddOqw+FlygHEeY=",
+ "dev": true,
+ "requires": {
+ "expand-range": "^0.1.0"
+ }
+ }
+ }
+ },
+ "expand-brackets": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
+ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
+ "dev": true,
+ "requires": {
+ "debug": "^2.3.3",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "posix-character-classes": "^0.1.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "expand-range": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-0.1.1.tgz",
+ "integrity": "sha1-TLjtoJk8pW+k9B/ELzy7TMrf8EQ=",
+ "dev": true,
+ "requires": {
+ "is-number": "^0.1.1",
+ "repeat-string": "^0.2.2"
+ },
+ "dependencies": {
+ "is-number": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-0.1.1.tgz",
+ "integrity": "sha1-aaevEWlj1HIG7JvZtIoUIW8eOAY=",
+ "dev": true
+ },
+ "repeat-string": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-0.2.2.tgz",
+ "integrity": "sha1-x6jTI2BoNiBZp+RlH8aITosftK4=",
+ "dev": true
+ }
+ }
+ },
+ "express": {
+ "version": "4.16.4",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz",
+ "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==",
+ "dev": true,
+ "requires": {
+ "accepts": "~1.3.5",
+ "array-flatten": "1.1.1",
+ "body-parser": "1.18.3",
+ "content-disposition": "0.5.2",
+ "content-type": "~1.0.4",
+ "cookie": "0.3.1",
+ "cookie-signature": "1.0.6",
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "1.1.1",
+ "fresh": "0.5.2",
+ "merge-descriptors": "1.0.1",
+ "methods": "~1.1.2",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.2",
+ "path-to-regexp": "0.1.7",
+ "proxy-addr": "~2.0.4",
+ "qs": "6.5.2",
+ "range-parser": "~1.2.0",
+ "safe-buffer": "5.1.2",
+ "send": "0.16.2",
+ "serve-static": "1.13.2",
+ "setprototypeof": "1.1.0",
+ "statuses": "~1.4.0",
+ "type-is": "~1.6.16",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ },
+ "dependencies": {
+ "array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=",
+ "dev": true
+ }
+ }
+ },
+ "extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
+ },
+ "extend-shallow": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+ "dev": true,
+ "requires": {
+ "assign-symbols": "^1.0.0",
+ "is-extendable": "^1.0.1"
+ },
+ "dependencies": {
+ "is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "requires": {
+ "is-plain-object": "^2.0.4"
+ }
+ }
+ }
+ },
+ "external-editor": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz",
+ "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==",
+ "dev": true,
+ "requires": {
+ "chardet": "^0.7.0",
+ "iconv-lite": "^0.4.24",
+ "tmp": "^0.0.33"
+ },
+ "dependencies": {
+ "iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ }
+ }
+ },
+ "extglob": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
+ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+ "dev": true,
+ "requires": {
+ "array-unique": "^0.3.2",
+ "define-property": "^1.0.0",
+ "expand-brackets": "^2.1.4",
+ "extend-shallow": "^2.0.1",
+ "fragment-cache": "^0.2.1",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ }
+ }
+ },
+ "extract-zip": {
+ "version": "1.6.7",
+ "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz",
+ "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=",
+ "dev": true,
+ "requires": {
+ "concat-stream": "1.6.2",
+ "debug": "2.6.9",
+ "mkdirp": "0.5.1",
+ "yauzl": "2.4.1"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+ "dev": true
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+ "dev": true,
+ "requires": {
+ "minimist": "0.0.8"
+ }
+ }
+ }
+ },
+ "extsprintf": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
+ "dev": true
+ },
+ "fast-deep-equal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
+ "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
+ "dev": true
+ },
+ "fast-diff": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz",
+ "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig=="
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
+ "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
+ "dev": true
+ },
+ "fast-xml-parser": {
+ "version": "3.12.16",
+ "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-3.12.16.tgz",
+ "integrity": "sha512-7ePrHTK4K9BLzY3+6ZOv2YEPOpdYJg3ohyMHxacG6kp1A0Y8KNyjrFfEHJuo8G4T7vT7cIlIXGWoHdIWu9U41A==",
+ "requires": {
+ "configstore": "^4.0.0",
+ "lodash": "^4.17.11",
+ "nimnjs": "^1.3.2"
+ }
+ },
+ "fastparse": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz",
+ "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==",
+ "dev": true
+ },
+ "faye-websocket": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz",
+ "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=",
+ "dev": true,
+ "requires": {
+ "websocket-driver": ">=0.5.1"
+ }
+ },
+ "fd-slicer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz",
+ "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=",
+ "dev": true,
+ "requires": {
+ "pend": "~1.2.0"
+ }
+ },
+ "figgy-pudding": {
+ "version": "3.5.1",
+ "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz",
+ "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==",
+ "dev": true
+ },
+ "figures": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
+ "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "^1.0.5"
+ }
+ },
+ "file-loader": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-3.0.1.tgz",
+ "integrity": "sha512-4sNIOXgtH/9WZq4NvlfU3Opn5ynUsqBwSLyM+I7UOwdGigTBYfVVQEwe/msZNX/j4pCJTIM14Fsw66Svo1oVrw==",
+ "dev": true,
+ "requires": {
+ "loader-utils": "^1.0.2",
+ "schema-utils": "^1.0.0"
+ }
+ },
+ "file-saver": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.2.tgz",
+ "integrity": "sha512-Wz3c3XQ5xroCxd1G8b7yL0Ehkf0TC9oYC6buPFkNnU9EnaPlifeAFCyCh+iewXTyFRcg0a6j3J7FmJsIhlhBdw=="
+ },
+ "fileset": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz",
+ "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=",
+ "dev": true,
+ "requires": {
+ "glob": "^7.0.3",
+ "minimatch": "^3.0.3"
+ }
+ },
+ "fill-range": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1",
+ "to-regex-range": "^2.1.0"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "finalhandler": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
+ "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.2",
+ "statuses": "~1.4.0",
+ "unpipe": "~1.0.0"
+ }
+ },
+ "find-cache-dir": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz",
+ "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=",
+ "dev": true,
+ "requires": {
+ "commondir": "^1.0.1",
+ "make-dir": "^1.0.0",
+ "pkg-dir": "^2.0.0"
+ }
+ },
+ "find-up": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+ "dev": true,
+ "requires": {
+ "locate-path": "^2.0.0"
+ }
+ },
+ "flatted": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.0.tgz",
+ "integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==",
+ "dev": true
+ },
+ "flora-colossus": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/flora-colossus/-/flora-colossus-1.0.1.tgz",
+ "integrity": "sha512-d+9na7t9FyH8gBJoNDSi28mE4NgQVGGvxQ4aHtFRetjyh5SXjuus+V5EZaxFmFdXVemSOrx0lsgEl/ZMjnOWJA==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.1",
+ "fs-extra": "^7.0.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "fs-extra": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
+ "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ }
+ }
+ },
+ "flush-write-stream": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz",
+ "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.3.6"
+ }
+ },
+ "follow-redirects": {
+ "version": "1.14.7",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz",
+ "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==",
+ "dev": true
+ },
+ "for-in": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+ "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
+ "dev": true
+ },
+ "for-own": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz",
+ "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=",
+ "dev": true,
+ "requires": {
+ "for-in": "^1.0.1"
+ }
+ },
+ "forever-agent": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
+ "dev": true
+ },
+ "form-data": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+ "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+ "dev": true,
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.6",
+ "mime-types": "^2.1.12"
+ }
+ },
+ "forwarded": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
+ "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=",
+ "dev": true
+ },
+ "fragment-cache": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
+ "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
+ "dev": true,
+ "requires": {
+ "map-cache": "^0.2.2"
+ }
+ },
+ "fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
+ "dev": true
+ },
+ "from2": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
+ "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "readable-stream": "^2.0.0"
+ }
+ },
+ "fs-access": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz",
+ "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=",
+ "dev": true,
+ "requires": {
+ "null-check": "^1.0.0"
+ }
+ },
+ "fs-extra": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz",
+ "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ },
+ "fs-write-stream-atomic": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
+ "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "iferr": "^0.1.5",
+ "imurmurhash": "^0.1.4",
+ "readable-stream": "1 || 2"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+ },
+ "fsevents": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz",
+ "integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "nan": "^2.9.2",
+ "node-pre-gyp": "^0.10.0"
+ },
+ "dependencies": {
+ "abbrev": {
+ "version": "1.1.1",
+ "resolved": false,
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+ "dev": true,
+ "optional": true
+ },
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": false,
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+ "dev": true,
+ "optional": true
+ },
+ "aproba": {
+ "version": "1.2.0",
+ "resolved": false,
+ "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
+ "dev": true,
+ "optional": true
+ },
+ "are-we-there-yet": {
+ "version": "1.1.5",
+ "resolved": false,
+ "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "delegates": "^1.0.0",
+ "readable-stream": "^2.0.6"
+ }
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": false,
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+ "dev": true,
+ "optional": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": false,
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "chownr": {
+ "version": "1.1.1",
+ "resolved": false,
+ "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==",
+ "dev": true,
+ "optional": true
+ },
+ "code-point-at": {
+ "version": "1.1.0",
+ "resolved": false,
+ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
+ "dev": true,
+ "optional": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": false,
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true,
+ "optional": true
+ },
+ "console-control-strings": {
+ "version": "1.1.0",
+ "resolved": false,
+ "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
+ "dev": true,
+ "optional": true
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "resolved": false,
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
+ "dev": true,
+ "optional": true
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": false,
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "deep-extend": {
+ "version": "0.6.0",
+ "resolved": false,
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+ "dev": true,
+ "optional": true
+ },
+ "delegates": {
+ "version": "1.0.0",
+ "resolved": false,
+ "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
+ "dev": true,
+ "optional": true
+ },
+ "detect-libc": {
+ "version": "1.0.3",
+ "resolved": false,
+ "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
+ "dev": true,
+ "optional": true
+ },
+ "fs-minipass": {
+ "version": "1.2.5",
+ "resolved": false,
+ "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "minipass": "^2.2.1"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": false,
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true,
+ "optional": true
+ },
+ "gauge": {
+ "version": "2.7.4",
+ "resolved": false,
+ "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "aproba": "^1.0.3",
+ "console-control-strings": "^1.0.0",
+ "has-unicode": "^2.0.0",
+ "object-assign": "^4.1.0",
+ "signal-exit": "^3.0.0",
+ "string-width": "^1.0.1",
+ "strip-ansi": "^3.0.1",
+ "wide-align": "^1.1.0"
+ }
+ },
+ "glob": {
+ "version": "7.1.3",
+ "resolved": false,
+ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "has-unicode": {
+ "version": "2.0.1",
+ "resolved": false,
+ "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
+ "dev": true,
+ "optional": true
+ },
+ "iconv-lite": {
+ "version": "0.4.24",
+ "resolved": false,
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "ignore-walk": {
+ "version": "3.0.1",
+ "resolved": false,
+ "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "minimatch": "^3.0.4"
+ }
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": false,
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": false,
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+ "dev": true,
+ "optional": true
+ },
+ "ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true,
+ "optional": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "1.0.0",
+ "resolved": false,
+ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": false,
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true,
+ "optional": true
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": false,
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minipass": {
+ "version": "2.3.5",
+ "resolved": false,
+ "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "safe-buffer": "^5.1.2",
+ "yallist": "^3.0.0"
+ }
+ },
+ "minizlib": {
+ "version": "1.2.1",
+ "resolved": false,
+ "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "minipass": "^2.2.1"
+ }
+ },
+ "mkdirp": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+ "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "minimist": "^1.2.5"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": false,
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true,
+ "optional": true
+ },
+ "needle": {
+ "version": "2.2.4",
+ "resolved": false,
+ "integrity": "sha512-HyoqEb4wr/rsoaIDfTH2aVL9nWtQqba2/HvMv+++m8u0dz808MaagKILxtfeSN7QU7nvbQ79zk3vYOJp9zsNEA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "debug": "^2.1.2",
+ "iconv-lite": "^0.4.4",
+ "sax": "^1.2.4"
+ }
+ },
+ "node-pre-gyp": {
+ "version": "0.10.3",
+ "resolved": false,
+ "integrity": "sha512-d1xFs+C/IPS8Id0qPTZ4bUT8wWryfR/OzzAFxweG+uLN85oPzyo2Iw6bVlLQ/JOdgNonXLCoRyqDzDWq4iw72A==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "detect-libc": "^1.0.2",
+ "mkdirp": "^0.5.1",
+ "needle": "^2.2.1",
+ "nopt": "^4.0.1",
+ "npm-packlist": "^1.1.6",
+ "npmlog": "^4.0.2",
+ "rc": "^1.2.7",
+ "rimraf": "^2.6.1",
+ "semver": "^5.3.0",
+ "tar": "^4"
+ }
+ },
+ "nopt": {
+ "version": "4.0.1",
+ "resolved": false,
+ "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "abbrev": "1",
+ "osenv": "^0.1.4"
+ }
+ },
+ "npm-bundled": {
+ "version": "1.0.5",
+ "resolved": false,
+ "integrity": "sha512-m/e6jgWu8/v5niCUKQi9qQl8QdeEduFA96xHDDzFGqly0OOjI7c+60KM/2sppfnUU9JJagf+zs+yGhqSOFj71g==",
+ "dev": true,
+ "optional": true
+ },
+ "npm-packlist": {
+ "version": "1.2.0",
+ "resolved": false,
+ "integrity": "sha512-7Mni4Z8Xkx0/oegoqlcao/JpPCPEMtUvsmB0q7mgvlMinykJLSRTYuFqoQLYgGY8biuxIeiHO+QNJKbCfljewQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "ignore-walk": "^3.0.1",
+ "npm-bundled": "^1.0.1"
+ }
+ },
+ "npmlog": {
+ "version": "4.1.2",
+ "resolved": false,
+ "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "are-we-there-yet": "~1.1.2",
+ "console-control-strings": "~1.1.0",
+ "gauge": "~2.7.3",
+ "set-blocking": "~2.0.0"
+ }
+ },
+ "number-is-nan": {
+ "version": "1.0.1",
+ "resolved": false,
+ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
+ "dev": true,
+ "optional": true
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": false,
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+ "dev": true,
+ "optional": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": false,
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "os-homedir": {
+ "version": "1.0.2",
+ "resolved": false,
+ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
+ "dev": true,
+ "optional": true
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": false,
+ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+ "dev": true,
+ "optional": true
+ },
+ "osenv": {
+ "version": "0.1.5",
+ "resolved": false,
+ "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "os-homedir": "^1.0.0",
+ "os-tmpdir": "^1.0.0"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": false,
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true,
+ "optional": true
+ },
+ "process-nextick-args": {
+ "version": "2.0.0",
+ "resolved": false,
+ "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
+ "dev": true,
+ "optional": true
+ },
+ "rc": {
+ "version": "1.2.8",
+ "resolved": false,
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": false,
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "rimraf": {
+ "version": "2.6.3",
+ "resolved": false,
+ "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": false,
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true,
+ "optional": true
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": false,
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true,
+ "optional": true
+ },
+ "sax": {
+ "version": "1.2.4",
+ "resolved": false,
+ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
+ "dev": true,
+ "optional": true
+ },
+ "semver": {
+ "version": "5.6.0",
+ "resolved": false,
+ "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
+ "dev": true,
+ "optional": true
+ },
+ "set-blocking": {
+ "version": "2.0.0",
+ "resolved": false,
+ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
+ "dev": true,
+ "optional": true
+ },
+ "signal-exit": {
+ "version": "3.0.2",
+ "resolved": false,
+ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
+ "dev": true,
+ "optional": true
+ },
+ "string-width": {
+ "version": "1.0.2",
+ "resolved": false,
+ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": false,
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": false,
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": false,
+ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
+ "dev": true,
+ "optional": true
+ },
+ "tar": {
+ "version": "4.4.8",
+ "resolved": false,
+ "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "chownr": "^1.1.1",
+ "fs-minipass": "^1.2.5",
+ "minipass": "^2.3.4",
+ "minizlib": "^1.1.1",
+ "mkdirp": "^0.5.0",
+ "safe-buffer": "^5.1.2",
+ "yallist": "^3.0.2"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": false,
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+ "dev": true,
+ "optional": true
+ },
+ "wide-align": {
+ "version": "1.1.3",
+ "resolved": false,
+ "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "string-width": "^1.0.2 || 2"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": false,
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true,
+ "optional": true
+ },
+ "yallist": {
+ "version": "3.0.3",
+ "resolved": false,
+ "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "fstream": {
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz",
+ "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "inherits": "~2.0.0",
+ "mkdirp": ">=0.5 0",
+ "rimraf": "2"
+ }
+ },
+ "galactus": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/galactus/-/galactus-0.2.1.tgz",
+ "integrity": "sha1-y+0tIKQMH1Z5o1kI4rlBVzPnjbk=",
+ "dev": true,
+ "requires": {
+ "debug": "^3.1.0",
+ "flora-colossus": "^1.0.0",
+ "fs-extra": "^4.0.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ }
+ }
+ },
+ "gauge": {
+ "version": "2.7.4",
+ "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
+ "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "aproba": "^1.0.3",
+ "console-control-strings": "^1.0.0",
+ "has-unicode": "^2.0.0",
+ "object-assign": "^4.1.0",
+ "signal-exit": "^3.0.0",
+ "string-width": "^1.0.1",
+ "strip-ansi": "^3.0.1",
+ "wide-align": "^1.1.0"
+ }
+ },
+ "gaze": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz",
+ "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "globule": "^1.0.0"
+ }
+ },
+ "genfun": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/genfun/-/genfun-5.0.0.tgz",
+ "integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA==",
+ "dev": true
+ },
+ "get-caller-file": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
+ "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
+ "dev": true
+ },
+ "get-package-info": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/get-package-info/-/get-package-info-1.0.0.tgz",
+ "integrity": "sha1-ZDJ5ZWPigRPNlHTbvQAFKYWkmZw=",
+ "dev": true,
+ "requires": {
+ "bluebird": "^3.1.1",
+ "debug": "^2.2.0",
+ "lodash.get": "^4.0.0",
+ "read-pkg-up": "^2.0.0"
+ },
+ "dependencies": {
+ "load-json-file": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
+ "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "parse-json": "^2.2.0",
+ "pify": "^2.0.0",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "path-type": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
+ "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
+ "dev": true,
+ "requires": {
+ "pify": "^2.0.0"
+ }
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ },
+ "read-pkg": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
+ "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
+ "dev": true,
+ "requires": {
+ "load-json-file": "^2.0.0",
+ "normalize-package-data": "^2.3.2",
+ "path-type": "^2.0.0"
+ }
+ },
+ "read-pkg-up": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
+ "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
+ "dev": true,
+ "requires": {
+ "find-up": "^2.0.0",
+ "read-pkg": "^2.0.0"
+ }
+ },
+ "strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+ "dev": true
+ }
+ }
+ },
+ "get-stdin": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
+ "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=",
+ "dev": true
+ },
+ "get-stream": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
+ "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
+ "dev": true
+ },
+ "get-value": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+ "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
+ "dev": true
+ },
+ "getpass": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+ "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "glob": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "glob-parent": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+ "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+ "dev": true,
+ "requires": {
+ "is-glob": "^3.1.0",
+ "path-dirname": "^1.0.0"
+ },
+ "dependencies": {
+ "is-glob": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.0"
+ }
+ }
+ }
+ },
+ "global-dirs": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz",
+ "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=",
+ "dev": true,
+ "requires": {
+ "ini": "^1.3.4"
+ }
+ },
+ "globals": {
+ "version": "9.18.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
+ "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==",
+ "dev": true
+ },
+ "globby": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz",
+ "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=",
+ "dev": true,
+ "requires": {
+ "array-union": "^1.0.1",
+ "dir-glob": "^2.0.0",
+ "glob": "^7.1.2",
+ "ignore": "^3.3.5",
+ "pify": "^3.0.0",
+ "slash": "^1.0.0"
+ }
+ },
+ "globule": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz",
+ "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "glob": "~7.1.1",
+ "lodash": "~4.17.10",
+ "minimatch": "~3.0.2"
+ }
+ },
+ "google-protobuf": {
+ "version": "3.8.0",
+ "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.8.0.tgz",
+ "integrity": "sha512-tx39PTc//HaIT7K/baUF/8JYLGDozEi1e4xwPP1qSx3InP78cNpbSJpxiDsDMwj77qNOndVBDXnn7oi9zKxZew=="
+ },
+ "got": {
+ "version": "9.6.0",
+ "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz",
+ "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==",
+ "dev": true,
+ "requires": {
+ "@sindresorhus/is": "^0.14.0",
+ "@szmarczak/http-timer": "^1.1.2",
+ "cacheable-request": "^6.0.0",
+ "decompress-response": "^3.3.0",
+ "duplexer3": "^0.1.4",
+ "get-stream": "^4.1.0",
+ "lowercase-keys": "^1.0.1",
+ "mimic-response": "^1.0.1",
+ "p-cancelable": "^1.0.0",
+ "to-readable-stream": "^1.0.0",
+ "url-parse-lax": "^3.0.0"
+ },
+ "dependencies": {
+ "get-stream": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
+ "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
+ "dev": true,
+ "requires": {
+ "pump": "^3.0.0"
+ }
+ },
+ "pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ }
+ }
+ },
+ "graceful-fs": {
+ "version": "4.1.15",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
+ "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA=="
+ },
+ "graceful-readlink": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
+ "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=",
+ "dev": true
+ },
+ "grpc": {
+ "version": "1.21.1",
+ "resolved": "https://registry.npmjs.org/grpc/-/grpc-1.21.1.tgz",
+ "integrity": "sha512-PFsZQazf62nP05a0xm23mlImMuw5oVlqF/0zakmsdqJgvbABe+d6VThY2PfhqJmWEL/FhQ6QNYsxS5EAM6++7g==",
+ "requires": {
+ "lodash.camelcase": "^4.3.0",
+ "lodash.clone": "^4.5.0",
+ "nan": "^2.13.2",
+ "node-pre-gyp": "^0.13.0",
+ "protobufjs": "^5.0.3"
+ },
+ "dependencies": {
+ "abbrev": {
+ "version": "1.1.1",
+ "bundled": true
+ },
+ "ansi-regex": {
+ "version": "2.1.1",
+ "bundled": true
+ },
+ "aproba": {
+ "version": "1.2.0",
+ "bundled": true
+ },
+ "are-we-there-yet": {
+ "version": "1.1.5",
+ "bundled": true,
+ "requires": {
+ "delegates": "^1.0.0",
+ "readable-stream": "^2.0.6"
+ }
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "bundled": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "bundled": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "chownr": {
+ "version": "1.1.1",
+ "bundled": true
+ },
+ "code-point-at": {
+ "version": "1.1.0",
+ "bundled": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "bundled": true
+ },
+ "console-control-strings": {
+ "version": "1.1.0",
+ "bundled": true
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "bundled": true
+ },
+ "deep-extend": {
+ "version": "0.6.0",
+ "bundled": true
+ },
+ "delegates": {
+ "version": "1.0.0",
+ "bundled": true
+ },
+ "detect-libc": {
+ "version": "1.0.3",
+ "bundled": true
+ },
+ "fs-minipass": {
+ "version": "1.2.5",
+ "bundled": true,
+ "requires": {
+ "minipass": "^2.2.1"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "bundled": true
+ },
+ "gauge": {
+ "version": "2.7.4",
+ "bundled": true,
+ "requires": {
+ "aproba": "^1.0.3",
+ "console-control-strings": "^1.0.0",
+ "has-unicode": "^2.0.0",
+ "object-assign": "^4.1.0",
+ "signal-exit": "^3.0.0",
+ "string-width": "^1.0.1",
+ "strip-ansi": "^3.0.1",
+ "wide-align": "^1.1.0"
+ }
+ },
+ "has-unicode": {
+ "version": "2.0.1",
+ "bundled": true
+ },
+ "iconv-lite": {
+ "version": "0.4.23",
+ "bundled": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "ignore-walk": {
+ "version": "3.0.1",
+ "bundled": true,
+ "requires": {
+ "minimatch": "^3.0.4"
+ }
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "bundled": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "bundled": true
+ },
+ "ini": {
+ "version": "1.3.5",
+ "bundled": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "1.0.0",
+ "bundled": true,
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "bundled": true
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "bundled": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "1.2.0",
+ "bundled": true
+ },
+ "minipass": {
+ "version": "2.3.5",
+ "bundled": true,
+ "requires": {
+ "safe-buffer": "^5.1.2",
+ "yallist": "^3.0.0"
+ }
+ },
+ "minizlib": {
+ "version": "1.2.1",
+ "bundled": true,
+ "requires": {
+ "minipass": "^2.2.1"
+ }
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "bundled": true,
+ "requires": {
+ "minimist": "0.0.8"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "0.0.8",
+ "bundled": true
+ }
+ }
+ },
+ "nan": {
+ "version": "2.14.0",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
+ "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg=="
+ },
+ "needle": {
+ "version": "2.3.1",
+ "bundled": true,
+ "requires": {
+ "debug": "^4.1.0",
+ "iconv-lite": "^0.4.4",
+ "sax": "^1.2.4"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "bundled": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.1",
+ "bundled": true
+ }
+ }
+ },
+ "node-pre-gyp": {
+ "version": "0.13.0",
+ "bundled": true,
+ "requires": {
+ "detect-libc": "^1.0.2",
+ "mkdirp": "^0.5.1",
+ "needle": "^2.2.1",
+ "nopt": "^4.0.1",
+ "npm-packlist": "^1.1.6",
+ "npmlog": "^4.0.2",
+ "rc": "^1.2.7",
+ "rimraf": "^2.6.1",
+ "semver": "^5.3.0",
+ "tar": "^4"
+ }
+ },
+ "nopt": {
+ "version": "4.0.1",
+ "bundled": true,
+ "requires": {
+ "abbrev": "1",
+ "osenv": "^0.1.4"
+ }
+ },
+ "npm-bundled": {
+ "version": "1.0.6",
+ "bundled": true
+ },
+ "npm-packlist": {
+ "version": "1.4.1",
+ "bundled": true,
+ "requires": {
+ "ignore-walk": "^3.0.1",
+ "npm-bundled": "^1.0.1"
+ }
+ },
+ "npmlog": {
+ "version": "4.1.2",
+ "bundled": true,
+ "requires": {
+ "are-we-there-yet": "~1.1.2",
+ "console-control-strings": "~1.1.0",
+ "gauge": "~2.7.3",
+ "set-blocking": "~2.0.0"
+ }
+ },
+ "number-is-nan": {
+ "version": "1.0.1",
+ "bundled": true
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "bundled": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "bundled": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "os-homedir": {
+ "version": "1.0.2",
+ "bundled": true
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "bundled": true
+ },
+ "osenv": {
+ "version": "0.1.5",
+ "bundled": true,
+ "requires": {
+ "os-homedir": "^1.0.0",
+ "os-tmpdir": "^1.0.0"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "bundled": true
+ },
+ "process-nextick-args": {
+ "version": "2.0.0",
+ "bundled": true
+ },
+ "rc": {
+ "version": "1.2.8",
+ "bundled": true,
+ "requires": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "bundled": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "rimraf": {
+ "version": "2.6.3",
+ "bundled": true,
+ "requires": {
+ "glob": "^7.1.3"
+ },
+ "dependencies": {
+ "glob": {
+ "version": "7.1.4",
+ "bundled": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ }
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "bundled": true
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "bundled": true
+ },
+ "sax": {
+ "version": "1.2.4",
+ "bundled": true
+ },
+ "semver": {
+ "version": "5.7.0",
+ "bundled": true
+ },
+ "set-blocking": {
+ "version": "2.0.0",
+ "bundled": true
+ },
+ "signal-exit": {
+ "version": "3.0.2",
+ "bundled": true
+ },
+ "string-width": {
+ "version": "1.0.2",
+ "bundled": true,
+ "requires": {
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "bundled": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "bundled": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "strip-json-comments": {
+ "version": "2.0.1",
+ "bundled": true
+ },
+ "tar": {
+ "version": "4.4.8",
+ "bundled": true,
+ "requires": {
+ "chownr": "^1.1.1",
+ "fs-minipass": "^1.2.5",
+ "minipass": "^2.3.4",
+ "minizlib": "^1.1.1",
+ "mkdirp": "^0.5.0",
+ "safe-buffer": "^5.1.2",
+ "yallist": "^3.0.2"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "bundled": true
+ },
+ "wide-align": {
+ "version": "1.1.3",
+ "bundled": true,
+ "requires": {
+ "string-width": "^1.0.2 || 2"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "bundled": true
+ },
+ "yallist": {
+ "version": "3.0.3",
+ "bundled": true
+ }
+ }
+ },
+ "hammerjs": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz",
+ "integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE="
+ },
+ "handle-thing": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz",
+ "integrity": "sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==",
+ "dev": true
+ },
+ "har-schema": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+ "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
+ "dev": true
+ },
+ "har-validator": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
+ "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.5.5",
+ "har-schema": "^2.0.0"
+ }
+ },
+ "has-ansi": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "has-binary2": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
+ "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
+ "dev": true,
+ "requires": {
+ "isarray": "2.0.1"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
+ "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=",
+ "dev": true
+ }
+ }
+ },
+ "has-cors": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
+ "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
+ "has-unicode": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
+ "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
+ "dev": true,
+ "optional": true
+ },
+ "has-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
+ "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
+ "dev": true,
+ "requires": {
+ "get-value": "^2.0.6",
+ "has-values": "^1.0.0",
+ "isobject": "^3.0.0"
+ }
+ },
+ "has-values": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
+ "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
+ "dev": true,
+ "requires": {
+ "is-number": "^3.0.0",
+ "kind-of": "^4.0.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
+ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "has-yarn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz",
+ "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==",
+ "dev": true
+ },
+ "hash-base": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz",
+ "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "hash.js": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
+ "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "minimalistic-assert": "^1.0.1"
+ }
+ },
+ "hmac-drbg": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
+ "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
+ "dev": true,
+ "requires": {
+ "hash.js": "^1.0.3",
+ "minimalistic-assert": "^1.0.0",
+ "minimalistic-crypto-utils": "^1.0.1"
+ }
+ },
+ "hopscotch": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/hopscotch/-/hopscotch-0.3.1.tgz",
+ "integrity": "sha1-BmOlqbQtQwdmapQEwCxvHqwKZYQ="
+ },
+ "hosted-git-info": {
+ "version": "2.8.9",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
+ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
+ "dev": true
+ },
+ "hpack.js": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
+ "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "obuf": "^1.0.0",
+ "readable-stream": "^2.0.1",
+ "wbuf": "^1.1.0"
+ }
+ },
+ "html-entities": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz",
+ "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=",
+ "dev": true
+ },
+ "html-escaper": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
+ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
+ "dev": true
+ },
+ "http-cache-semantics": {
+ "version": "3.8.1",
+ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz",
+ "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==",
+ "dev": true
+ },
+ "http-deceiver": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz",
+ "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=",
+ "dev": true
+ },
+ "http-errors": {
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+ "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
+ "dev": true,
+ "requires": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.3",
+ "setprototypeof": "1.1.0",
+ "statuses": ">= 1.4.0 < 2"
+ }
+ },
+ "http-parser-js": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.0.tgz",
+ "integrity": "sha512-cZdEF7r4gfRIq7ezX9J0T+kQmJNOub71dWbgAXVHDct80TKP4MCETtZQ31xyv38UwgzkWPYF/Xc0ge55dW9Z9w==",
+ "dev": true
+ },
+ "http-proxy": {
+ "version": "1.18.1",
+ "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
+ "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
+ "dev": true,
+ "requires": {
+ "eventemitter3": "^4.0.0",
+ "follow-redirects": "^1.0.0",
+ "requires-port": "^1.0.0"
+ },
+ "dependencies": {
+ "eventemitter3": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
+ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
+ "dev": true
+ }
+ }
+ },
+ "http-proxy-agent": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz",
+ "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==",
+ "dev": true,
+ "requires": {
+ "agent-base": "4",
+ "debug": "3.1.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ }
+ }
+ },
+ "http-proxy-middleware": {
+ "version": "0.18.0",
+ "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz",
+ "integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==",
+ "dev": true,
+ "requires": {
+ "http-proxy": "^1.16.2",
+ "is-glob": "^4.0.0",
+ "lodash": "^4.17.5",
+ "micromatch": "^3.1.9"
+ }
+ },
+ "http-signature": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+ "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "jsprim": "^1.2.2",
+ "sshpk": "^1.7.0"
+ }
+ },
+ "https-browserify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
+ "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
+ "dev": true
+ },
+ "https-proxy-agent": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz",
+ "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
+ "dev": true,
+ "requires": {
+ "agent-base": "^4.3.0",
+ "debug": "^3.1.0"
+ },
+ "dependencies": {
+ "agent-base": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
+ "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
+ "dev": true,
+ "requires": {
+ "es6-promisify": "^5.0.0"
+ }
+ },
+ "debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true
+ }
+ }
+ },
+ "humanize-ms": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
+ "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=",
+ "dev": true,
+ "requires": {
+ "ms": "^2.0.0"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.4.23",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
+ "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "ieee754": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz",
+ "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==",
+ "dev": true
+ },
+ "iferr": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
+ "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=",
+ "dev": true
+ },
+ "ignore": {
+ "version": "3.3.10",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz",
+ "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==",
+ "dev": true
+ },
+ "ignore-walk": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz",
+ "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==",
+ "dev": true,
+ "requires": {
+ "minimatch": "^3.0.4"
+ }
+ },
+ "image-size": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
+ "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=",
+ "dev": true,
+ "optional": true
+ },
+ "immediate": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+ "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=",
+ "dev": true
+ },
+ "immutable": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz",
+ "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==",
+ "dev": true
+ },
+ "import-cwd": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz",
+ "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=",
+ "dev": true,
+ "requires": {
+ "import-from": "^2.1.0"
+ }
+ },
+ "import-from": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz",
+ "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=",
+ "dev": true,
+ "requires": {
+ "resolve-from": "^3.0.0"
+ }
+ },
+ "import-lazy": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz",
+ "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=",
+ "dev": true
+ },
+ "import-local": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz",
+ "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==",
+ "dev": true,
+ "requires": {
+ "pkg-dir": "^3.0.0",
+ "resolve-cwd": "^2.0.0"
+ },
+ "dependencies": {
+ "find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^3.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "p-limit": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz",
+ "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==",
+ "dev": true,
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.0.0"
+ }
+ },
+ "p-try": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz",
+ "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==",
+ "dev": true
+ },
+ "pkg-dir": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz",
+ "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
+ "dev": true,
+ "requires": {
+ "find-up": "^3.0.0"
+ }
+ }
+ }
+ },
+ "imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
+ },
+ "in-publish": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz",
+ "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=",
+ "dev": true,
+ "optional": true
+ },
+ "indent-string": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
+ "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=",
+ "dev": true,
+ "requires": {
+ "repeating": "^2.0.0"
+ }
+ },
+ "indexof": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
+ "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=",
+ "dev": true
+ },
+ "infer-owner": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
+ "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==",
+ "dev": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+ },
+ "ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true
+ },
+ "inquirer": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.1.tgz",
+ "integrity": "sha512-088kl3DRT2dLU5riVMKKr1DlImd6X7smDhpXUCkJDCKvTEJeRiXh0G132HG9u5a+6Ylw9plFRY7RuTnwohYSpg==",
+ "dev": true,
+ "requires": {
+ "ansi-escapes": "^3.0.0",
+ "chalk": "^2.0.0",
+ "cli-cursor": "^2.1.0",
+ "cli-width": "^2.0.0",
+ "external-editor": "^3.0.0",
+ "figures": "^2.0.0",
+ "lodash": "^4.17.10",
+ "mute-stream": "0.0.7",
+ "run-async": "^2.2.0",
+ "rxjs": "^6.1.0",
+ "string-width": "^2.1.0",
+ "strip-ansi": "^5.0.0",
+ "through": "^2.3.6"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
+ },
+ "string-width": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "dev": true,
+ "requires": {
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^4.0.0"
+ },
+ "dependencies": {
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ }
+ }
+ },
+ "strip-ansi": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.0.0.tgz",
+ "integrity": "sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz",
+ "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==",
+ "dev": true
+ }
+ }
+ }
+ }
+ },
+ "internal-ip": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-3.0.1.tgz",
+ "integrity": "sha512-NXXgESC2nNVtU+pqmC9e6R8B1GpKxzsAQhffvh5AL79qKnodd+L7tnEQmTiUAVngqLalPbSqRA7XGIEL5nCd0Q==",
+ "dev": true,
+ "requires": {
+ "default-gateway": "^2.6.0",
+ "ipaddr.js": "^1.5.2"
+ }
+ },
+ "interpret": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz",
+ "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==",
+ "dev": true
+ },
+ "invariant": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+ "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+ "dev": true,
+ "requires": {
+ "loose-envify": "^1.0.0"
+ }
+ },
+ "invert-kv": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
+ "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY="
+ },
+ "ip": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
+ "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=",
+ "dev": true
+ },
+ "ip-regex": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
+ "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=",
+ "dev": true
+ },
+ "ipaddr.js": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz",
+ "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=",
+ "dev": true
+ },
+ "is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+ "dev": true
+ },
+ "is-binary-path": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
+ "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
+ "dev": true,
+ "requires": {
+ "binary-extensions": "^1.0.0"
+ }
+ },
+ "is-buffer": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+ "dev": true
+ },
+ "is-ci": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
+ "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
+ "dev": true,
+ "requires": {
+ "ci-info": "^2.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true
+ }
+ }
+ },
+ "is-directory": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz",
+ "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=",
+ "dev": true
+ },
+ "is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+ "dev": true
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true
+ },
+ "is-finite": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz",
+ "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=",
+ "dev": true,
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "is-fullwidth-code-point": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "is-glob": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz",
+ "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-installed-globally": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz",
+ "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=",
+ "dev": true,
+ "requires": {
+ "global-dirs": "^0.1.0",
+ "is-path-inside": "^1.0.0"
+ }
+ },
+ "is-npm": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-3.0.0.tgz",
+ "integrity": "sha512-wsigDr1Kkschp2opC4G3yA6r9EgVA6NjRpWzIi9axXqeIaAATPRJc4uLujXe3Nd9uO8KoDyA4MD6aZSeXTADhA==",
+ "dev": true
+ },
+ "is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-obj": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
+ "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8="
+ },
+ "is-path-cwd": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz",
+ "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=",
+ "dev": true
+ },
+ "is-path-in-cwd": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz",
+ "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==",
+ "dev": true,
+ "requires": {
+ "is-path-inside": "^1.0.0"
+ }
+ },
+ "is-path-inside": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz",
+ "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=",
+ "dev": true,
+ "requires": {
+ "path-is-inside": "^1.0.1"
+ }
+ },
+ "is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ },
+ "is-promise": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
+ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
+ "dev": true
+ },
+ "is-stream": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
+ "dev": true
+ },
+ "is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
+ "dev": true
+ },
+ "is-utf8": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
+ "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
+ "dev": true
+ },
+ "is-windows": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
+ "dev": true
+ },
+ "is-wsl": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
+ "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=",
+ "dev": true
+ },
+ "is-yarn-global": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz",
+ "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==",
+ "dev": true
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "isbinaryfile": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz",
+ "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==",
+ "dev": true,
+ "requires": {
+ "buffer-alloc": "^1.2.0"
+ }
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+ "dev": true
+ },
+ "isstream": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
+ "dev": true
+ },
+ "istanbul-api": {
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-2.1.7.tgz",
+ "integrity": "sha512-LYTOa2UrYFyJ/aSczZi/6lBykVMjCCvUmT64gOe+jPZFy4w6FYfPGqFT2IiQ2BxVHHDOvCD7qrIXb0EOh4uGWw==",
+ "dev": true,
+ "requires": {
+ "async": "^2.6.2",
+ "compare-versions": "^3.4.0",
+ "fileset": "^2.0.3",
+ "istanbul-lib-coverage": "^2.0.5",
+ "istanbul-lib-hook": "^2.0.7",
+ "istanbul-lib-instrument": "^3.3.0",
+ "istanbul-lib-report": "^2.0.8",
+ "istanbul-lib-source-maps": "^3.0.6",
+ "istanbul-reports": "^2.2.5",
+ "js-yaml": "^3.13.1",
+ "make-dir": "^2.1.0",
+ "minimatch": "^3.0.4",
+ "once": "^1.4.0"
+ },
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz",
+ "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.16.7"
+ }
+ },
+ "@babel/generator": {
+ "version": "7.16.8",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz",
+ "integrity": "sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.16.8",
+ "jsesc": "^2.5.1",
+ "source-map": "^0.5.0"
+ }
+ },
+ "@babel/helper-function-name": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz",
+ "integrity": "sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-get-function-arity": "^7.16.7",
+ "@babel/template": "^7.16.7",
+ "@babel/types": "^7.16.7"
+ }
+ },
+ "@babel/helper-get-function-arity": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz",
+ "integrity": "sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.16.7"
+ }
+ },
+ "@babel/helper-split-export-declaration": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz",
+ "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.16.7"
+ }
+ },
+ "@babel/highlight": {
+ "version": "7.16.10",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz",
+ "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.16.7",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.16.12",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.12.tgz",
+ "integrity": "sha512-VfaV15po8RiZssrkPweyvbGVSe4x2y+aciFCgn0n0/SJMR22cwofRV1mtnJQYcSB1wUTaA/X1LnA3es66MCO5A==",
+ "dev": true
+ },
+ "@babel/template": {
+ "version": "7.16.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz",
+ "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.16.7",
+ "@babel/parser": "^7.16.7",
+ "@babel/types": "^7.16.7"
+ }
+ },
+ "@babel/traverse": {
+ "version": "7.16.10",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.10.tgz",
+ "integrity": "sha512-yzuaYXoRJBGMlBhsMJoUW7G1UmSb/eXr/JHYM/MsOJgavJibLwASijW7oXBdw3NQ6T0bW7Ty5P/VarOs9cHmqw==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.16.7",
+ "@babel/generator": "^7.16.8",
+ "@babel/helper-environment-visitor": "^7.16.7",
+ "@babel/helper-function-name": "^7.16.7",
+ "@babel/helper-hoist-variables": "^7.16.7",
+ "@babel/helper-split-export-declaration": "^7.16.7",
+ "@babel/parser": "^7.16.10",
+ "@babel/types": "^7.16.8",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0"
+ }
+ },
+ "@babel/types": {
+ "version": "7.16.8",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.8.tgz",
+ "integrity": "sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.16.7",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "async": {
+ "version": "2.6.3",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
+ "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.14"
+ }
+ },
+ "debug": {
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
+ "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
+ "dev": true,
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "dev": true
+ },
+ "istanbul-lib-coverage": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz",
+ "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==",
+ "dev": true
+ },
+ "istanbul-lib-hook": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz",
+ "integrity": "sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA==",
+ "dev": true,
+ "requires": {
+ "append-transform": "^1.0.0"
+ }
+ },
+ "istanbul-lib-instrument": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz",
+ "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==",
+ "dev": true,
+ "requires": {
+ "@babel/generator": "^7.4.0",
+ "@babel/parser": "^7.4.3",
+ "@babel/template": "^7.4.0",
+ "@babel/traverse": "^7.4.3",
+ "@babel/types": "^7.4.0",
+ "istanbul-lib-coverage": "^2.0.5",
+ "semver": "^6.0.0"
+ }
+ },
+ "istanbul-lib-report": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz",
+ "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==",
+ "dev": true,
+ "requires": {
+ "istanbul-lib-coverage": "^2.0.5",
+ "make-dir": "^2.1.0",
+ "supports-color": "^6.1.0"
+ }
+ },
+ "istanbul-lib-source-maps": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz",
+ "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.1",
+ "istanbul-lib-coverage": "^2.0.5",
+ "make-dir": "^2.1.0",
+ "rimraf": "^2.6.3",
+ "source-map": "^0.6.1"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "istanbul-reports": {
+ "version": "2.2.7",
+ "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.7.tgz",
+ "integrity": "sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg==",
+ "dev": true,
+ "requires": {
+ "html-escaper": "^2.0.0"
+ }
+ },
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "jsesc": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+ "dev": true
+ },
+ "make-dir": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
+ "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+ "dev": true,
+ "requires": {
+ "pify": "^4.0.1",
+ "semver": "^5.6.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ }
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "pify": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+ "dev": true
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ },
+ "to-fast-properties": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
+ "dev": true
+ }
+ }
+ },
+ "istanbul-instrumenter-loader": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-3.0.1.tgz",
+ "integrity": "sha512-a5SPObZgS0jB/ixaKSMdn6n/gXSrK2S6q/UfRJBT3e6gQmVjwZROTODQsYW5ZNwOu78hG62Y3fWlebaVOL0C+w==",
+ "dev": true,
+ "requires": {
+ "convert-source-map": "^1.5.0",
+ "istanbul-lib-instrument": "^1.7.3",
+ "loader-utils": "^1.1.0",
+ "schema-utils": "^0.3.0"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "5.5.2",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
+ "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
+ "dev": true,
+ "requires": {
+ "co": "^4.6.0",
+ "fast-deep-equal": "^1.0.0",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.3.0"
+ }
+ },
+ "fast-deep-equal": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
+ "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=",
+ "dev": true
+ },
+ "json-schema-traverse": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
+ "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=",
+ "dev": true
+ },
+ "schema-utils": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz",
+ "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=",
+ "dev": true,
+ "requires": {
+ "ajv": "^5.0.0"
+ }
+ }
+ }
+ },
+ "istanbul-lib-coverage": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz",
+ "integrity": "sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ==",
+ "dev": true
+ },
+ "istanbul-lib-instrument": {
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.2.tgz",
+ "integrity": "sha512-aWHxfxDqvh/ZlxR8BBaEPVSWDPUkGD63VjGQn3jcw8jCp7sHEMKcrj4xfJn/ABzdMEHiQNyvDQhqm5o8+SQg7A==",
+ "dev": true,
+ "requires": {
+ "babel-generator": "^6.18.0",
+ "babel-template": "^6.16.0",
+ "babel-traverse": "^6.18.0",
+ "babel-types": "^6.18.0",
+ "babylon": "^6.18.0",
+ "istanbul-lib-coverage": "^1.2.1",
+ "semver": "^5.3.0"
+ }
+ },
+ "jasmine": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz",
+ "integrity": "sha1-awicChFXax8W3xG4AUbZHU6Lij4=",
+ "dev": true,
+ "requires": {
+ "exit": "^0.1.2",
+ "glob": "^7.0.6",
+ "jasmine-core": "~2.8.0"
+ },
+ "dependencies": {
+ "jasmine-core": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz",
+ "integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=",
+ "dev": true
+ }
+ }
+ },
+ "jasmine-core": {
+ "version": "2.99.1",
+ "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.99.1.tgz",
+ "integrity": "sha1-5kAN8ea1bhMLYcS80JPap/boyhU=",
+ "dev": true
+ },
+ "jasmine-spec-reporter": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-4.2.1.tgz",
+ "integrity": "sha512-FZBoZu7VE5nR7Nilzy+Np8KuVIOxF4oXDPDknehCYBDE080EnlPu0afdZNmpGDBRCUBv3mj5qgqCRmk6W/K8vg==",
+ "dev": true,
+ "requires": {
+ "colors": "1.1.2"
+ }
+ },
+ "jasminewd2": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz",
+ "integrity": "sha1-43zwsX8ZnM4jvqcbIDk5Uka07E4=",
+ "dev": true
+ },
+ "js-base64": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz",
+ "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==",
+ "dev": true,
+ "optional": true
+ },
+ "js-tokens": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
+ "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+ "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "jsbn": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
+ "dev": true
+ },
+ "jsesc": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz",
+ "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=",
+ "dev": true
+ },
+ "json-buffer": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz",
+ "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=",
+ "dev": true
+ },
+ "json-parse-better-errors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
+ "dev": true
+ },
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
+ "dev": true
+ },
+ "json3": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz",
+ "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=",
+ "dev": true
+ },
+ "json5": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+ "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.0"
+ }
+ },
+ "jsonfile": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+ "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "jsonparse": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
+ "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=",
+ "dev": true
+ },
+ "jsprim": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
+ "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
+ "dev": true,
+ "requires": {
+ "assert-plus": "1.0.0",
+ "extsprintf": "1.3.0",
+ "json-schema": "0.4.0",
+ "verror": "1.10.0"
+ },
+ "dependencies": {
+ "json-schema": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
+ "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
+ "dev": true
+ }
+ }
+ },
+ "jszip": {
+ "version": "3.7.1",
+ "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.7.1.tgz",
+ "integrity": "sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg==",
+ "dev": true,
+ "requires": {
+ "lie": "~3.3.0",
+ "pako": "~1.0.2",
+ "readable-stream": "~2.3.6",
+ "set-immediate-shim": "~1.0.1"
+ }
+ },
+ "junk": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz",
+ "integrity": "sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==",
+ "dev": true
+ },
+ "karma": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/karma/-/karma-4.0.0.tgz",
+ "integrity": "sha512-EFoFs3F6G0BcUGPNOn/YloGOb3h09hzTguyXlg6loHlKY76qbJikkcyPk43m2kfRF65TUGda/mig29QQtyhm1g==",
+ "dev": true,
+ "requires": {
+ "bluebird": "^3.3.0",
+ "body-parser": "^1.16.1",
+ "chokidar": "^2.0.3",
+ "colors": "^1.1.0",
+ "combine-lists": "^1.0.0",
+ "connect": "^3.6.0",
+ "core-js": "^2.2.0",
+ "di": "^0.0.1",
+ "dom-serialize": "^2.2.0",
+ "expand-braces": "^0.1.1",
+ "flatted": "^2.0.0",
+ "glob": "^7.1.1",
+ "graceful-fs": "^4.1.2",
+ "http-proxy": "^1.13.0",
+ "isbinaryfile": "^3.0.0",
+ "lodash": "^4.17.5",
+ "log4js": "^3.0.0",
+ "mime": "^2.3.1",
+ "minimatch": "^3.0.2",
+ "optimist": "^0.6.1",
+ "qjobs": "^1.1.4",
+ "range-parser": "^1.2.0",
+ "rimraf": "^2.6.0",
+ "safe-buffer": "^5.0.1",
+ "socket.io": "2.1.1",
+ "source-map": "^0.6.1",
+ "tmp": "0.0.33",
+ "useragent": "2.3.0"
+ },
+ "dependencies": {
+ "mime": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz",
+ "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "karma-chrome-launcher": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz",
+ "integrity": "sha512-uf/ZVpAabDBPvdPdveyk1EPgbnloPvFFGgmRhYLTDH7gEB4nZdSBk8yTU47w1g/drLSx5uMOkjKk7IWKfWg/+w==",
+ "dev": true,
+ "requires": {
+ "fs-access": "^1.0.0",
+ "which": "^1.2.1"
+ }
+ },
+ "karma-coverage-istanbul-reporter": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-2.0.5.tgz",
+ "integrity": "sha512-yPvAlKtY3y+rKKWbOo0CzBMVTvJEeMOgbMXuVv3yWvS8YtYKC98AU9vFF0mVBZ2RP1E9SgS90+PT6Kf14P3S4w==",
+ "dev": true,
+ "requires": {
+ "istanbul-api": "^2.1.1",
+ "minimatch": "^3.0.4"
+ }
+ },
+ "karma-jasmine": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-1.1.2.tgz",
+ "integrity": "sha1-OU8rJf+0pkS5rabyLUQ+L9CIhsM=",
+ "dev": true
+ },
+ "karma-jasmine-html-reporter": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-0.2.2.tgz",
+ "integrity": "sha1-SKjl7xiAdhfuK14zwRlMNbQ5Ukw=",
+ "dev": true,
+ "requires": {
+ "karma-jasmine": "^1.0.2"
+ }
+ },
+ "karma-source-map-support": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.3.0.tgz",
+ "integrity": "sha512-HcPqdAusNez/ywa+biN4EphGz62MmQyPggUsDfsHqa7tSe4jdsxgvTKuDfIazjL+IOxpVWyT7Pr4dhAV+sxX5Q==",
+ "dev": true,
+ "requires": {
+ "source-map-support": "^0.5.5"
+ }
+ },
+ "keyv": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz",
+ "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==",
+ "dev": true,
+ "requires": {
+ "json-buffer": "3.0.0"
+ }
+ },
+ "killable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
+ "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==",
+ "dev": true
+ },
+ "kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true
+ },
+ "latest-version": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz",
+ "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==",
+ "dev": true,
+ "requires": {
+ "package-json": "^6.3.0"
+ }
+ },
+ "lazy-val": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.4.tgz",
+ "integrity": "sha512-u93kb2fPbIrfzBuLjZE+w+fJbUUMhNDXxNmMfaqNgpfQf1CO5ZSe2LfsnBqVAk7i/2NF48OSoRj+Xe2VT+lE8Q==",
+ "dev": true
+ },
+ "lcid": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
+ "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
+ "requires": {
+ "invert-kv": "^1.0.0"
+ }
+ },
+ "less": {
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/less/-/less-3.9.0.tgz",
+ "integrity": "sha512-31CmtPEZraNUtuUREYjSqRkeETFdyEHSEPAGq4erDlUXtda7pzNmctdljdIagSb589d/qXGWiiP31R5JVf+v0w==",
+ "dev": true,
+ "requires": {
+ "clone": "^2.1.2",
+ "errno": "^0.1.1",
+ "graceful-fs": "^4.1.2",
+ "image-size": "~0.5.0",
+ "mime": "^1.4.1",
+ "mkdirp": "^0.5.0",
+ "promise": "^7.1.1",
+ "request": "^2.83.0",
+ "source-map": "~0.6.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "less-loader": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-4.1.0.tgz",
+ "integrity": "sha512-KNTsgCE9tMOM70+ddxp9yyt9iHqgmSs0yTZc5XH5Wo+g80RWRIYNqE58QJKm/yMud5wZEvz50ugRDuzVIkyahg==",
+ "dev": true,
+ "requires": {
+ "clone": "^2.1.1",
+ "loader-utils": "^1.1.0",
+ "pify": "^3.0.0"
+ }
+ },
+ "libphonenumber-js": {
+ "version": "0.4.52",
+ "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-0.4.52.tgz",
+ "integrity": "sha512-Ns5FcEp8W/lZd+lKu7OB46RYBKcncN/S3C51OFh7qXbu6kOFAd75A4M0jtasK3MUWMxKsKXII4WL+GbqaLa2DQ==",
+ "requires": {
+ "babel-runtime": "^6.6.1",
+ "bluebird": "^3.4.6",
+ "minimist": "^1.2.0",
+ "xml2js": "^0.4.17"
+ }
+ },
+ "license-webpack-plugin": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-2.1.0.tgz",
+ "integrity": "sha512-vDiBeMWxjE9n6TabQ9J4FH8urFdsRK0Nvxn1cit9biCiR9aq1zBR0X2BlAkEiIG6qPamLeU0GzvIgLkrFc398A==",
+ "dev": true,
+ "requires": {
+ "@types/webpack-sources": "^0.1.5",
+ "webpack-sources": "^1.2.0"
+ }
+ },
+ "lie": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
+ "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+ "dev": true,
+ "requires": {
+ "immediate": "~3.0.5"
+ }
+ },
+ "load-json-file": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
+ "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "parse-json": "^2.2.0",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0",
+ "strip-bom": "^2.0.0"
+ },
+ "dependencies": {
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ }
+ }
+ },
+ "loader-runner": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz",
+ "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==",
+ "dev": true
+ },
+ "loader-utils": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz",
+ "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==",
+ "dev": true,
+ "requires": {
+ "big.js": "^5.2.2",
+ "emojis-list": "^2.0.0",
+ "json5": "^1.0.1"
+ }
+ },
+ "locate-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+ "dev": true,
+ "requires": {
+ "p-locate": "^2.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+ },
+ "lodash.assign": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz",
+ "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=",
+ "dev": true,
+ "optional": true
+ },
+ "lodash.camelcase": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+ "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY="
+ },
+ "lodash.clone": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz",
+ "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y="
+ },
+ "lodash.clonedeep": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
+ "dev": true
+ },
+ "lodash.debounce": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+ "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=",
+ "dev": true
+ },
+ "lodash.get": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
+ "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=",
+ "dev": true
+ },
+ "lodash.mergewith": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz",
+ "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==",
+ "dev": true,
+ "optional": true
+ },
+ "lodash.tail": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.tail/-/lodash.tail-4.1.1.tgz",
+ "integrity": "sha1-0jM6NtnncXyK0vfKyv7HwytERmQ=",
+ "dev": true
+ },
+ "log4js": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/log4js/-/log4js-3.0.6.tgz",
+ "integrity": "sha512-ezXZk6oPJCWL483zj64pNkMuY/NcRX5MPiB0zE6tjZM137aeusrOnW1ecxgF9cmwMWkBMhjteQxBPoZBh9FDxQ==",
+ "dev": true,
+ "requires": {
+ "circular-json": "^0.5.5",
+ "date-format": "^1.2.0",
+ "debug": "^3.1.0",
+ "rfdc": "^1.1.2",
+ "streamroller": "0.7.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+ "dev": true
+ }
+ }
+ },
+ "loglevel": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.1.tgz",
+ "integrity": "sha1-4PyVEztu8nbNyIh82vJKpvFW+Po=",
+ "dev": true
+ },
+ "long": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz",
+ "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s="
+ },
+ "loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "dev": true,
+ "requires": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ }
+ },
+ "loud-rejection": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
+ "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
+ "dev": true,
+ "requires": {
+ "currently-unhandled": "^0.4.1",
+ "signal-exit": "^3.0.0"
+ }
+ },
+ "lowercase-keys": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
+ "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==",
+ "dev": true
+ },
+ "lru-cache": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
+ "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
+ "dev": true,
+ "requires": {
+ "pseudomap": "^1.0.2",
+ "yallist": "^2.1.2"
+ }
+ },
+ "magic-string": {
+ "version": "0.25.2",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.2.tgz",
+ "integrity": "sha512-iLs9mPjh9IuTtRsqqhNGYcZXGei0Nh/A4xirrsqW7c+QhKVFL2vm7U09ru6cHRD22azaP/wMDgI+HCqbETMTtg==",
+ "dev": true,
+ "requires": {
+ "sourcemap-codec": "^1.4.4"
+ }
+ },
+ "make-dir": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz",
+ "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==",
+ "requires": {
+ "pify": "^3.0.0"
+ }
+ },
+ "make-error": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz",
+ "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==",
+ "dev": true
+ },
+ "make-fetch-happen": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-4.0.1.tgz",
+ "integrity": "sha512-7R5ivfy9ilRJ1EMKIOziwrns9fGeAD4bAha8EB7BIiBBLHm2KeTUGCrICFt2rbHfzheTLynv50GnNTK1zDTrcQ==",
+ "dev": true,
+ "requires": {
+ "agentkeepalive": "^3.4.1",
+ "cacache": "^11.0.1",
+ "http-cache-semantics": "^3.8.1",
+ "http-proxy-agent": "^2.1.0",
+ "https-proxy-agent": "^2.2.1",
+ "lru-cache": "^4.1.2",
+ "mississippi": "^3.0.0",
+ "node-fetch-npm": "^2.0.2",
+ "promise-retry": "^1.1.1",
+ "socks-proxy-agent": "^4.0.0",
+ "ssri": "^6.0.0"
+ },
+ "dependencies": {
+ "cacache": {
+ "version": "11.3.2",
+ "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz",
+ "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==",
+ "dev": true,
+ "requires": {
+ "bluebird": "^3.5.3",
+ "chownr": "^1.1.1",
+ "figgy-pudding": "^3.5.1",
+ "glob": "^7.1.3",
+ "graceful-fs": "^4.1.15",
+ "lru-cache": "^5.1.1",
+ "mississippi": "^3.0.0",
+ "mkdirp": "^0.5.1",
+ "move-concurrently": "^1.0.1",
+ "promise-inflight": "^1.0.1",
+ "rimraf": "^2.6.2",
+ "ssri": "^6.0.1",
+ "unique-filename": "^1.1.1",
+ "y18n": "^4.0.0"
+ },
+ "dependencies": {
+ "lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "requires": {
+ "yallist": "^3.0.2"
+ }
+ }
+ }
+ },
+ "mississippi": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
+ "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==",
+ "dev": true,
+ "requires": {
+ "concat-stream": "^1.5.0",
+ "duplexify": "^3.4.2",
+ "end-of-stream": "^1.1.0",
+ "flush-write-stream": "^1.0.0",
+ "from2": "^2.1.0",
+ "parallel-transform": "^1.1.0",
+ "pump": "^3.0.0",
+ "pumpify": "^1.3.3",
+ "stream-each": "^1.1.0",
+ "through2": "^2.0.0"
+ }
+ },
+ "pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "ssri": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz",
+ "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==",
+ "dev": true,
+ "requires": {
+ "figgy-pudding": "^3.5.1"
+ }
+ },
+ "yallist": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
+ "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
+ "dev": true
+ }
+ }
+ },
+ "map-age-cleaner": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz",
+ "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==",
+ "dev": true,
+ "requires": {
+ "p-defer": "^1.0.0"
+ }
+ },
+ "map-cache": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
+ "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
+ "dev": true
+ },
+ "map-obj": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
+ "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=",
+ "dev": true
+ },
+ "map-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
+ "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
+ "dev": true,
+ "requires": {
+ "object-visit": "^1.0.0"
+ }
+ },
+ "material-colors": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz",
+ "integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==",
+ "dev": true
+ },
+ "md5.js": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
+ "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
+ "dev": true,
+ "requires": {
+ "hash-base": "^3.0.0",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
+ "dev": true
+ },
+ "mem": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/mem/-/mem-4.1.0.tgz",
+ "integrity": "sha512-I5u6Q1x7wxO0kdOpYBB28xueHADYps5uty/zg936CiG8NTe5sJL8EjrCuLneuDW3PlMdZBGDIn8BirEVdovZvg==",
+ "dev": true,
+ "requires": {
+ "map-age-cleaner": "^0.1.1",
+ "mimic-fn": "^1.0.0",
+ "p-is-promise": "^2.0.0"
+ }
+ },
+ "memory-fs": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
+ "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=",
+ "dev": true,
+ "requires": {
+ "errno": "^0.1.3",
+ "readable-stream": "^2.0.1"
+ }
+ },
+ "meow": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
+ "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
+ "dev": true,
+ "requires": {
+ "camelcase-keys": "^2.0.0",
+ "decamelize": "^1.1.2",
+ "loud-rejection": "^1.0.0",
+ "map-obj": "^1.0.1",
+ "minimist": "^1.1.3",
+ "normalize-package-data": "^2.3.4",
+ "object-assign": "^4.0.1",
+ "read-pkg-up": "^1.0.1",
+ "redent": "^1.0.0",
+ "trim-newlines": "^1.0.0"
+ }
+ },
+ "merge-descriptors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+ "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=",
+ "dev": true
+ },
+ "methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
+ "dev": true
+ },
+ "micromatch": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+ "dev": true,
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "braces": "^2.3.1",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "extglob": "^2.0.4",
+ "fragment-cache": "^0.2.1",
+ "kind-of": "^6.0.2",
+ "nanomatch": "^1.2.9",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.2"
+ }
+ },
+ "miller-rabin": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
+ "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.0.0",
+ "brorand": "^1.0.1"
+ }
+ },
+ "mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "dev": true,
+ "optional": true
+ },
+ "mime-db": {
+ "version": "1.38.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz",
+ "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==",
+ "dev": true
+ },
+ "mime-types": {
+ "version": "2.1.22",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz",
+ "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==",
+ "dev": true,
+ "requires": {
+ "mime-db": "~1.38.0"
+ }
+ },
+ "mimic-fn": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
+ "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
+ "dev": true
+ },
+ "mimic-response": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
+ "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==",
+ "dev": true
+ },
+ "mini-css-extract-plugin": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.5.0.tgz",
+ "integrity": "sha512-IuaLjruM0vMKhUUT51fQdQzBYTX49dLj8w68ALEAe2A4iYNpIC4eMac67mt3NzycvjOlf07/kYxJDc0RTl1Wqw==",
+ "dev": true,
+ "requires": {
+ "loader-utils": "^1.1.0",
+ "schema-utils": "^1.0.0",
+ "webpack-sources": "^1.1.0"
+ }
+ },
+ "minimalistic-assert": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
+ "dev": true
+ },
+ "minimalistic-crypto-utils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
+ "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
+ },
+ "minipass": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz",
+ "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.1.2",
+ "yallist": "^3.0.0"
+ },
+ "dependencies": {
+ "yallist": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
+ "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
+ "dev": true
+ }
+ }
+ },
+ "mississippi": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz",
+ "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==",
+ "dev": true,
+ "requires": {
+ "concat-stream": "^1.5.0",
+ "duplexify": "^3.4.2",
+ "end-of-stream": "^1.1.0",
+ "flush-write-stream": "^1.0.0",
+ "from2": "^2.1.0",
+ "parallel-transform": "^1.1.0",
+ "pump": "^2.0.1",
+ "pumpify": "^1.3.3",
+ "stream-each": "^1.1.0",
+ "through2": "^2.0.0"
+ }
+ },
+ "mixin-deep": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
+ "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
+ "dev": true,
+ "requires": {
+ "for-in": "^1.0.2",
+ "is-extendable": "^1.0.1"
+ },
+ "dependencies": {
+ "is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "requires": {
+ "is-plain-object": "^2.0.4"
+ }
+ }
+ }
+ },
+ "mixin-object": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz",
+ "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=",
+ "dev": true,
+ "requires": {
+ "for-in": "^0.1.3",
+ "is-extendable": "^0.1.1"
+ },
+ "dependencies": {
+ "for-in": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz",
+ "integrity": "sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=",
+ "dev": true
+ }
+ }
+ },
+ "mixpanel-browser": {
+ "version": "2.29.1",
+ "resolved": "https://registry.npmjs.org/mixpanel-browser/-/mixpanel-browser-2.29.1.tgz",
+ "integrity": "sha512-RSBqVBznOkKBz3MkCXRrkTEEXqoNNYAbASpjaCxvhpT5pykWhjh7JY54fAmOvtG9XNL3GHYA6XiB7Yos4ngNYQ=="
+ },
+ "mkdirp": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+ "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.5"
+ }
+ },
+ "moment": {
+ "version": "2.24.0",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
+ "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
+ },
+ "move-concurrently": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
+ "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=",
+ "dev": true,
+ "requires": {
+ "aproba": "^1.1.1",
+ "copy-concurrently": "^1.0.0",
+ "fs-write-stream-atomic": "^1.0.8",
+ "mkdirp": "^0.5.1",
+ "rimraf": "^2.5.4",
+ "run-queue": "^1.0.3"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
+ "multicast-dns": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz",
+ "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==",
+ "dev": true,
+ "requires": {
+ "dns-packet": "^1.3.1",
+ "thunky": "^1.0.2"
+ }
+ },
+ "multicast-dns-service-types": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz",
+ "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=",
+ "dev": true
+ },
+ "mute-stream": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
+ "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
+ "dev": true
+ },
+ "nan": {
+ "version": "2.12.1",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz",
+ "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==",
+ "dev": true,
+ "optional": true
+ },
+ "nanomatch": {
+ "version": "1.2.13",
+ "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
+ "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
+ "dev": true,
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "fragment-cache": "^0.2.1",
+ "is-windows": "^1.0.2",
+ "kind-of": "^6.0.2",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ }
+ },
+ "negotiator": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
+ "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=",
+ "dev": true
+ },
+ "neo-async": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz",
+ "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==",
+ "dev": true
+ },
+ "ng2-charts": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/ng2-charts/-/ng2-charts-1.6.0.tgz",
+ "integrity": "sha512-9w0WH69x5/nuqC1og2WaY39NbaBqTGIP1+5gZaH7/KPN6UEPonNg/pYnsIVklLj1DWPWXKa8+XXIJZ1jy5nLxg==",
+ "requires": {
+ "chart.js": "^2.6.0"
+ },
+ "dependencies": {
+ "chart.js": {
+ "version": "2.9.4",
+ "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.9.4.tgz",
+ "integrity": "sha512-B07aAzxcrikjAPyV+01j7BmOpxtQETxTSlQ26BEYJ+3iUkbNKaOJ/nDbT6JjyqYxseM0ON12COHYdU2cTIjC7A==",
+ "requires": {
+ "chartjs-color": "^2.1.0",
+ "moment": "^2.10.2"
+ }
+ }
+ }
+ },
+ "ng2-dragula": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/ng2-dragula/-/ng2-dragula-1.3.1.tgz",
+ "integrity": "sha1-EDhMJAApBDOm+0XpHHRm5ojjrTc=",
+ "requires": {
+ "dragula": "^3.7.2"
+ }
+ },
+ "ng2-file-upload": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ng2-file-upload/-/ng2-file-upload-1.2.1.tgz",
+ "integrity": "sha1-VWPF39b0P7++gVwgbjQ0ZKCmoZc="
+ },
+ "ng2-validation": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/ng2-validation/-/ng2-validation-4.2.0.tgz",
+ "integrity": "sha1-841EHT+36GIVUWZIAEWq/5rRHbs=",
+ "requires": {
+ "libphonenumber-js": "^0.4.5"
+ }
+ },
+ "ngx-color": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/ngx-color/-/ngx-color-7.0.0.tgz",
+ "integrity": "sha512-BiTapBTT/f3sFSEFqet3xe06bpqmBm7hmA24s09ogRYYVGL1J69U13XfLwTQG8PhX4qNBceh7p5nhKA1zczHMg==",
+ "dev": true,
+ "requires": {
+ "@ctrl/tinycolor": "^3.4.0",
+ "material-colors": "^1.2.6",
+ "tslib": "^2.1.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
+ "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==",
+ "dev": true
+ }
+ }
+ },
+ "ngx-color-picker": {
+ "version": "5.3.8",
+ "resolved": "https://registry.npmjs.org/ngx-color-picker/-/ngx-color-picker-5.3.8.tgz",
+ "integrity": "sha512-LZ+GR6hZ/Tpv58mjIdnnH1uP1HyQmeZgBFQUL34914OBspeoD7f+vI4lZ7J3WpCTX8w2DzvnfXqzToWAx0SImA=="
+ },
+ "ngx-electron": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ngx-electron/-/ngx-electron-2.1.1.tgz",
+ "integrity": "sha512-XCr/tiVR9J1SLbxy8TB0LjVzwahwYZCsHhFmng7pjsKUByWVkzDcvxo1EE+rR2MlHjE5jKmP3JH5qeLONdSAtw==",
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "ngx-pagination": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/ngx-pagination/-/ngx-pagination-3.1.0.tgz",
+ "integrity": "sha512-D/8Is3z0nipHCCQN0XZyh5/nhSrON/ybft3Wk8dfPHMGtjqzoOSorNaanypO35JD/jECcam/cS/evjlaqDAgQg=="
+ },
+ "ngx-perfect-scrollbar": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/ngx-perfect-scrollbar/-/ngx-perfect-scrollbar-6.1.0.tgz",
+ "integrity": "sha512-96/NE6gmP8+qF+ur84IQqDQtoLHhheKpQ+uDG11Z+WuOIuH7hHmZIKi1E7Wz+889s0AARGMTpgR05T0CMCxUew==",
+ "requires": {
+ "perfect-scrollbar": "^1.3.0",
+ "resize-observer-polyfill": "^1.4.0"
+ }
+ },
+ "ngx-quill": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/ngx-quill/-/ngx-quill-2.0.4.tgz",
+ "integrity": "sha1-D1rBD2BjU04n1femGIlswlVzGJ4=",
+ "requires": {
+ "@types/quill": "^1.3.3",
+ "quill": "^1.3.2"
+ }
+ },
+ "nice-try": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+ "dev": true
+ },
+ "nimn-date-parser": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/nimn-date-parser/-/nimn-date-parser-1.0.0.tgz",
+ "integrity": "sha512-1Nf+x3EeMvHUiHsVuEhiZnwA8RMeOBVTQWfB1S2n9+i6PYCofHd2HRMD+WOHIHYshy4T4Gk8wQoCol7Hq3av8Q=="
+ },
+ "nimn_schema_builder": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/nimn_schema_builder/-/nimn_schema_builder-1.1.0.tgz",
+ "integrity": "sha512-DK5/B8CM4qwzG2URy130avcwPev4uO0ev836FbQyKo1ms6I9z/i6EJyiZ+d9xtgloxUri0W+5gfR8YbPq7SheA=="
+ },
+ "nimnjs": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/nimnjs/-/nimnjs-1.3.2.tgz",
+ "integrity": "sha512-TIOtI4iqkQrUM1tiM76AtTQem0c7e56SkDZ7sj1d1MfUsqRcq2ZWQvej/O+HBTZV7u/VKnwlKTDugK/75IRPPw==",
+ "requires": {
+ "nimn-date-parser": "^1.0.0",
+ "nimn_schema_builder": "^1.0.0"
+ }
+ },
+ "node-fetch-npm": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz",
+ "integrity": "sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw==",
+ "dev": true,
+ "requires": {
+ "encoding": "^0.1.11",
+ "json-parse-better-errors": "^1.0.0",
+ "safe-buffer": "^5.1.1"
+ }
+ },
+ "node-forge": {
+ "version": "0.7.5",
+ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz",
+ "integrity": "sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ==",
+ "dev": true
+ },
+ "node-gyp": {
+ "version": "3.8.0",
+ "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz",
+ "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "fstream": "^1.0.0",
+ "glob": "^7.0.3",
+ "graceful-fs": "^4.1.2",
+ "mkdirp": "^0.5.0",
+ "nopt": "2 || 3",
+ "npmlog": "0 || 1 || 2 || 3 || 4",
+ "osenv": "0",
+ "request": "^2.87.0",
+ "rimraf": "2",
+ "semver": "~5.3.0",
+ "tar": "^2.0.0",
+ "which": "1"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
+ "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=",
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "node-libs-browser": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.0.tgz",
+ "integrity": "sha512-5MQunG/oyOaBdttrL40dA7bUfPORLRWMUJLQtMg7nluxUvk5XwnLdL9twQHFAjRx/y7mIMkLKT9++qPbbk6BZA==",
+ "dev": true,
+ "requires": {
+ "assert": "^1.1.1",
+ "browserify-zlib": "^0.2.0",
+ "buffer": "^4.3.0",
+ "console-browserify": "^1.1.0",
+ "constants-browserify": "^1.0.0",
+ "crypto-browserify": "^3.11.0",
+ "domain-browser": "^1.1.1",
+ "events": "^3.0.0",
+ "https-browserify": "^1.0.0",
+ "os-browserify": "^0.3.0",
+ "path-browserify": "0.0.0",
+ "process": "^0.11.10",
+ "punycode": "^1.2.4",
+ "querystring-es3": "^0.2.0",
+ "readable-stream": "^2.3.3",
+ "stream-browserify": "^2.0.1",
+ "stream-http": "^2.7.2",
+ "string_decoder": "^1.0.0",
+ "timers-browserify": "^2.0.4",
+ "tty-browserify": "0.0.0",
+ "url": "^0.11.0",
+ "util": "^0.11.0",
+ "vm-browserify": "0.0.4"
+ },
+ "dependencies": {
+ "punycode": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+ "dev": true
+ }
+ }
+ },
+ "node-sass": {
+ "version": "4.11.0",
+ "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.11.0.tgz",
+ "integrity": "sha512-bHUdHTphgQJZaF1LASx0kAviPH7sGlcyNhWade4eVIpFp6tsn7SV8xNMTbsQFpEV9VXpnwTTnNYlfsZXgGgmkA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "async-foreach": "^0.1.3",
+ "chalk": "^1.1.1",
+ "cross-spawn": "^3.0.0",
+ "gaze": "^1.0.0",
+ "get-stdin": "^4.0.1",
+ "glob": "^7.0.3",
+ "in-publish": "^2.0.0",
+ "lodash.assign": "^4.2.0",
+ "lodash.clonedeep": "^4.3.2",
+ "lodash.mergewith": "^4.6.0",
+ "meow": "^3.7.0",
+ "mkdirp": "^0.5.1",
+ "nan": "^2.10.0",
+ "node-gyp": "^3.8.0",
+ "npmlog": "^4.0.0",
+ "request": "^2.88.0",
+ "sass-graph": "^2.2.4",
+ "stdout-stream": "^1.4.0",
+ "true-case-path": "^1.0.2"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+ "dev": true,
+ "optional": true
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "nopt": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
+ "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "abbrev": "1"
+ }
+ },
+ "normalize-package-data": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+ "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^2.1.4",
+ "resolve": "^1.10.0",
+ "semver": "2 || 3 || 4 || 5",
+ "validate-npm-package-license": "^3.0.1"
+ }
+ },
+ "normalize-path": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+ "dev": true,
+ "requires": {
+ "remove-trailing-separator": "^1.0.1"
+ }
+ },
+ "normalize-range": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
+ "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=",
+ "dev": true
+ },
+ "normalize-url": {
+ "version": "4.5.1",
+ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz",
+ "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==",
+ "dev": true
+ },
+ "npm-bundled": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz",
+ "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==",
+ "dev": true
+ },
+ "npm-package-arg": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.0.tgz",
+ "integrity": "sha512-zYbhP2k9DbJhA0Z3HKUePUgdB1x7MfIfKssC+WLPFMKTBZKpZh5m13PgexJjCq6KW7j17r0jHWcCpxEqnnncSA==",
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^2.6.0",
+ "osenv": "^0.1.5",
+ "semver": "^5.5.0",
+ "validate-npm-package-name": "^3.0.0"
+ }
+ },
+ "npm-packlist": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.1.tgz",
+ "integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==",
+ "dev": true,
+ "requires": {
+ "ignore-walk": "^3.0.1",
+ "npm-bundled": "^1.0.1"
+ }
+ },
+ "npm-pick-manifest": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-2.2.3.tgz",
+ "integrity": "sha512-+IluBC5K201+gRU85vFlUwX3PFShZAbAgDNp2ewJdWMVSppdo/Zih0ul2Ecky/X7b51J7LrrUAP+XOmOCvYZqA==",
+ "dev": true,
+ "requires": {
+ "figgy-pudding": "^3.5.1",
+ "npm-package-arg": "^6.0.0",
+ "semver": "^5.4.1"
+ }
+ },
+ "npm-registry-fetch": {
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-3.9.0.tgz",
+ "integrity": "sha512-srwmt8YhNajAoSAaDWndmZgx89lJwIZ1GWxOuckH4Coek4uHv5S+o/l9FLQe/awA+JwTnj4FJHldxhlXdZEBmw==",
+ "dev": true,
+ "requires": {
+ "JSONStream": "^1.3.4",
+ "bluebird": "^3.5.1",
+ "figgy-pudding": "^3.4.1",
+ "lru-cache": "^4.1.3",
+ "make-fetch-happen": "^4.0.1",
+ "npm-package-arg": "^6.1.0"
+ }
+ },
+ "npm-run-path": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
+ "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
+ "dev": true,
+ "requires": {
+ "path-key": "^2.0.0"
+ }
+ },
+ "npmlog": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
+ "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "are-we-there-yet": "~1.1.2",
+ "console-control-strings": "~1.1.0",
+ "gauge": "~2.7.3",
+ "set-blocking": "~2.0.0"
+ }
+ },
+ "nugget": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/nugget/-/nugget-2.0.1.tgz",
+ "integrity": "sha1-IBCVpIfhrTYIGzQy+jytpPjQcbA=",
+ "dev": true,
+ "requires": {
+ "debug": "^2.1.3",
+ "minimist": "^1.1.0",
+ "pretty-bytes": "^1.0.2",
+ "progress-stream": "^1.1.0",
+ "request": "^2.45.0",
+ "single-line-log": "^1.1.2",
+ "throttleit": "0.0.2"
+ }
+ },
+ "null-check": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/null-check/-/null-check-1.0.0.tgz",
+ "integrity": "sha1-l33/1xdgErnsMNKjnbXPcqBDnt0=",
+ "dev": true
+ },
+ "num2fraction": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz",
+ "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=",
+ "dev": true
+ },
+ "number-is-nan": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
+ },
+ "nwjs-builder-phoenix": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/nwjs-builder-phoenix/-/nwjs-builder-phoenix-1.15.0.tgz",
+ "integrity": "sha512-bYA1lsNwjm+WvfSfa2YxGanUGUN8CsSImBB6gkUZ+OWLxLAniga2sUrKcLaRi5l+qmLsDhbXmyNNppHsX4Ci5g==",
+ "dev": true,
+ "requires": {
+ "7zip-bin": "^2.0.4",
+ "bluebird": "^3.5.0",
+ "debug": "^2.6.1",
+ "dir-compare": "^1.3.0",
+ "fs-extra": "^3.0.1",
+ "globby": "^6.1.0",
+ "plist": "^2.0.1",
+ "progress": "^1.1.8",
+ "rcedit": "^0.8.0",
+ "request": "^2.80.0",
+ "request-progress": "^3.0.0",
+ "semver": "^5.3.0",
+ "source-map-support": "^0.4.11",
+ "tmp": "0.0.31",
+ "yargs": "^7.0.1"
+ },
+ "dependencies": {
+ "7zip-bin": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-2.4.1.tgz",
+ "integrity": "sha512-QU3oR1dLLVrYGRkb7LU17jMCpIkWtXXW7q71ECXWXkR9vOv37VjykqpvFgs29HgSCNLZHnNKJzdG6RwAW0LwIA==",
+ "dev": true,
+ "requires": {
+ "7zip-bin-linux": "~1.3.1",
+ "7zip-bin-mac": "~1.0.1",
+ "7zip-bin-win": "~2.1.1"
+ }
+ },
+ "base64-js": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.0.tgz",
+ "integrity": "sha1-o5mS1yNYSBGYK+XikLtqU9hnAPE=",
+ "dev": true
+ },
+ "fs-extra": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz",
+ "integrity": "sha1-N5TzeMWLNC6n27sjCVEJxLO2IpE=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "jsonfile": "^3.0.0",
+ "universalify": "^0.1.0"
+ }
+ },
+ "globby": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
+ "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=",
+ "dev": true,
+ "requires": {
+ "array-union": "^1.0.1",
+ "glob": "^7.0.3",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "jsonfile": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz",
+ "integrity": "sha1-pezG9l9T9mLEQVx2daAzHQmS7GY=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ },
+ "plist": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/plist/-/plist-2.1.0.tgz",
+ "integrity": "sha1-V8zbeggh3yGDEhejytVOPhRqECU=",
+ "dev": true,
+ "requires": {
+ "base64-js": "1.2.0",
+ "xmlbuilder": "8.2.2",
+ "xmldom": "0.1.x"
+ }
+ },
+ "rcedit": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/rcedit/-/rcedit-0.8.0.tgz",
+ "integrity": "sha1-3DEwrrAo9KZYrC48jm0EciLayUg=",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ },
+ "source-map-support": {
+ "version": "0.4.18",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
+ "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
+ "dev": true,
+ "requires": {
+ "source-map": "^0.5.6"
+ }
+ },
+ "tmp": {
+ "version": "0.0.31",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz",
+ "integrity": "sha1-jzirlDjhcxXl29izZX6L+yd65Kc=",
+ "dev": true,
+ "requires": {
+ "os-tmpdir": "~1.0.1"
+ }
+ },
+ "xmlbuilder": {
+ "version": "8.2.2",
+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz",
+ "integrity": "sha1-aSSGc0ELS6QuGmE2VR0pIjNap3M=",
+ "dev": true
+ }
+ }
+ },
+ "oauth-sign": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
+ "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
+ "dev": true
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+ "dev": true
+ },
+ "object-component": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz",
+ "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=",
+ "dev": true
+ },
+ "object-copy": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
+ "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
+ "dev": true,
+ "requires": {
+ "copy-descriptor": "^0.1.0",
+ "define-property": "^0.2.5",
+ "kind-of": "^3.0.3"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "object-keys": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz",
+ "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=",
+ "dev": true
+ },
+ "object-visit": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
+ "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.0"
+ }
+ },
+ "object.pick": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
+ "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ },
+ "obuf": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
+ "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==",
+ "dev": true
+ },
+ "on-finished": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+ "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+ "dev": true,
+ "requires": {
+ "ee-first": "1.1.1"
+ }
+ },
+ "on-headers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
+ "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
+ "dev": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "onetime": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
+ "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
+ "dev": true,
+ "requires": {
+ "mimic-fn": "^1.0.0"
+ }
+ },
+ "opn": {
+ "version": "5.4.0",
+ "resolved": "https://registry.npmjs.org/opn/-/opn-5.4.0.tgz",
+ "integrity": "sha512-YF9MNdVy/0qvJvDtunAOzFw9iasOQHpVthTCvGzxt61Il64AYSGdK+rYwld7NAfk9qJ7dt+hymBNSc9LNYS+Sw==",
+ "dev": true,
+ "requires": {
+ "is-wsl": "^1.1.0"
+ }
+ },
+ "optimist": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
+ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
+ "dev": true,
+ "requires": {
+ "minimist": "~0.0.1",
+ "wordwrap": "~0.0.2"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
+ "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=",
+ "dev": true
+ }
+ }
+ },
+ "optjs": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/optjs/-/optjs-3.2.2.tgz",
+ "integrity": "sha1-aabOicRCpEQDFBrS+bNwvVu29O4="
+ },
+ "original": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz",
+ "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==",
+ "dev": true,
+ "requires": {
+ "url-parse": "^1.4.3"
+ }
+ },
+ "os-browserify": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
+ "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=",
+ "dev": true
+ },
+ "os-homedir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
+ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
+ "dev": true
+ },
+ "os-locale": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
+ "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
+ "requires": {
+ "lcid": "^1.0.0"
+ }
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+ "dev": true
+ },
+ "osenv": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
+ "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
+ "dev": true,
+ "requires": {
+ "os-homedir": "^1.0.0",
+ "os-tmpdir": "^1.0.0"
+ }
+ },
+ "p-cancelable": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz",
+ "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==",
+ "dev": true
+ },
+ "p-defer": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
+ "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=",
+ "dev": true
+ },
+ "p-finally": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
+ "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
+ "dev": true
+ },
+ "p-is-promise": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.0.0.tgz",
+ "integrity": "sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg==",
+ "dev": true
+ },
+ "p-limit": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+ "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+ "dev": true,
+ "requires": {
+ "p-try": "^1.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+ "dev": true,
+ "requires": {
+ "p-limit": "^1.1.0"
+ }
+ },
+ "p-map": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz",
+ "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==",
+ "dev": true
+ },
+ "p-try": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+ "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
+ "dev": true
+ },
+ "package-json": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz",
+ "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==",
+ "dev": true,
+ "requires": {
+ "got": "^9.6.0",
+ "registry-auth-token": "^4.0.0",
+ "registry-url": "^5.0.0",
+ "semver": "^6.2.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ }
+ }
+ },
+ "pacote": {
+ "version": "9.4.0",
+ "resolved": "https://registry.npmjs.org/pacote/-/pacote-9.4.0.tgz",
+ "integrity": "sha512-WQ1KL/phGMkedYEQx9ODsjj7xvwLSpdFJJdEXrLyw5SILMxcTNt5DTxT2Z93fXuLFYJBlZJdnwdalrQdB/rX5w==",
+ "dev": true,
+ "requires": {
+ "bluebird": "^3.5.3",
+ "cacache": "^11.3.2",
+ "figgy-pudding": "^3.5.1",
+ "get-stream": "^4.1.0",
+ "glob": "^7.1.3",
+ "lru-cache": "^5.1.1",
+ "make-fetch-happen": "^4.0.1",
+ "minimatch": "^3.0.4",
+ "minipass": "^2.3.5",
+ "mississippi": "^3.0.0",
+ "mkdirp": "^0.5.1",
+ "normalize-package-data": "^2.4.0",
+ "npm-package-arg": "^6.1.0",
+ "npm-packlist": "^1.1.12",
+ "npm-pick-manifest": "^2.2.3",
+ "npm-registry-fetch": "^3.8.0",
+ "osenv": "^0.1.5",
+ "promise-inflight": "^1.0.1",
+ "promise-retry": "^1.1.1",
+ "protoduck": "^5.0.1",
+ "rimraf": "^2.6.2",
+ "safe-buffer": "^5.1.2",
+ "semver": "^5.6.0",
+ "ssri": "^6.0.1",
+ "tar": "^4.4.8",
+ "unique-filename": "^1.1.1",
+ "which": "^1.3.1"
+ },
+ "dependencies": {
+ "cacache": {
+ "version": "11.3.2",
+ "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz",
+ "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==",
+ "dev": true,
+ "requires": {
+ "bluebird": "^3.5.3",
+ "chownr": "^1.1.1",
+ "figgy-pudding": "^3.5.1",
+ "glob": "^7.1.3",
+ "graceful-fs": "^4.1.15",
+ "lru-cache": "^5.1.1",
+ "mississippi": "^3.0.0",
+ "mkdirp": "^0.5.1",
+ "move-concurrently": "^1.0.1",
+ "promise-inflight": "^1.0.1",
+ "rimraf": "^2.6.2",
+ "ssri": "^6.0.1",
+ "unique-filename": "^1.1.1",
+ "y18n": "^4.0.0"
+ }
+ },
+ "get-stream": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
+ "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
+ "dev": true,
+ "requires": {
+ "pump": "^3.0.0"
+ }
+ },
+ "lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "requires": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "mississippi": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
+ "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==",
+ "dev": true,
+ "requires": {
+ "concat-stream": "^1.5.0",
+ "duplexify": "^3.4.2",
+ "end-of-stream": "^1.1.0",
+ "flush-write-stream": "^1.0.0",
+ "from2": "^2.1.0",
+ "parallel-transform": "^1.1.0",
+ "pump": "^3.0.0",
+ "pumpify": "^1.3.3",
+ "stream-each": "^1.1.0",
+ "through2": "^2.0.0"
+ }
+ },
+ "pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "ssri": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz",
+ "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==",
+ "dev": true,
+ "requires": {
+ "figgy-pudding": "^3.5.1"
+ }
+ },
+ "tar": {
+ "version": "4.4.19",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz",
+ "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==",
+ "dev": true,
+ "requires": {
+ "chownr": "^1.1.4",
+ "fs-minipass": "^1.2.7",
+ "minipass": "^2.9.0",
+ "minizlib": "^1.3.3",
+ "mkdirp": "^0.5.5",
+ "safe-buffer": "^5.2.1",
+ "yallist": "^3.1.1"
+ },
+ "dependencies": {
+ "chownr": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+ "dev": true
+ },
+ "fs-minipass": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
+ "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
+ "dev": true,
+ "requires": {
+ "minipass": "^2.6.0"
+ }
+ },
+ "minipass": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
+ "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.1.2",
+ "yallist": "^3.0.0"
+ }
+ },
+ "minizlib": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz",
+ "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
+ "dev": true,
+ "requires": {
+ "minipass": "^2.9.0"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true
+ },
+ "yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true
+ }
+ }
+ },
+ "yallist": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
+ "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
+ "dev": true
+ }
+ }
+ },
+ "pako": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.8.tgz",
+ "integrity": "sha512-6i0HVbUfcKaTv+EG8ZTr75az7GFXcLYk9UyLEg7Notv/Ma+z/UG3TCoz6GiNeOrn1E/e63I0X/Hpw18jHOTUnA==",
+ "dev": true
+ },
+ "parallel-transform": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz",
+ "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=",
+ "dev": true,
+ "requires": {
+ "cyclist": "~0.2.2",
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.1.5"
+ }
+ },
+ "parchment": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz",
+ "integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg=="
+ },
+ "parse-asn1": {
+ "version": "5.1.4",
+ "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz",
+ "integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==",
+ "dev": true,
+ "requires": {
+ "asn1.js": "^4.0.0",
+ "browserify-aes": "^1.0.0",
+ "create-hash": "^1.1.0",
+ "evp_bytestokey": "^1.0.0",
+ "pbkdf2": "^3.0.3",
+ "safe-buffer": "^5.1.1"
+ }
+ },
+ "parse-author": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/parse-author/-/parse-author-2.0.0.tgz",
+ "integrity": "sha1-00YL8d3Q367tQtp1QkLmX7aEqB8=",
+ "dev": true,
+ "requires": {
+ "author-regex": "^1.0.0"
+ }
+ },
+ "parse-json": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+ "dev": true,
+ "requires": {
+ "error-ex": "^1.2.0"
+ }
+ },
+ "parse5": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz",
+ "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==",
+ "dev": true
+ },
+ "parseqs": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
+ "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=",
+ "dev": true,
+ "requires": {
+ "better-assert": "~1.0.0"
+ }
+ },
+ "parseuri": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz",
+ "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=",
+ "dev": true,
+ "requires": {
+ "better-assert": "~1.0.0"
+ }
+ },
+ "parseurl": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
+ "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=",
+ "dev": true
+ },
+ "pascalcase": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
+ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
+ "dev": true
+ },
+ "path-browserify": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz",
+ "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=",
+ "dev": true
+ },
+ "path-dirname": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
+ "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
+ "dev": true
+ },
+ "path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+ "dev": true
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
+ },
+ "path-is-inside": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+ "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
+ "dev": true
+ },
+ "path-key": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+ "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
+ },
+ "path-to-regexp": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+ "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=",
+ "dev": true
+ },
+ "path-type": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
+ "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
+ "dev": true,
+ "requires": {
+ "pify": "^3.0.0"
+ }
+ },
+ "pbkdf2": {
+ "version": "3.0.17",
+ "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz",
+ "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==",
+ "dev": true,
+ "requires": {
+ "create-hash": "^1.1.2",
+ "create-hmac": "^1.1.4",
+ "ripemd160": "^2.0.1",
+ "safe-buffer": "^5.0.1",
+ "sha.js": "^2.4.8"
+ }
+ },
+ "pend": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+ "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=",
+ "dev": true
+ },
+ "perfect-scrollbar": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/perfect-scrollbar/-/perfect-scrollbar-1.4.0.tgz",
+ "integrity": "sha512-/2Sk/khljhdrsamjJYS5NjrH+GKEHEwh7zFSiYyxROyYKagkE4kSn2zDQDRTOMo8mpT2jikxx6yI1dG7lNP/hw=="
+ },
+ "performance-now": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
+ "dev": true
+ },
+ "picocolors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
+ "dev": true
+ },
+ "picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true
+ },
+ "pify": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+ "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
+ },
+ "pinkie": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+ "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
+ "dev": true
+ },
+ "pinkie-promise": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
+ "dev": true,
+ "requires": {
+ "pinkie": "^2.0.0"
+ }
+ },
+ "pkg-dir": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
+ "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
+ "dev": true,
+ "requires": {
+ "find-up": "^2.1.0"
+ }
+ },
+ "plist": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/plist/-/plist-3.0.1.tgz",
+ "integrity": "sha512-GpgvHHocGRyQm74b6FWEZZVRroHKE1I0/BTjAmySaohK+cUn+hZpbqXkc3KWgW3gQYkqcQej35FohcT0FRlkRQ==",
+ "dev": true,
+ "requires": {
+ "base64-js": "^1.2.3",
+ "xmlbuilder": "^9.0.7",
+ "xmldom": "0.1.x"
+ }
+ },
+ "portfinder": {
+ "version": "1.0.20",
+ "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.20.tgz",
+ "integrity": "sha512-Yxe4mTyDzTd59PZJY4ojZR8F+E5e97iq2ZOHPz3HDgSvYC5siNad2tLooQ5y5QHyQhc3xVqvyk/eNA3wuoa7Sw==",
+ "dev": true,
+ "requires": {
+ "async": "^1.5.2",
+ "debug": "^2.2.0",
+ "mkdirp": "0.5.x"
+ },
+ "dependencies": {
+ "async": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
+ "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
+ "dev": true
+ }
+ }
+ },
+ "positioning": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/positioning/-/positioning-1.4.0.tgz",
+ "integrity": "sha512-LbN+mgAXtcDdN46xMJ3yZwjndqqYJODaO5qKmU+MVMu5tL3K2dlm1Qha/zh1k2JAFym5HDaZpnPfO4gr91VTRw=="
+ },
+ "posix-character-classes": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
+ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
+ "dev": true
+ },
+ "postcss": {
+ "version": "7.0.14",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.14.tgz",
+ "integrity": "sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.4.2",
+ "source-map": "^0.6.1",
+ "supports-color": "^6.1.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "postcss-import": {
+ "version": "12.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-12.0.1.tgz",
+ "integrity": "sha512-3Gti33dmCjyKBgimqGxL3vcV8w9+bsHwO5UrBawp796+jdardbcFl4RP5w/76BwNL7aGzpKstIfF9I+kdE8pTw==",
+ "dev": true,
+ "requires": {
+ "postcss": "^7.0.1",
+ "postcss-value-parser": "^3.2.3",
+ "read-cache": "^1.0.0",
+ "resolve": "^1.1.7"
+ }
+ },
+ "postcss-load-config": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.0.0.tgz",
+ "integrity": "sha512-V5JBLzw406BB8UIfsAWSK2KSwIJ5yoEIVFb4gVkXci0QdKgA24jLmHZ/ghe/GgX0lJ0/D1uUK1ejhzEY94MChQ==",
+ "dev": true,
+ "requires": {
+ "cosmiconfig": "^4.0.0",
+ "import-cwd": "^2.0.0"
+ }
+ },
+ "postcss-loader": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz",
+ "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==",
+ "dev": true,
+ "requires": {
+ "loader-utils": "^1.1.0",
+ "postcss": "^7.0.0",
+ "postcss-load-config": "^2.0.0",
+ "schema-utils": "^1.0.0"
+ }
+ },
+ "postcss-value-parser": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+ "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+ "dev": true
+ },
+ "prepend-http": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz",
+ "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=",
+ "dev": true
+ },
+ "pretty-bytes": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-1.0.4.tgz",
+ "integrity": "sha1-CiLoIQYJrTVUL4yNXSFZr/B1HIQ=",
+ "dev": true,
+ "requires": {
+ "get-stdin": "^4.0.1",
+ "meow": "^3.1.0"
+ }
+ },
+ "process": {
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+ "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=",
+ "dev": true
+ },
+ "process-nextick-args": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
+ "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
+ "dev": true
+ },
+ "progress": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz",
+ "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=",
+ "dev": true
+ },
+ "progress-stream": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/progress-stream/-/progress-stream-1.2.0.tgz",
+ "integrity": "sha1-LNPP6jO6OonJwSHsM0er6asSX3c=",
+ "dev": true,
+ "requires": {
+ "speedometer": "~0.1.2",
+ "through2": "~0.2.3"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+ "dev": true
+ },
+ "readable-stream": {
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+ "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "string_decoder": {
+ "version": "0.10.31",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
+ "dev": true
+ },
+ "through2": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-0.2.3.tgz",
+ "integrity": "sha1-6zKE2k6jEbbMis42U3SKUqvyWj8=",
+ "dev": true,
+ "requires": {
+ "readable-stream": "~1.1.9",
+ "xtend": "~2.1.1"
+ }
+ },
+ "xtend": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz",
+ "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=",
+ "dev": true,
+ "requires": {
+ "object-keys": "~0.4.0"
+ }
+ }
+ }
+ },
+ "promise": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
+ "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "asap": "~2.0.3"
+ }
+ },
+ "promise-inflight": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
+ "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
+ "dev": true
+ },
+ "promise-retry": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-1.1.1.tgz",
+ "integrity": "sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=",
+ "dev": true,
+ "requires": {
+ "err-code": "^1.0.0",
+ "retry": "^0.10.0"
+ }
+ },
+ "protobufjs": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-5.0.3.tgz",
+ "integrity": "sha512-55Kcx1MhPZX0zTbVosMQEO5R6/rikNXd9b6RQK4KSPcrSIIwoXTtebIczUrXlwaSrbz4x8XUVThGPob1n8I4QA==",
+ "requires": {
+ "ascli": "~1",
+ "bytebuffer": "~5",
+ "glob": "^7.0.5",
+ "yargs": "^3.10.0"
+ },
+ "dependencies": {
+ "y18n": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
+ "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE="
+ },
+ "yargs": {
+ "version": "3.32.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz",
+ "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=",
+ "requires": {
+ "camelcase": "^2.0.1",
+ "cliui": "^3.0.3",
+ "decamelize": "^1.1.1",
+ "os-locale": "^1.4.0",
+ "string-width": "^1.0.1",
+ "window-size": "^0.1.4",
+ "y18n": "^3.2.0"
+ }
+ }
+ }
+ },
+ "protoduck": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/protoduck/-/protoduck-5.0.1.tgz",
+ "integrity": "sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg==",
+ "dev": true,
+ "requires": {
+ "genfun": "^5.0.0"
+ }
+ },
+ "protractor": {
+ "version": "5.4.2",
+ "resolved": "https://registry.npmjs.org/protractor/-/protractor-5.4.2.tgz",
+ "integrity": "sha512-zlIj64Cr6IOWP7RwxVeD8O4UskLYPoyIcg0HboWJL9T79F1F0VWtKkGTr/9GN6BKL+/Q/GmM7C9kFVCfDbP5sA==",
+ "dev": true,
+ "requires": {
+ "@types/q": "^0.0.32",
+ "@types/selenium-webdriver": "^3.0.0",
+ "blocking-proxy": "^1.0.0",
+ "browserstack": "^1.5.1",
+ "chalk": "^1.1.3",
+ "glob": "^7.0.3",
+ "jasmine": "2.8.0",
+ "jasminewd2": "^2.1.0",
+ "optimist": "~0.6.0",
+ "q": "1.4.1",
+ "saucelabs": "^1.5.0",
+ "selenium-webdriver": "3.6.0",
+ "source-map-support": "~0.4.0",
+ "webdriver-js-extender": "2.1.0",
+ "webdriver-manager": "^12.0.6"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+ "dev": true
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "del": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz",
+ "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=",
+ "dev": true,
+ "requires": {
+ "globby": "^5.0.0",
+ "is-path-cwd": "^1.0.0",
+ "is-path-in-cwd": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0",
+ "rimraf": "^2.2.8"
+ }
+ },
+ "globby": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz",
+ "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=",
+ "dev": true,
+ "requires": {
+ "array-union": "^1.0.1",
+ "arrify": "^1.0.0",
+ "glob": "^7.0.3",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ },
+ "source-map-support": {
+ "version": "0.4.18",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
+ "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
+ "dev": true,
+ "requires": {
+ "source-map": "^0.5.6"
+ }
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+ "dev": true
+ },
+ "webdriver-manager": {
+ "version": "12.1.1",
+ "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.1.tgz",
+ "integrity": "sha512-L9TEQmZs6JbMMRQI1w60mfps265/NCr0toYJl7p/R2OAk6oXAfwI6jqYP7EWae+d7Ad2S2Aj4+rzxoSjqk3ZuA==",
+ "dev": true,
+ "requires": {
+ "adm-zip": "^0.4.9",
+ "chalk": "^1.1.1",
+ "del": "^2.2.0",
+ "glob": "^7.0.3",
+ "ini": "^1.3.4",
+ "minimist": "^1.2.0",
+ "q": "^1.4.1",
+ "request": "^2.87.0",
+ "rimraf": "^2.5.2",
+ "semver": "^5.3.0",
+ "xml2js": "^0.4.17"
+ }
+ }
+ }
+ },
+ "proxy-addr": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz",
+ "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==",
+ "dev": true,
+ "requires": {
+ "forwarded": "~0.1.2",
+ "ipaddr.js": "1.8.0"
+ }
+ },
+ "prr": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
+ "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
+ "dev": true
+ },
+ "pseudomap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
+ "dev": true
+ },
+ "psl": {
+ "version": "1.1.31",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz",
+ "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==",
+ "dev": true
+ },
+ "public-encrypt": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
+ "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.1.0",
+ "browserify-rsa": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "parse-asn1": "^5.0.0",
+ "randombytes": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "pump": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
+ "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "pumpify": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz",
+ "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
+ "dev": true,
+ "requires": {
+ "duplexify": "^3.6.0",
+ "inherits": "^2.0.3",
+ "pump": "^2.0.0"
+ }
+ },
+ "punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "dev": true
+ },
+ "q": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz",
+ "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=",
+ "dev": true
+ },
+ "qjobs": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz",
+ "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==",
+ "dev": true
+ },
+ "qs": {
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
+ "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
+ "dev": true
+ },
+ "querystring": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
+ "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
+ },
+ "querystring-es3": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
+ "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=",
+ "dev": true
+ },
+ "quill": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/quill/-/quill-1.3.6.tgz",
+ "integrity": "sha512-K0mvhimWZN6s+9OQ249CH2IEPZ9JmkFuCQeHAOQax3EZ2nDJ3wfGh59mnlQaZV2i7u8eFarx6wAtvQKgShojug==",
+ "requires": {
+ "clone": "^2.1.1",
+ "deep-equal": "^1.0.1",
+ "eventemitter3": "^2.0.3",
+ "extend": "^3.0.1",
+ "parchment": "^1.1.4",
+ "quill-delta": "^3.6.2"
+ },
+ "dependencies": {
+ "eventemitter3": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz",
+ "integrity": "sha1-teEHm1n7XhuidxwKmTvgYKWMmbo="
+ }
+ }
+ },
+ "quill-delta": {
+ "version": "3.6.3",
+ "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz",
+ "integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==",
+ "requires": {
+ "deep-equal": "^1.0.1",
+ "extend": "^3.0.2",
+ "fast-diff": "1.1.2"
+ }
+ },
+ "randombytes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "randomfill": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz",
+ "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==",
+ "dev": true,
+ "requires": {
+ "randombytes": "^2.0.5",
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "range-parser": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
+ "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=",
+ "dev": true
+ },
+ "raw-body": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz",
+ "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==",
+ "dev": true,
+ "requires": {
+ "bytes": "3.0.0",
+ "http-errors": "1.6.3",
+ "iconv-lite": "0.4.23",
+ "unpipe": "1.0.0"
+ }
+ },
+ "raw-loader": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-1.0.0.tgz",
+ "integrity": "sha512-Uqy5AqELpytJTRxYT4fhltcKPj0TyaEpzJDcGz7DFJi+pQOOi3GjR/DOdxTkTsF+NzhnldIoG6TORaBlInUuqA==",
+ "dev": true,
+ "requires": {
+ "loader-utils": "^1.1.0",
+ "schema-utils": "^1.0.0"
+ }
+ },
+ "rc": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "dev": true,
+ "requires": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ }
+ },
+ "rcedit": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/rcedit/-/rcedit-2.0.0.tgz",
+ "integrity": "sha512-XcFGyEBjhWSsud+R8elwQtGBbVkCf7tAiad+nXo5jc6l2rMf46NfGNwjnmBNneBIZDfq+Npf8lwP371JTONfrw==",
+ "dev": true
+ },
+ "read-cache": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
+ "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=",
+ "dev": true,
+ "requires": {
+ "pify": "^2.3.0"
+ },
+ "dependencies": {
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ }
+ }
+ },
+ "read-config-file": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-5.0.0.tgz",
+ "integrity": "sha512-jIKUu+C84bfnKxyJ5j30CxCqgXWYjZLXuVE/NYlMEpeni+dhESgAeZOZd0JZbg1xTkMmnCdxksDoarkOyfEsOg==",
+ "dev": true,
+ "requires": {
+ "dotenv": "^8.0.0",
+ "dotenv-expand": "^5.1.0",
+ "fs-extra": "^8.1.0",
+ "js-yaml": "^3.13.1",
+ "json5": "^2.1.0",
+ "lazy-val": "^1.0.4"
+ },
+ "dependencies": {
+ "fs-extra": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.1.tgz",
+ "integrity": "sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw==",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.13.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+ "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "json5": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz",
+ "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.0"
+ }
+ }
+ }
+ },
+ "read-pkg": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
+ "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
+ "dev": true,
+ "requires": {
+ "load-json-file": "^1.0.0",
+ "normalize-package-data": "^2.3.2",
+ "path-type": "^1.0.0"
+ },
+ "dependencies": {
+ "path-type": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
+ "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ }
+ }
+ },
+ "read-pkg-up": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
+ "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
+ "dev": true,
+ "requires": {
+ "find-up": "^1.0.0",
+ "read-pkg": "^1.0.0"
+ },
+ "dependencies": {
+ "find-up": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
+ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
+ "dev": true,
+ "requires": {
+ "path-exists": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "path-exists": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
+ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
+ "dev": true,
+ "requires": {
+ "pinkie-promise": "^2.0.0"
+ }
+ }
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "readdirp": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
+ "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.11",
+ "micromatch": "^3.1.10",
+ "readable-stream": "^2.0.2"
+ }
+ },
+ "rechoir": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
+ "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
+ "dev": true,
+ "requires": {
+ "resolve": "^1.1.6"
+ }
+ },
+ "redent": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
+ "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=",
+ "dev": true,
+ "requires": {
+ "indent-string": "^2.1.0",
+ "strip-indent": "^1.0.1"
+ }
+ },
+ "reflect-metadata": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
+ "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==",
+ "dev": true
+ },
+ "regenerate": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz",
+ "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==",
+ "dev": true
+ },
+ "regenerator-runtime": {
+ "version": "0.11.1",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
+ "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
+ },
+ "regex-not": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
+ "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^3.0.2",
+ "safe-regex": "^1.1.0"
+ }
+ },
+ "regexpu-core": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz",
+ "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=",
+ "dev": true,
+ "requires": {
+ "regenerate": "^1.2.1",
+ "regjsgen": "^0.2.0",
+ "regjsparser": "^0.1.4"
+ }
+ },
+ "registry-auth-token": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.0.0.tgz",
+ "integrity": "sha512-lpQkHxd9UL6tb3k/aHAVfnVtn+Bcs9ob5InuFLLEDqSqeq+AljB8GZW9xY0x7F+xYwEcjKe07nyoxzEYz6yvkw==",
+ "dev": true,
+ "requires": {
+ "rc": "^1.2.8",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "registry-url": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz",
+ "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==",
+ "dev": true,
+ "requires": {
+ "rc": "^1.2.8"
+ }
+ },
+ "regjsgen": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz",
+ "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=",
+ "dev": true
+ },
+ "regjsparser": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz",
+ "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=",
+ "dev": true,
+ "requires": {
+ "jsesc": "~0.5.0"
+ },
+ "dependencies": {
+ "jsesc": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
+ "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=",
+ "dev": true
+ }
+ }
+ },
+ "remove-trailing-separator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
+ "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
+ "dev": true
+ },
+ "repeat-element": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz",
+ "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==",
+ "dev": true
+ },
+ "repeat-string": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
+ "dev": true
+ },
+ "repeating": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
+ "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
+ "dev": true,
+ "requires": {
+ "is-finite": "^1.0.0"
+ }
+ },
+ "request": {
+ "version": "2.88.0",
+ "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
+ "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
+ "dev": true,
+ "requires": {
+ "aws-sign2": "~0.7.0",
+ "aws4": "^1.8.0",
+ "caseless": "~0.12.0",
+ "combined-stream": "~1.0.6",
+ "extend": "~3.0.2",
+ "forever-agent": "~0.6.1",
+ "form-data": "~2.3.2",
+ "har-validator": "~5.1.0",
+ "http-signature": "~1.2.0",
+ "is-typedarray": "~1.0.0",
+ "isstream": "~0.1.2",
+ "json-stringify-safe": "~5.0.1",
+ "mime-types": "~2.1.19",
+ "oauth-sign": "~0.9.0",
+ "performance-now": "^2.1.0",
+ "qs": "~6.5.2",
+ "safe-buffer": "^5.1.2",
+ "tough-cookie": "~2.4.3",
+ "tunnel-agent": "^0.6.0",
+ "uuid": "^3.3.2"
+ }
+ },
+ "request-progress": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz",
+ "integrity": "sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4=",
+ "dev": true,
+ "requires": {
+ "throttleit": "^1.0.0"
+ },
+ "dependencies": {
+ "throttleit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz",
+ "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=",
+ "dev": true
+ }
+ }
+ },
+ "require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+ "dev": true
+ },
+ "require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "dev": true
+ },
+ "require-main-filename": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
+ "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
+ "dev": true
+ },
+ "requires-port": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+ "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
+ "dev": true
+ },
+ "resize-observer-polyfill": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
+ "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
+ },
+ "resolve": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz",
+ "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==",
+ "dev": true,
+ "requires": {
+ "path-parse": "^1.0.6"
+ }
+ },
+ "resolve-cwd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz",
+ "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=",
+ "dev": true,
+ "requires": {
+ "resolve-from": "^3.0.0"
+ }
+ },
+ "resolve-from": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
+ "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
+ "dev": true
+ },
+ "resolve-url": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
+ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
+ "dev": true
+ },
+ "responselike": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz",
+ "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=",
+ "dev": true,
+ "requires": {
+ "lowercase-keys": "^1.0.0"
+ }
+ },
+ "restore-cursor": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
+ "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
+ "dev": true,
+ "requires": {
+ "onetime": "^2.0.0",
+ "signal-exit": "^3.0.2"
+ }
+ },
+ "ret": {
+ "version": "0.1.15",
+ "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
+ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
+ "dev": true
+ },
+ "retry": {
+ "version": "0.10.1",
+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz",
+ "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=",
+ "dev": true
+ },
+ "rfdc": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.2.tgz",
+ "integrity": "sha512-92ktAgvZhBzYTIK0Mja9uen5q5J3NRVMoDkJL2VMwq6SXjVCgqvQeVP2XAaUY6HT+XpQYeLSjb3UoitBryKmdA==",
+ "dev": true
+ },
+ "rimraf": {
+ "version": "2.6.3",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
+ "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "ripemd160": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
+ "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
+ "dev": true,
+ "requires": {
+ "hash-base": "^3.0.0",
+ "inherits": "^2.0.1"
+ }
+ },
+ "run-async": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
+ "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
+ "dev": true,
+ "requires": {
+ "is-promise": "^2.1.0"
+ }
+ },
+ "run-queue": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
+ "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=",
+ "dev": true,
+ "requires": {
+ "aproba": "^1.1.1"
+ }
+ },
+ "rxjs": {
+ "version": "6.3.3",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz",
+ "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==",
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "rxjs-compat": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/rxjs-compat/-/rxjs-compat-6.1.0.tgz",
+ "integrity": "sha512-x5L1KQy1RqDRpPadN5iDOx71TV9Wqmlmu6OOEn3tFFgaTCB0/N+Lmby/rZHgJ6JEPzzt0nD9Zv+kS53E5JIR5g=="
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ },
+ "safe-regex": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
+ "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
+ "dev": true,
+ "requires": {
+ "ret": "~0.1.10"
+ }
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
+ },
+ "sanitize-filename": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.1.tgz",
+ "integrity": "sha1-YS2hyWRz+gLczaktzVtKsWSmdyo=",
+ "dev": true,
+ "requires": {
+ "truncate-utf8-bytes": "^1.0.0"
+ }
+ },
+ "sass": {
+ "version": "1.49.0",
+ "resolved": "https://registry.npmjs.org/sass/-/sass-1.49.0.tgz",
+ "integrity": "sha512-TVwVdNDj6p6b4QymJtNtRS2YtLJ/CqZriGg0eIAbAKMlN8Xy6kbv33FsEZSF7FufFFM705SQviHjjThfaQ4VNw==",
+ "dev": true,
+ "requires": {
+ "chokidar": ">=3.0.0 <4.0.0",
+ "immutable": "^4.0.0",
+ "source-map-js": ">=0.6.2 <2.0.0"
+ },
+ "dependencies": {
+ "anymatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
+ "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+ "dev": true,
+ "requires": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ }
+ },
+ "binary-extensions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "dev": true
+ },
+ "braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "requires": {
+ "fill-range": "^7.0.1"
+ }
+ },
+ "chokidar": {
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+ "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+ "dev": true,
+ "requires": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "fsevents": "~2.3.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ }
+ },
+ "fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "requires": {
+ "to-regex-range": "^5.0.1"
+ }
+ },
+ "fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "optional": true
+ },
+ "glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ },
+ "is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "requires": {
+ "binary-extensions": "^2.0.0"
+ }
+ },
+ "is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true
+ },
+ "normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true
+ },
+ "readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "requires": {
+ "picomatch": "^2.2.1"
+ }
+ },
+ "to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "requires": {
+ "is-number": "^7.0.0"
+ }
+ }
+ }
+ },
+ "sass-graph": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz",
+ "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "glob": "^7.0.0",
+ "lodash": "^4.0.0",
+ "scss-tokenizer": "^0.2.3",
+ "yargs": "^7.0.0"
+ }
+ },
+ "sass-loader": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.1.0.tgz",
+ "integrity": "sha512-+G+BKGglmZM2GUSfT9TLuEp6tzehHPjAMoRRItOojWIqIGPloVCMhNIQuG639eJ+y033PaGTSjLaTHts8Kw79w==",
+ "dev": true,
+ "requires": {
+ "clone-deep": "^2.0.1",
+ "loader-utils": "^1.0.1",
+ "lodash.tail": "^4.1.1",
+ "neo-async": "^2.5.0",
+ "pify": "^3.0.0",
+ "semver": "^5.5.0"
+ }
+ },
+ "saucelabs": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz",
+ "integrity": "sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ==",
+ "dev": true,
+ "requires": {
+ "https-proxy-agent": "^2.2.1"
+ }
+ },
+ "sax": {
+ "version": "0.5.8",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-0.5.8.tgz",
+ "integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=",
+ "dev": true
+ },
+ "schema-utils": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+ "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.1.0",
+ "ajv-errors": "^1.0.0",
+ "ajv-keywords": "^3.1.0"
+ }
+ },
+ "scss-tokenizer": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz",
+ "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "js-base64": "^2.1.8",
+ "source-map": "^0.4.2"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
+ "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "amdefine": ">=0.0.4"
+ }
+ }
+ }
+ },
+ "select-hose": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
+ "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=",
+ "dev": true
+ },
+ "selenium-webdriver": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz",
+ "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==",
+ "dev": true,
+ "requires": {
+ "jszip": "^3.1.3",
+ "rimraf": "^2.5.4",
+ "tmp": "0.0.30",
+ "xml2js": "^0.4.17"
+ },
+ "dependencies": {
+ "tmp": {
+ "version": "0.0.30",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz",
+ "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=",
+ "dev": true,
+ "requires": {
+ "os-tmpdir": "~1.0.1"
+ }
+ }
+ }
+ },
+ "selfsigned": {
+ "version": "1.10.4",
+ "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.4.tgz",
+ "integrity": "sha512-9AukTiDmHXGXWtWjembZ5NDmVvP2695EtpgbCsxCa68w3c88B+alqbmZ4O3hZ4VWGXeGWzEVdvqgAJD8DQPCDw==",
+ "dev": true,
+ "requires": {
+ "node-forge": "0.7.5"
+ }
+ },
+ "semver": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
+ "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
+ "dev": true
+ },
+ "semver-diff": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz",
+ "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=",
+ "dev": true,
+ "requires": {
+ "semver": "^5.0.3"
+ }
+ },
+ "semver-dsl": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/semver-dsl/-/semver-dsl-1.0.1.tgz",
+ "integrity": "sha1-02eN5VVeimH2Ke7QJTZq5fJzQKA=",
+ "dev": true,
+ "requires": {
+ "semver": "^5.3.0"
+ }
+ },
+ "semver-intersect": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/semver-intersect/-/semver-intersect-1.4.0.tgz",
+ "integrity": "sha512-d8fvGg5ycKAq0+I6nfWeCx6ffaWJCsBYU0H2Rq56+/zFePYfT8mXkB3tWBSjR5BerkHNZ5eTPIk1/LBYas35xQ==",
+ "dev": true,
+ "requires": {
+ "semver": "^5.0.0"
+ }
+ },
+ "send": {
+ "version": "0.16.2",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
+ "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "destroy": "~1.0.4",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "http-errors": "~1.6.2",
+ "mime": "1.4.1",
+ "ms": "2.0.0",
+ "on-finished": "~2.3.0",
+ "range-parser": "~1.2.0",
+ "statuses": "~1.4.0"
+ },
+ "dependencies": {
+ "mime": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
+ "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==",
+ "dev": true
+ }
+ }
+ },
+ "serialize-javascript": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.6.1.tgz",
+ "integrity": "sha512-A5MOagrPFga4YaKQSWHryl7AXvbQkEqpw4NNYMTNYUNV51bA8ABHgYFpqKx+YFFrw59xMV1qGH1R4AgoNIVgCw==",
+ "dev": true
+ },
+ "serve-index": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
+ "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=",
+ "dev": true,
+ "requires": {
+ "accepts": "~1.3.4",
+ "batch": "0.6.1",
+ "debug": "2.6.9",
+ "escape-html": "~1.0.3",
+ "http-errors": "~1.6.2",
+ "mime-types": "~2.1.17",
+ "parseurl": "~1.3.2"
+ }
+ },
+ "serve-static": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
+ "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
+ "dev": true,
+ "requires": {
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.2",
+ "send": "0.16.2"
+ }
+ },
+ "set-blocking": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
+ "dev": true
+ },
+ "set-immediate-shim": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
+ "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=",
+ "dev": true
+ },
+ "set-value": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
+ "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-extendable": "^0.1.1",
+ "is-plain-object": "^2.0.3",
+ "split-string": "^3.0.1"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "setimmediate": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+ "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=",
+ "dev": true
+ },
+ "setprototypeof": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
+ "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
+ "dev": true
+ },
+ "sha.js": {
+ "version": "2.4.11",
+ "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
+ "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "shallow-clone": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-1.0.0.tgz",
+ "integrity": "sha512-oeXreoKR/SyNJtRJMAKPDSvd28OqEwG4eR/xc856cRGBII7gX9lvAqDxusPm0846z/w/hWYjI1NpKwJ00NHzRA==",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.1",
+ "kind-of": "^5.0.0",
+ "mixin-object": "^2.0.1"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true
+ }
+ }
+ },
+ "shebang-command": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+ "dev": true,
+ "requires": {
+ "shebang-regex": "^1.0.0"
+ }
+ },
+ "shebang-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+ "dev": true
+ },
+ "shelljs": {
+ "version": "0.8.5",
+ "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz",
+ "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.0.0",
+ "interpret": "^1.0.0",
+ "rechoir": "^0.6.2"
+ }
+ },
+ "signal-exit": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
+ },
+ "single-line-log": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/single-line-log/-/single-line-log-1.1.2.tgz",
+ "integrity": "sha1-wvg/Jzo+GhbtsJlWYdoO1e8DM2Q=",
+ "dev": true,
+ "requires": {
+ "string-width": "^1.0.1"
+ }
+ },
+ "slash": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
+ "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=",
+ "dev": true
+ },
+ "smart-buffer": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.0.2.tgz",
+ "integrity": "sha512-JDhEpTKzXusOqXZ0BUIdH+CjFdO/CR3tLlf5CN34IypI+xMmXW1uB16OOY8z3cICbJlDAVJzNbwBhNO0wt9OAw==",
+ "dev": true
+ },
+ "snapdragon": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
+ "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
+ "dev": true,
+ "requires": {
+ "base": "^0.11.1",
+ "debug": "^2.2.0",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "map-cache": "^0.2.2",
+ "source-map": "^0.5.6",
+ "source-map-resolve": "^0.5.0",
+ "use": "^3.1.0"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ }
+ }
+ },
+ "snapdragon-node": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
+ "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
+ "dev": true,
+ "requires": {
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.0",
+ "snapdragon-util": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ }
+ }
+ },
+ "snapdragon-util": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
+ "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.2.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "socket.io": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.1.1.tgz",
+ "integrity": "sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA==",
+ "dev": true,
+ "requires": {
+ "debug": "~3.1.0",
+ "engine.io": "~3.2.0",
+ "has-binary2": "~1.0.2",
+ "socket.io-adapter": "~1.1.0",
+ "socket.io-client": "2.1.1",
+ "socket.io-parser": "~3.2.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ }
+ }
+ },
+ "socket.io-adapter": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz",
+ "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs=",
+ "dev": true
+ },
+ "socket.io-client": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz",
+ "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==",
+ "dev": true,
+ "requires": {
+ "backo2": "1.0.2",
+ "base64-arraybuffer": "0.1.5",
+ "component-bind": "1.0.0",
+ "component-emitter": "1.2.1",
+ "debug": "~3.1.0",
+ "engine.io-client": "~3.2.0",
+ "has-binary2": "~1.0.2",
+ "has-cors": "1.1.0",
+ "indexof": "0.0.1",
+ "object-component": "0.0.3",
+ "parseqs": "0.0.5",
+ "parseuri": "0.0.5",
+ "socket.io-parser": "~3.2.0",
+ "to-array": "0.1.4"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ }
+ }
+ },
+ "socket.io-parser": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz",
+ "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==",
+ "dev": true,
+ "requires": {
+ "component-emitter": "1.2.1",
+ "debug": "~3.1.0",
+ "isarray": "2.0.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "isarray": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
+ "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=",
+ "dev": true
+ }
+ }
+ },
+ "sockjs": {
+ "version": "0.3.19",
+ "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz",
+ "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==",
+ "dev": true,
+ "requires": {
+ "faye-websocket": "^0.10.0",
+ "uuid": "^3.0.1"
+ }
+ },
+ "sockjs-client": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.3.0.tgz",
+ "integrity": "sha512-R9jxEzhnnrdxLCNln0xg5uGHqMnkhPSTzUZH2eXcR03S/On9Yvoq2wyUZILRUhZCNVu2PmwWVoyuiPz8th8zbg==",
+ "dev": true,
+ "requires": {
+ "debug": "^3.2.5",
+ "eventsource": "^1.0.7",
+ "faye-websocket": "~0.11.1",
+ "inherits": "^2.0.3",
+ "json3": "^3.3.2",
+ "url-parse": "^1.4.3"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "faye-websocket": {
+ "version": "0.11.1",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz",
+ "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=",
+ "dev": true,
+ "requires": {
+ "websocket-driver": ">=0.5.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+ "dev": true
+ }
+ }
+ },
+ "socks": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/socks/-/socks-2.2.3.tgz",
+ "integrity": "sha512-+2r83WaRT3PXYoO/1z+RDEBE7Z2f9YcdQnJ0K/ncXXbV5gJ6wYfNAebYFYiiUjM6E4JyXnPY8cimwyvFYHVUUA==",
+ "dev": true,
+ "requires": {
+ "ip": "^1.1.5",
+ "smart-buffer": "4.0.2"
+ }
+ },
+ "socks-proxy-agent": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.1.tgz",
+ "integrity": "sha512-Kezx6/VBguXOsEe5oU3lXYyKMi4+gva72TwJ7pQY5JfqUx2nMk7NXA6z/mpNqIlfQjWYVfeuNvQjexiTaTn6Nw==",
+ "dev": true,
+ "requires": {
+ "agent-base": "~4.2.0",
+ "socks": "~2.2.0"
+ }
+ },
+ "source-list-map": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
+ "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
+ "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
+ "dev": true
+ },
+ "source-map-js": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+ "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
+ "dev": true
+ },
+ "source-map-loader": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-0.2.4.tgz",
+ "integrity": "sha512-OU6UJUty+i2JDpTItnizPrlpOIBLmQbWMuBg9q5bVtnHACqw1tn9nNwqJLbv0/00JjnJb/Ee5g5WS5vrRv7zIQ==",
+ "dev": true,
+ "requires": {
+ "async": "^2.5.0",
+ "loader-utils": "^1.1.0"
+ },
+ "dependencies": {
+ "async": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz",
+ "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.11"
+ }
+ }
+ }
+ },
+ "source-map-resolve": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz",
+ "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==",
+ "dev": true,
+ "requires": {
+ "atob": "^2.1.1",
+ "decode-uri-component": "^0.2.0",
+ "resolve-url": "^0.2.1",
+ "source-map-url": "^0.4.0",
+ "urix": "^0.1.0"
+ }
+ },
+ "source-map-support": {
+ "version": "0.5.10",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.10.tgz",
+ "integrity": "sha512-YfQ3tQFTK/yzlGJuX8pTwa4tifQj4QS2Mj7UegOu8jAz59MqIiMGPXxQhVQiIMNzayuUSF/jEuVnfFF5JqybmQ==",
+ "dev": true,
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "source-map-url": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
+ "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
+ "dev": true
+ },
+ "sourcemap-codec": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.4.tgz",
+ "integrity": "sha512-CYAPYdBu34781kLHkaW3m6b/uUSyMOC2R61gcYMWooeuaGtjof86ZA/8T+qVPPt7np1085CR9hmMGrySwEc8Xg==",
+ "dev": true
+ },
+ "spdx-correct": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz",
+ "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==",
+ "dev": true,
+ "requires": {
+ "spdx-expression-parse": "^3.0.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-exceptions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz",
+ "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==",
+ "dev": true
+ },
+ "spdx-expression-parse": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
+ "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
+ "dev": true,
+ "requires": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-license-ids": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz",
+ "integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==",
+ "dev": true
+ },
+ "spdy": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.0.tgz",
+ "integrity": "sha512-ot0oEGT/PGUpzf/6uk4AWLqkq+irlqHXkrdbk51oWONh3bxQmBuljxPNl66zlRRcIJStWq0QkLUCPOPjgjvU0Q==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.0",
+ "handle-thing": "^2.0.0",
+ "http-deceiver": "^1.2.7",
+ "select-hose": "^2.0.0",
+ "spdy-transport": "^3.0.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+ "dev": true
+ }
+ }
+ },
+ "spdy-transport": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz",
+ "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.0",
+ "detect-node": "^2.0.4",
+ "hpack.js": "^2.1.6",
+ "obuf": "^1.1.2",
+ "readable-stream": "^3.0.6",
+ "wbuf": "^1.7.3"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+ "dev": true
+ },
+ "readable-stream": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.1.1.tgz",
+ "integrity": "sha512-DkN66hPyqDhnIQ6Jcsvx9bFjhw214O4poMBcIMgPVpQvNy9a0e0Uhg5SqySyDKAmUlwt8LonTBz1ezOnM8pUdA==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ }
+ }
+ },
+ "speed-measure-webpack-plugin": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.3.0.tgz",
+ "integrity": "sha512-b9Yd0TrzceMVYSbuamM1sFsGM1oVfyFTM22gOoyLhymNvBVApuYpkdFOgYkKJpN/KhTpcCYcTGHg7X+FJ33Vvw==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.0.1"
+ }
+ },
+ "speedometer": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/speedometer/-/speedometer-0.1.4.tgz",
+ "integrity": "sha1-mHbb0qFp0xFUAtSObqYynIgWpQ0=",
+ "dev": true
+ },
+ "split-string": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
+ "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^3.0.0"
+ }
+ },
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ },
+ "sshpk": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
+ "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
+ "dev": true,
+ "requires": {
+ "asn1": "~0.2.3",
+ "assert-plus": "^1.0.0",
+ "bcrypt-pbkdf": "^1.0.0",
+ "dashdash": "^1.12.0",
+ "ecc-jsbn": "~0.1.1",
+ "getpass": "^0.1.1",
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.0.2",
+ "tweetnacl": "~0.14.0"
+ }
+ },
+ "ssri": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz",
+ "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.1.1"
+ }
+ },
+ "stat-mode": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-0.3.0.tgz",
+ "integrity": "sha512-QjMLR0A3WwFY2aZdV0okfFEJB5TRjkggXZjxP3A1RsWsNHNu3YPv8btmtc6iCFZ0Rul3FE93OYogvhOUClU+ng==",
+ "dev": true
+ },
+ "static-extend": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
+ "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
+ "dev": true,
+ "requires": {
+ "define-property": "^0.2.5",
+ "object-copy": "^0.1.0"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ }
+ }
+ },
+ "stats-webpack-plugin": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/stats-webpack-plugin/-/stats-webpack-plugin-0.7.0.tgz",
+ "integrity": "sha512-NT0YGhwuQ0EOX+uPhhUcI6/+1Sq/pMzNuSCBVT4GbFl/ac6I/JZefBcjlECNfAb1t3GOx5dEj1Z7x0cAxeeVLQ==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.4"
+ }
+ },
+ "stats.js": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/stats.js/-/stats.js-0.17.0.tgz",
+ "integrity": "sha1-scPcRtlEmLV4t/05hbgaznExzH0="
+ },
+ "statuses": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
+ "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==",
+ "dev": true
+ },
+ "stdout-stream": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz",
+ "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "readable-stream": "^2.0.1"
+ }
+ },
+ "stream-browserify": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz",
+ "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==",
+ "dev": true,
+ "requires": {
+ "inherits": "~2.0.1",
+ "readable-stream": "^2.0.2"
+ }
+ },
+ "stream-each": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz",
+ "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "stream-shift": "^1.0.0"
+ }
+ },
+ "stream-http": {
+ "version": "2.8.3",
+ "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz",
+ "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==",
+ "dev": true,
+ "requires": {
+ "builtin-status-codes": "^3.0.0",
+ "inherits": "^2.0.1",
+ "readable-stream": "^2.3.6",
+ "to-arraybuffer": "^1.0.0",
+ "xtend": "^4.0.0"
+ }
+ },
+ "stream-shift": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz",
+ "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=",
+ "dev": true
+ },
+ "streamroller": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-0.7.0.tgz",
+ "integrity": "sha512-WREzfy0r0zUqp3lGO096wRuUp7ho1X6uo/7DJfTlEi0Iv/4gT7YHqXDjKC2ioVGBZtE8QzsQD9nx1nIuoZ57jQ==",
+ "dev": true,
+ "requires": {
+ "date-format": "^1.2.0",
+ "debug": "^3.1.0",
+ "mkdirp": "^0.5.1",
+ "readable-stream": "^2.3.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+ "dev": true
+ }
+ }
+ },
+ "string-width": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+ "requires": {
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "strip-bom": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
+ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
+ "dev": true,
+ "requires": {
+ "is-utf8": "^0.2.0"
+ }
+ },
+ "strip-eof": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
+ "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
+ "dev": true
+ },
+ "strip-indent": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
+ "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=",
+ "dev": true,
+ "requires": {
+ "get-stdin": "^4.0.1"
+ }
+ },
+ "strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
+ "dev": true
+ },
+ "style-loader": {
+ "version": "0.23.1",
+ "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.23.1.tgz",
+ "integrity": "sha512-XK+uv9kWwhZMZ1y7mysB+zoihsEj4wneFWAS5qoiLwzW0WzSqMrrsIy+a3zkQJq0ipFtBpX5W3MqyRIBF/WFGg==",
+ "dev": true,
+ "requires": {
+ "loader-utils": "^1.1.0",
+ "schema-utils": "^1.0.0"
+ }
+ },
+ "stylus": {
+ "version": "0.54.5",
+ "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.5.tgz",
+ "integrity": "sha1-QrlWCTHKcJDOhRWnmLqeaqPW3Hk=",
+ "dev": true,
+ "requires": {
+ "css-parse": "1.7.x",
+ "debug": "*",
+ "glob": "7.0.x",
+ "mkdirp": "0.5.x",
+ "sax": "0.5.x",
+ "source-map": "0.1.x"
+ },
+ "dependencies": {
+ "glob": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz",
+ "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.2",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "source-map": {
+ "version": "0.1.43",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz",
+ "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=",
+ "dev": true,
+ "requires": {
+ "amdefine": ">=0.0.4"
+ }
+ }
+ }
+ },
+ "stylus-loader": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-3.0.2.tgz",
+ "integrity": "sha512-+VomPdZ6a0razP+zinir61yZgpw2NfljeSsdUF5kJuEzlo3khXhY19Fn6l8QQz1GRJGtMCo8nG5C04ePyV7SUA==",
+ "dev": true,
+ "requires": {
+ "loader-utils": "^1.0.2",
+ "lodash.clonedeep": "^4.5.0",
+ "when": "~3.6.x"
+ }
+ },
+ "sumchecker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-2.0.2.tgz",
+ "integrity": "sha1-D0LBDl0F2l1C7qPlbDOZo31sWz4=",
+ "dev": true,
+ "requires": {
+ "debug": "^2.2.0"
+ }
+ },
+ "supports-color": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+ "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "symbol-observable": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
+ "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==",
+ "dev": true
+ },
+ "tapable": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.1.tgz",
+ "integrity": "sha512-9I2ydhj8Z9veORCw5PRm4u9uebCn0mcCa6scWoNcbZ6dAtoo2618u9UUzxgmsCOreJpqDDuv61LvwofW7hLcBA==",
+ "dev": true
+ },
+ "tar": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz",
+ "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "block-stream": "*",
+ "fstream": "^1.0.2",
+ "inherits": "2"
+ }
+ },
+ "temp-file": {
+ "version": "3.3.4",
+ "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.3.4.tgz",
+ "integrity": "sha512-qSZ5W5q54iyGnP8cNl49RE0jTJc5CrzNocux5APD5yIxcgonoMuMSbsZfaZy8rTGCYo0Xz6ySVv3adagZ8gffg==",
+ "dev": true,
+ "requires": {
+ "async-exit-hook": "^2.0.1",
+ "fs-extra": "^8.1.0"
+ },
+ "dependencies": {
+ "fs-extra": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.1.tgz",
+ "integrity": "sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw==",
+ "dev": true
+ }
+ }
+ },
+ "term-size": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz",
+ "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=",
+ "dev": true,
+ "requires": {
+ "execa": "^0.7.0"
+ },
+ "dependencies": {
+ "cross-spawn": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
+ "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^4.0.1",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ }
+ },
+ "execa": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz",
+ "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^5.0.1",
+ "get-stream": "^3.0.0",
+ "is-stream": "^1.1.0",
+ "npm-run-path": "^2.0.0",
+ "p-finally": "^1.0.0",
+ "signal-exit": "^3.0.0",
+ "strip-eof": "^1.0.0"
+ }
+ }
+ }
+ },
+ "terser": {
+ "version": "3.16.1",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-3.16.1.tgz",
+ "integrity": "sha512-JDJjgleBROeek2iBcSNzOHLKsB/MdDf+E/BOAJ0Tk9r7p9/fVobfv7LMJ/g/k3v9SXdmjZnIlFd5nfn/Rt0Xow==",
+ "dev": true,
+ "requires": {
+ "commander": "~2.17.1",
+ "source-map": "~0.6.1",
+ "source-map-support": "~0.5.9"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "terser-webpack-plugin": {
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz",
+ "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==",
+ "dev": true,
+ "requires": {
+ "cacache": "^12.0.2",
+ "find-cache-dir": "^2.1.0",
+ "is-wsl": "^1.1.0",
+ "schema-utils": "^1.0.0",
+ "serialize-javascript": "^4.0.0",
+ "source-map": "^0.6.1",
+ "terser": "^4.1.2",
+ "webpack-sources": "^1.4.0",
+ "worker-farm": "^1.7.0"
+ },
+ "dependencies": {
+ "bluebird": {
+ "version": "3.7.2",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
+ "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
+ "dev": true
+ },
+ "cacache": {
+ "version": "12.0.4",
+ "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz",
+ "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==",
+ "dev": true,
+ "requires": {
+ "bluebird": "^3.5.5",
+ "chownr": "^1.1.1",
+ "figgy-pudding": "^3.5.1",
+ "glob": "^7.1.4",
+ "graceful-fs": "^4.1.15",
+ "infer-owner": "^1.0.3",
+ "lru-cache": "^5.1.1",
+ "mississippi": "^3.0.0",
+ "mkdirp": "^0.5.1",
+ "move-concurrently": "^1.0.1",
+ "promise-inflight": "^1.0.1",
+ "rimraf": "^2.6.3",
+ "ssri": "^6.0.1",
+ "unique-filename": "^1.1.1",
+ "y18n": "^4.0.0"
+ }
+ },
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "find-cache-dir": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz",
+ "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==",
+ "dev": true,
+ "requires": {
+ "commondir": "^1.0.1",
+ "make-dir": "^2.0.0",
+ "pkg-dir": "^3.0.0"
+ }
+ },
+ "find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^3.0.0"
+ }
+ },
+ "glob": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
+ "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "requires": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "make-dir": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
+ "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+ "dev": true,
+ "requires": {
+ "pify": "^4.0.1",
+ "semver": "^5.6.0"
+ }
+ },
+ "mississippi": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
+ "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==",
+ "dev": true,
+ "requires": {
+ "concat-stream": "^1.5.0",
+ "duplexify": "^3.4.2",
+ "end-of-stream": "^1.1.0",
+ "flush-write-stream": "^1.0.0",
+ "from2": "^2.1.0",
+ "parallel-transform": "^1.1.0",
+ "pump": "^3.0.0",
+ "pumpify": "^1.3.3",
+ "stream-each": "^1.1.0",
+ "through2": "^2.0.0"
+ }
+ },
+ "p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.0.0"
+ }
+ },
+ "p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true
+ },
+ "pify": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+ "dev": true
+ },
+ "pkg-dir": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz",
+ "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
+ "dev": true,
+ "requires": {
+ "find-up": "^3.0.0"
+ }
+ },
+ "pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "serialize-javascript": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
+ "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
+ "dev": true,
+ "requires": {
+ "randombytes": "^2.1.0"
+ }
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "dev": true,
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "ssri": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz",
+ "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==",
+ "dev": true,
+ "requires": {
+ "figgy-pudding": "^3.5.1"
+ }
+ },
+ "terser": {
+ "version": "4.8.0",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz",
+ "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==",
+ "dev": true,
+ "requires": {
+ "commander": "^2.20.0",
+ "source-map": "~0.6.1",
+ "source-map-support": "~0.5.12"
+ }
+ },
+ "webpack-sources": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz",
+ "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==",
+ "dev": true,
+ "requires": {
+ "source-list-map": "^2.0.0",
+ "source-map": "~0.6.1"
+ }
+ },
+ "worker-farm": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz",
+ "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==",
+ "dev": true,
+ "requires": {
+ "errno": "~0.1.7"
+ }
+ },
+ "yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true
+ }
+ }
+ },
+ "three": {
+ "version": "0.101.1",
+ "resolved": "https://registry.npmjs.org/three/-/three-0.101.1.tgz",
+ "integrity": "sha512-8ufimUVmRLtH+BTpEIbDjdGEKQOVWLMLgGynaKin1KbYTE136ZNOepJ8EgByi0tN43dQ7B1YrKLCJgXGy4bLmw=="
+ },
+ "throttleit": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-0.0.2.tgz",
+ "integrity": "sha1-z+34jmDADdlpe2H90qg0OptoDq8=",
+ "dev": true
+ },
+ "through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
+ "dev": true
+ },
+ "through2": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
+ "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+ "dev": true,
+ "requires": {
+ "readable-stream": "~2.3.6",
+ "xtend": "~4.0.1"
+ }
+ },
+ "thunky": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.3.tgz",
+ "integrity": "sha512-YwT8pjmNcAXBZqrubu22P4FYsh2D4dxRmnWBOL8Jk8bUcRUtc5326kx32tuTmFDAZtLOGEVNl8POAR8j896Iow==",
+ "dev": true
+ },
+ "ticky": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/ticky/-/ticky-1.0.1.tgz",
+ "integrity": "sha1-t8+nHnaPHJAAxJe5FRswlHxQ5G0="
+ },
+ "timers-browserify": {
+ "version": "2.0.10",
+ "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz",
+ "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==",
+ "dev": true,
+ "requires": {
+ "setimmediate": "^1.0.4"
+ }
+ },
+ "tmp": {
+ "version": "0.0.33",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+ "dev": true,
+ "requires": {
+ "os-tmpdir": "~1.0.2"
+ }
+ },
+ "tmp-promise": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-1.1.0.tgz",
+ "integrity": "sha512-8+Ah9aB1IRXCnIOxXZ0uFozV1nMU5xiu7hhFVUSxZ3bYu+psD4TzagCzVbexUCgNNGJnsmNDQlS4nG3mTyoNkw==",
+ "dev": true,
+ "requires": {
+ "bluebird": "^3.5.0",
+ "tmp": "0.1.0"
+ },
+ "dependencies": {
+ "tmp": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz",
+ "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==",
+ "dev": true,
+ "requires": {
+ "rimraf": "^2.6.3"
+ }
+ }
+ }
+ },
+ "to-array": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
+ "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=",
+ "dev": true
+ },
+ "to-arraybuffer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
+ "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=",
+ "dev": true
+ },
+ "to-fast-properties": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
+ "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=",
+ "dev": true
+ },
+ "to-object-path": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
+ "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "to-readable-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz",
+ "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==",
+ "dev": true
+ },
+ "to-regex": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
+ "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
+ "dev": true,
+ "requires": {
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "regex-not": "^1.0.2",
+ "safe-regex": "^1.1.0"
+ }
+ },
+ "to-regex-range": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+ "dev": true,
+ "requires": {
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1"
+ }
+ },
+ "tough-cookie": {
+ "version": "2.4.3",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
+ "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
+ "dev": true,
+ "requires": {
+ "psl": "^1.1.24",
+ "punycode": "^1.4.1"
+ },
+ "dependencies": {
+ "punycode": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+ "dev": true
+ }
+ }
+ },
+ "tree-kill": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.1.tgz",
+ "integrity": "sha512-4hjqbObwlh2dLyW4tcz0Ymw0ggoaVDMveUB9w8kFSQScdRLo0gxO9J7WFcUBo+W3C1TLdFIEwNOWebgZZ0RH9Q==",
+ "dev": true
+ },
+ "trim-newlines": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
+ "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=",
+ "dev": true
+ },
+ "trim-right": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
+ "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=",
+ "dev": true
+ },
+ "true-case-path": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz",
+ "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "glob": "^7.1.2"
+ }
+ },
+ "truncate-utf8-bytes": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz",
+ "integrity": "sha1-QFkjkJWS1W94pYGENLC3hInKXys=",
+ "dev": true,
+ "requires": {
+ "utf8-byte-length": "^1.0.1"
+ }
+ },
+ "ts-node": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz",
+ "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==",
+ "dev": true,
+ "requires": {
+ "arrify": "^1.0.0",
+ "buffer-from": "^1.1.0",
+ "diff": "^3.1.0",
+ "make-error": "^1.1.1",
+ "minimist": "^1.2.0",
+ "mkdirp": "^0.5.1",
+ "source-map-support": "^0.5.6",
+ "yn": "^2.0.0"
+ }
+ },
+ "tslib": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
+ "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ=="
+ },
+ "tslint": {
+ "version": "5.11.0",
+ "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.11.0.tgz",
+ "integrity": "sha1-mPMMAurjzecAYgHkwzywi0hYHu0=",
+ "dev": true,
+ "requires": {
+ "babel-code-frame": "^6.22.0",
+ "builtin-modules": "^1.1.1",
+ "chalk": "^2.3.0",
+ "commander": "^2.12.1",
+ "diff": "^3.2.0",
+ "glob": "^7.1.1",
+ "js-yaml": "^3.7.0",
+ "minimatch": "^3.0.4",
+ "resolve": "^1.3.2",
+ "semver": "^5.3.0",
+ "tslib": "^1.8.0",
+ "tsutils": "^2.27.2"
+ }
+ },
+ "tsutils": {
+ "version": "2.29.0",
+ "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz",
+ "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.8.1"
+ }
+ },
+ "tty-browserify": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
+ "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=",
+ "dev": true
+ },
+ "tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "tweetnacl": {
+ "version": "0.14.5",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+ "dev": true
+ },
+ "type-fest": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz",
+ "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==",
+ "dev": true
+ },
+ "type-is": {
+ "version": "1.6.16",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
+ "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==",
+ "dev": true,
+ "requires": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.18"
+ }
+ },
+ "typedarray": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
+ "dev": true
+ },
+ "typescript": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.4.tgz",
+ "integrity": "sha512-0RNDbSdEokBeEAkgNbxJ+BLwSManFy9TeXz8uW+48j/xhEXv1ePME60olyzw2XzUqUBNAYFeJadIqAgNqIACwg==",
+ "dev": true
+ },
+ "ultron": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
+ "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==",
+ "dev": true
+ },
+ "union-value": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
+ "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
+ "dev": true,
+ "requires": {
+ "arr-union": "^3.1.0",
+ "get-value": "^2.0.6",
+ "is-extendable": "^0.1.1",
+ "set-value": "^2.0.1"
+ }
+ },
+ "unique-filename": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
+ "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==",
+ "dev": true,
+ "requires": {
+ "unique-slug": "^2.0.0"
+ }
+ },
+ "unique-slug": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.1.tgz",
+ "integrity": "sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg==",
+ "dev": true,
+ "requires": {
+ "imurmurhash": "^0.1.4"
+ }
+ },
+ "unique-string": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz",
+ "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=",
+ "requires": {
+ "crypto-random-string": "^1.0.0"
+ }
+ },
+ "universalify": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+ "dev": true
+ },
+ "unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
+ "dev": true
+ },
+ "unset-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
+ "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
+ "dev": true,
+ "requires": {
+ "has-value": "^0.3.1",
+ "isobject": "^3.0.0"
+ },
+ "dependencies": {
+ "has-value": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
+ "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
+ "dev": true,
+ "requires": {
+ "get-value": "^2.0.3",
+ "has-values": "^0.1.4",
+ "isobject": "^2.0.0"
+ },
+ "dependencies": {
+ "isobject": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+ "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+ "dev": true,
+ "requires": {
+ "isarray": "1.0.0"
+ }
+ }
+ }
+ },
+ "has-values": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
+ "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=",
+ "dev": true
+ }
+ }
+ },
+ "upath": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz",
+ "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==",
+ "dev": true
+ },
+ "update-notifier": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-3.0.1.tgz",
+ "integrity": "sha512-grrmrB6Zb8DUiyDIaeRTBCkgISYUgETNe7NglEbVsrLWXeESnlCSP50WfRSj/GmzMPl6Uchj24S/p80nP/ZQrQ==",
+ "dev": true,
+ "requires": {
+ "boxen": "^3.0.0",
+ "chalk": "^2.0.1",
+ "configstore": "^4.0.0",
+ "has-yarn": "^2.1.0",
+ "import-lazy": "^2.1.0",
+ "is-ci": "^2.0.0",
+ "is-installed-globally": "^0.1.0",
+ "is-npm": "^3.0.0",
+ "is-yarn-global": "^0.3.0",
+ "latest-version": "^5.0.0",
+ "semver-diff": "^2.0.0",
+ "xdg-basedir": "^3.0.0"
+ }
+ },
+ "uri-js": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
+ "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "urix": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
+ "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
+ "dev": true
+ },
+ "url": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
+ "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
+ "requires": {
+ "punycode": "1.3.2",
+ "querystring": "0.2.0"
+ },
+ "dependencies": {
+ "punycode": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
+ "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
+ }
+ }
+ },
+ "url-parse": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.4.tgz",
+ "integrity": "sha512-ITeAByWWoqutFClc/lRZnFplgXgEZr3WJ6XngMM/N9DMIm4K8zXPCZ1Jdu0rERwO84w1WC5wkle2ubwTA4NTBg==",
+ "dev": true,
+ "requires": {
+ "querystringify": "^2.1.1",
+ "requires-port": "^1.0.0"
+ },
+ "dependencies": {
+ "querystringify": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
+ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
+ "dev": true
+ }
+ }
+ },
+ "url-parse-lax": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz",
+ "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=",
+ "dev": true,
+ "requires": {
+ "prepend-http": "^2.0.0"
+ }
+ },
+ "use": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
+ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
+ "dev": true
+ },
+ "useragent": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz",
+ "integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==",
+ "dev": true,
+ "requires": {
+ "lru-cache": "4.1.x",
+ "tmp": "0.0.x"
+ }
+ },
+ "utf8-byte-length": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz",
+ "integrity": "sha1-9F8VDExm7uloGGUFq5P8u4rWv2E=",
+ "dev": true
+ },
+ "util": {
+ "version": "0.11.1",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz",
+ "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==",
+ "dev": true,
+ "requires": {
+ "inherits": "2.0.3"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+ "dev": true
+ },
+ "utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
+ "dev": true
+ },
+ "uuid": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
+ "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
+ "dev": true
+ },
+ "validate-npm-package-license": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+ "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+ "dev": true,
+ "requires": {
+ "spdx-correct": "^3.0.0",
+ "spdx-expression-parse": "^3.0.0"
+ }
+ },
+ "validate-npm-package-name": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz",
+ "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=",
+ "dev": true,
+ "requires": {
+ "builtins": "^1.0.3"
+ }
+ },
+ "vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
+ "dev": true
+ },
+ "verror": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+ "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "^1.2.0"
+ }
+ },
+ "vm-browserify": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz",
+ "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=",
+ "dev": true,
+ "requires": {
+ "indexof": "0.0.1"
+ }
+ },
+ "void-elements": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz",
+ "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=",
+ "dev": true
+ },
+ "watchpack": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz",
+ "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==",
+ "dev": true,
+ "requires": {
+ "chokidar": "^2.0.2",
+ "graceful-fs": "^4.1.2",
+ "neo-async": "^2.5.0"
+ }
+ },
+ "wbuf": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz",
+ "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==",
+ "dev": true,
+ "requires": {
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "web-animations-js": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/web-animations-js/-/web-animations-js-2.3.1.tgz",
+ "integrity": "sha1-Om2bwVGWN3qQ+OKAP6UmIWWwRRA="
+ },
+ "webdriver-js-extender": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-2.1.0.tgz",
+ "integrity": "sha512-lcUKrjbBfCK6MNsh7xaY2UAUmZwe+/ib03AjVOpFobX4O7+83BUveSrLfU0Qsyb1DaKJdQRbuU+kM9aZ6QUhiQ==",
+ "dev": true,
+ "requires": {
+ "@types/selenium-webdriver": "^3.0.0",
+ "selenium-webdriver": "^3.0.1"
+ }
+ },
+ "webpack": {
+ "version": "4.29.0",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.29.0.tgz",
+ "integrity": "sha512-pxdGG0keDBtamE1mNvT5zyBdx+7wkh6mh7uzMOo/uRQ/fhsdj5FXkh/j5mapzs060forql1oXqXN9HJGju+y7w==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.7.11",
+ "@webassemblyjs/helper-module-context": "1.7.11",
+ "@webassemblyjs/wasm-edit": "1.7.11",
+ "@webassemblyjs/wasm-parser": "1.7.11",
+ "acorn": "^6.0.5",
+ "acorn-dynamic-import": "^4.0.0",
+ "ajv": "^6.1.0",
+ "ajv-keywords": "^3.1.0",
+ "chrome-trace-event": "^1.0.0",
+ "enhanced-resolve": "^4.1.0",
+ "eslint-scope": "^4.0.0",
+ "json-parse-better-errors": "^1.0.2",
+ "loader-runner": "^2.3.0",
+ "loader-utils": "^1.1.0",
+ "memory-fs": "~0.4.1",
+ "micromatch": "^3.1.8",
+ "mkdirp": "~0.5.0",
+ "neo-async": "^2.5.0",
+ "node-libs-browser": "^2.0.0",
+ "schema-utils": "^0.4.4",
+ "tapable": "^1.1.0",
+ "terser-webpack-plugin": "^1.1.0",
+ "watchpack": "^1.5.0",
+ "webpack-sources": "^1.3.0"
+ },
+ "dependencies": {
+ "schema-utils": {
+ "version": "0.4.7",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz",
+ "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.1.0",
+ "ajv-keywords": "^3.1.0"
+ }
+ }
+ }
+ },
+ "webpack-core": {
+ "version": "0.6.9",
+ "resolved": "https://registry.npmjs.org/webpack-core/-/webpack-core-0.6.9.tgz",
+ "integrity": "sha1-/FcViMhVjad76e+23r3Fo7FyvcI=",
+ "dev": true,
+ "requires": {
+ "source-list-map": "~0.1.7",
+ "source-map": "~0.4.1"
+ },
+ "dependencies": {
+ "source-list-map": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-0.1.8.tgz",
+ "integrity": "sha1-xVCyq1Qn9rPyH1r+rYjE9Vh7IQY=",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
+ "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
+ "dev": true,
+ "requires": {
+ "amdefine": ">=0.0.4"
+ }
+ }
+ }
+ },
+ "webpack-dev-middleware": {
+ "version": "3.5.1",
+ "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.5.1.tgz",
+ "integrity": "sha512-4dwCh/AyMOYAybggUr8fiCkRnjVDp+Cqlr9c+aaNB3GJYgRGYQWJ1YX/WAKUNA9dPNHZ6QSN2lYDKqjKSI8Vqw==",
+ "dev": true,
+ "requires": {
+ "memory-fs": "~0.4.1",
+ "mime": "^2.3.1",
+ "range-parser": "^1.0.3",
+ "webpack-log": "^2.0.0"
+ },
+ "dependencies": {
+ "mime": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz",
+ "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==",
+ "dev": true
+ }
+ }
+ },
+ "webpack-dev-server": {
+ "version": "3.1.14",
+ "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.1.14.tgz",
+ "integrity": "sha512-mGXDgz5SlTxcF3hUpfC8hrQ11yhAttuUQWf1Wmb+6zo3x6rb7b9mIfuQvAPLdfDRCGRGvakBWHdHOa0I9p/EVQ==",
+ "dev": true,
+ "requires": {
+ "ansi-html": "0.0.7",
+ "bonjour": "^3.5.0",
+ "chokidar": "^2.0.0",
+ "compression": "^1.5.2",
+ "connect-history-api-fallback": "^1.3.0",
+ "debug": "^3.1.0",
+ "del": "^3.0.0",
+ "express": "^4.16.2",
+ "html-entities": "^1.2.0",
+ "http-proxy-middleware": "~0.18.0",
+ "import-local": "^2.0.0",
+ "internal-ip": "^3.0.1",
+ "ip": "^1.1.5",
+ "killable": "^1.0.0",
+ "loglevel": "^1.4.1",
+ "opn": "^5.1.0",
+ "portfinder": "^1.0.9",
+ "schema-utils": "^1.0.0",
+ "selfsigned": "^1.9.1",
+ "semver": "^5.6.0",
+ "serve-index": "^1.7.2",
+ "sockjs": "0.3.19",
+ "sockjs-client": "1.3.0",
+ "spdy": "^4.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^5.1.0",
+ "url": "^0.11.0",
+ "webpack-dev-middleware": "3.4.0",
+ "webpack-log": "^2.0.0",
+ "yargs": "12.0.2"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
+ },
+ "camelcase": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
+ "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
+ "dev": true
+ },
+ "cliui": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz",
+ "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==",
+ "dev": true,
+ "requires": {
+ "string-width": "^2.1.1",
+ "strip-ansi": "^4.0.0",
+ "wrap-ansi": "^2.0.0"
+ },
+ "dependencies": {
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ }
+ }
+ },
+ "cross-spawn": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+ "dev": true,
+ "requires": {
+ "nice-try": "^1.0.4",
+ "path-key": "^2.0.1",
+ "semver": "^5.5.0",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ }
+ },
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "decamelize": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-2.0.0.tgz",
+ "integrity": "sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==",
+ "dev": true,
+ "requires": {
+ "xregexp": "4.0.0"
+ }
+ },
+ "execa": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
+ "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^6.0.0",
+ "get-stream": "^4.0.0",
+ "is-stream": "^1.1.0",
+ "npm-run-path": "^2.0.0",
+ "p-finally": "^1.0.0",
+ "signal-exit": "^3.0.0",
+ "strip-eof": "^1.0.0"
+ }
+ },
+ "find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^3.0.0"
+ }
+ },
+ "get-stream": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
+ "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
+ "dev": true,
+ "requires": {
+ "pump": "^3.0.0"
+ }
+ },
+ "invert-kv": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz",
+ "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
+ },
+ "lcid": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz",
+ "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==",
+ "dev": true,
+ "requires": {
+ "invert-kv": "^2.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "mime": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz",
+ "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+ "dev": true
+ },
+ "os-locale": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz",
+ "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==",
+ "dev": true,
+ "requires": {
+ "execa": "^1.0.0",
+ "lcid": "^2.0.0",
+ "mem": "^4.0.0"
+ }
+ },
+ "p-limit": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz",
+ "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==",
+ "dev": true,
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.0.0"
+ }
+ },
+ "p-try": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz",
+ "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==",
+ "dev": true
+ },
+ "pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "string-width": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "dev": true,
+ "requires": {
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^4.0.0"
+ },
+ "dependencies": {
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ }
+ }
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "webpack-dev-middleware": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.4.0.tgz",
+ "integrity": "sha512-Q9Iyc0X9dP9bAsYskAVJ/hmIZZQwf/3Sy4xCAZgL5cUkjZmUZLt4l5HpbST/Pdgjn3u6pE7u5OdGd1apgzRujA==",
+ "dev": true,
+ "requires": {
+ "memory-fs": "~0.4.1",
+ "mime": "^2.3.1",
+ "range-parser": "^1.0.3",
+ "webpack-log": "^2.0.0"
+ }
+ },
+ "which-module": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
+ "dev": true
+ },
+ "yargs": {
+ "version": "12.0.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.2.tgz",
+ "integrity": "sha512-e7SkEx6N6SIZ5c5H22RTZae61qtn3PYUE8JYbBFlK9sYmh3DMQ6E5ygtaG/2BW0JZi4WGgTR2IV5ChqlqrDGVQ==",
+ "dev": true,
+ "requires": {
+ "cliui": "^4.0.0",
+ "decamelize": "^2.0.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^1.0.1",
+ "os-locale": "^3.0.0",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^1.0.1",
+ "set-blocking": "^2.0.0",
+ "string-width": "^2.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^3.2.1 || ^4.0.0",
+ "yargs-parser": "^10.1.0"
+ }
+ },
+ "yargs-parser": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz",
+ "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^4.1.0"
+ }
+ }
+ }
+ },
+ "webpack-log": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz",
+ "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==",
+ "dev": true,
+ "requires": {
+ "ansi-colors": "^3.0.0",
+ "uuid": "^3.3.2"
+ }
+ },
+ "webpack-merge": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.1.tgz",
+ "integrity": "sha512-4p8WQyS98bUJcCvFMbdGZyZmsKuWjWVnVHnAS3FFg0HDaRVrPbkivx2RYCre8UiemD67RsiFFLfn4JhLAin8Vw==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.5"
+ }
+ },
+ "webpack-sources": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.3.0.tgz",
+ "integrity": "sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA==",
+ "dev": true,
+ "requires": {
+ "source-list-map": "^2.0.0",
+ "source-map": "~0.6.1"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "webpack-subresource-integrity": {
+ "version": "1.1.0-rc.6",
+ "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-1.1.0-rc.6.tgz",
+ "integrity": "sha512-Az7y8xTniNhaA0620AV1KPwWOqawurVVDzQSpPAeR5RwNbL91GoBSJAAo9cfd+GiFHwsS5bbHepBw1e6Hzxy4w==",
+ "dev": true,
+ "requires": {
+ "webpack-core": "^0.6.8"
+ }
+ },
+ "websocket-driver": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz",
+ "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=",
+ "dev": true,
+ "requires": {
+ "http-parser-js": ">=0.4.0",
+ "websocket-extensions": ">=0.1.1"
+ }
+ },
+ "websocket-extensions": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
+ "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==",
+ "dev": true
+ },
+ "when": {
+ "version": "3.6.4",
+ "resolved": "https://registry.npmjs.org/when/-/when-3.6.4.tgz",
+ "integrity": "sha1-RztRfsFZ4rhQBUl6E5g/CVQS404=",
+ "dev": true
+ },
+ "which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "which-module": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz",
+ "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=",
+ "dev": true
+ },
+ "wide-align": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
+ "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "string-width": "^1.0.2 || 2"
+ }
+ },
+ "widest-line": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz",
+ "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==",
+ "dev": true,
+ "requires": {
+ "string-width": "^2.1.1"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
+ },
+ "string-width": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "dev": true,
+ "requires": {
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^4.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ }
+ }
+ },
+ "window-size": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz",
+ "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY="
+ },
+ "wordwrap": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
+ "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=",
+ "dev": true
+ },
+ "worker-farm": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.6.0.tgz",
+ "integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==",
+ "dev": true,
+ "requires": {
+ "errno": "~0.1.7"
+ }
+ },
+ "wrap-ansi": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
+ "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
+ "requires": {
+ "string-width": "^1.0.1",
+ "strip-ansi": "^3.0.1"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ },
+ "write-file-atomic": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.2.tgz",
+ "integrity": "sha512-s0b6vB3xIVRLWywa6X9TOMA7k9zio0TMOsl9ZnDkliA/cfJlpHXAscj0gbHVJiTdIuAYpIyqS5GW91fqm6gG5g==",
+ "requires": {
+ "graceful-fs": "^4.1.11",
+ "imurmurhash": "^0.1.4",
+ "signal-exit": "^3.0.2"
+ }
+ },
+ "ws": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
+ "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
+ "dev": true,
+ "requires": {
+ "async-limiter": "~1.0.0",
+ "safe-buffer": "~5.1.0",
+ "ultron": "~1.1.0"
+ }
+ },
+ "xdg-basedir": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz",
+ "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ="
+ },
+ "xml2js": {
+ "version": "0.4.19",
+ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
+ "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==",
+ "requires": {
+ "sax": ">=0.6.0",
+ "xmlbuilder": "~9.0.1"
+ },
+ "dependencies": {
+ "sax": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
+ }
+ }
+ },
+ "xmlbuilder": {
+ "version": "9.0.7",
+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz",
+ "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0="
+ },
+ "xmldom": {
+ "version": "0.1.27",
+ "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz",
+ "integrity": "sha1-1QH5ezvbQDr4757MIFcxh6rawOk=",
+ "dev": true
+ },
+ "xmlhttprequest-ssl": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
+ "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=",
+ "dev": true
+ },
+ "xregexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz",
+ "integrity": "sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==",
+ "dev": true
+ },
+ "xtend": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
+ "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=",
+ "dev": true
+ },
+ "y18n": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
+ "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
+ "dev": true
+ },
+ "yallist": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
+ "dev": true
+ },
+ "yargs": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz",
+ "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=",
+ "dev": true,
+ "requires": {
+ "camelcase": "^3.0.0",
+ "cliui": "^3.2.0",
+ "decamelize": "^1.1.1",
+ "get-caller-file": "^1.0.1",
+ "os-locale": "^1.4.0",
+ "read-pkg-up": "^1.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^1.0.1",
+ "set-blocking": "^2.0.0",
+ "string-width": "^1.0.2",
+ "which-module": "^1.0.0",
+ "y18n": "^3.2.1",
+ "yargs-parser": "^5.0.0"
+ },
+ "dependencies": {
+ "camelcase": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
+ "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
+ "dev": true
+ },
+ "y18n": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
+ "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
+ "dev": true
+ }
+ }
+ },
+ "yargs-parser": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz",
+ "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=",
+ "dev": true,
+ "requires": {
+ "camelcase": "^3.0.0"
+ },
+ "dependencies": {
+ "camelcase": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
+ "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
+ "dev": true
+ }
+ }
+ },
+ "yauzl": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz",
+ "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=",
+ "dev": true,
+ "requires": {
+ "fd-slicer": "~1.0.1"
+ }
+ },
+ "yeast": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
+ "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=",
+ "dev": true
+ },
+ "yn": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz",
+ "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=",
+ "dev": true
+ },
+ "zone.js": {
+ "version": "0.8.29",
+ "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.29.tgz",
+ "integrity": "sha512-mla2acNCMkWXBD+c+yeUrBUrzOxYMNFdQ6FGfigGGtEVBPJx07BQeJekjt9DmH1FtZek4E9rE1eRR9qQpxACOQ=="
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..04c66861
--- /dev/null
+++ b/package.json
@@ -0,0 +1,117 @@
+{
+ "name": "truevision-designer",
+ "homepage": "https://www.truevision.ai",
+ "description": "Truevison designer is a tool to create environment and scenarios for training & testing autonomous vehicles",
+ "version": "2022.3.01",
+ "author": {
+ "name": "Truevision.ai",
+ "email": "himanshu@truevision.ai",
+ "url": "https://www.truevision.ai"
+ },
+ "main": "main.js",
+ "scripts": {
+ "ng": "ng",
+ "start": "ng serve",
+ "start:dev": "npx ng serve --host 0.0.0.0 --poll",
+ "build": "ng build",
+ "build-watch": "ng build --watch",
+ "build-prod": "ng build --prod",
+ "build-prod-live": "ng build --prod --watch",
+ "test": "ng test",
+ "lint": "ng lint",
+ "e2e": "ng e2e",
+ "pack": "electron-builder --dir",
+ "dist": "electron-builder",
+ "postinstall": "electron-builder install-app-deps",
+ "linux": "ng build --prod --build-optimizer && electron-builder build --linux",
+ "windows": "ng build --prod --build-optimizer && electron-builder build --windows"
+ },
+ "private": true,
+ "dependencies": {
+ "@agm/core": "1.0.0-beta.2",
+ "@angular/animations": "^7.2.6",
+ "@angular/cdk": "^7.3.3",
+ "@angular/common": "~7.2.0",
+ "@angular/compiler": "~7.2.0",
+ "@angular/core": "~7.2.0",
+ "@angular/flex-layout": "^7.0.0-beta.23",
+ "@angular/forms": "~7.2.0",
+ "@angular/http": "7.2.0",
+ "@angular/material": "^7.3.3",
+ "@angular/platform-browser": "~7.2.0",
+ "@angular/platform-browser-dynamic": "~7.2.0",
+ "@angular/router": "~7.2.0",
+ "@auth0/angular-jwt": "^3.0.0",
+ "@grpc/proto-loader": "^0.1.0",
+ "@ncstate/sat-popover": "^4.0.0",
+ "@ngu/carousel": "1.5.4",
+ "@ngx-translate/core": "10.0.1",
+ "@ngx-translate/http-loader": "3.0.1",
+ "@types/mixpanel-browser": "^2.23.1",
+ "angular-calendar": "0.26.4",
+ "angular-file-saver": "^1.1.3",
+ "async": "^1.5.2",
+ "chart.js": "2.5.0",
+ "classlist.js": "1.1.20150312",
+ "core-js": "^2.5.4",
+ "date-fns": "1.28.5",
+ "earcut": "^2.2.3",
+ "electron-splashscreen": "^1.0.0",
+ "fast-xml-parser": "^3.12.16",
+ "file-saver": "^2.0.2",
+ "google-protobuf": "^3.0.0",
+ "grpc": "^1.21.1",
+ "hammerjs": "^2.0.8",
+ "hopscotch": "0.3.1",
+ "lodash": "^4.17.21",
+ "minimist": "^1.2.5",
+ "mixpanel-browser": "^2.29.1",
+ "ng2-charts": "1.6.0",
+ "ng2-dragula": "1.3.1",
+ "ng2-file-upload": "1.2.1",
+ "ng2-validation": "4.2.0",
+ "ngx-color-picker": "^5.3.8",
+ "ngx-electron": "^2.1.1",
+ "ngx-pagination": "3.1.0",
+ "ngx-perfect-scrollbar": "6.1.0",
+ "ngx-quill": "2.0.4",
+ "perfect-scrollbar": "1.4.0",
+ "rxjs": "^6.3.3",
+ "rxjs-compat": "6.1.0",
+ "stats.js": "^0.17.0",
+ "three": "^0.101.1",
+ "tslib": "^1.9.0",
+ "web-animations-js": "2.3.1",
+ "zone.js": "~0.8.26"
+ },
+ "devDependencies": {
+ "@angular-devkit/build-angular": "~0.13.0",
+ "@angular-devkit/core": "7.0.6",
+ "@angular/cli": "~7.3.3",
+ "@angular/compiler-cli": "~7.2.0",
+ "@angular/language-service": "~7.2.0",
+ "@types/jasmine": "~2.8.8",
+ "@types/jasminewd2": "~2.0.3",
+ "@types/node": "~8.9.4",
+ "@types/three": "^0.93.31",
+ "codelyzer": "~4.5.0",
+ "copy-webpack-plugin": "4.3.0",
+ "electron": "^5.0.3",
+ "electron-builder": "^21.2.0",
+ "electron-packager": "^14.0.0",
+ "jasmine-core": "~2.99.1",
+ "jasmine-spec-reporter": "~4.2.1",
+ "karma": "~4.0.0",
+ "karma-chrome-launcher": "~2.2.0",
+ "karma-coverage-istanbul-reporter": "~2.0.1",
+ "karma-jasmine": "~1.1.2",
+ "karma-jasmine-html-reporter": "^0.2.2",
+ "ngx-color": "^7.0.0",
+ "nwjs-builder-phoenix": "^1.15.0",
+ "protractor": "~5.4.0",
+ "sass": "^1.49.0",
+ "ts-node": "~7.0.0",
+ "tslint": "~5.11.0",
+ "typescript": "~3.2.2"
+ }
+}
diff --git a/src/app/app.component.css b/src/app/app.component.css
new file mode 100644
index 00000000..bd9ce3fc
--- /dev/null
+++ b/src/app/app.component.css
@@ -0,0 +1,11 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+.example-icon {
+ padding: 0 14px;
+}
+
+.example-spacer {
+ flex: 1 1 auto;
+}
\ No newline at end of file
diff --git a/src/app/app.component.html b/src/app/app.component.html
new file mode 100644
index 00000000..f5ee029f
--- /dev/null
+++ b/src/app/app.component.html
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
new file mode 100644
index 00000000..9d61d630
--- /dev/null
+++ b/src/app/app.component.ts
@@ -0,0 +1,94 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { AfterViewInit, Component, OnInit, Renderer2 } from '@angular/core';
+import { Title } from '@angular/platform-browser';
+import { ActivatedRoute, NavigationEnd, Router, RouterEvent } from '@angular/router';
+
+import { RoutePartsService } from './shared/services/route-parts.service';
+import { ThemeService } from './shared/services/theme.service';
+
+import { filter, takeUntil } from 'rxjs/operators';
+import { LayoutService } from './shared/services/layout.service';
+import { AppService } from './core/services/app.service';
+import { AnalyticsService } from './core/analytics/analytics.service';
+import { environment } from '../environments/environment';
+import { Subject } from 'rxjs';
+import { TranslateService } from '@ngx-translate/core';
+
+@Component( {
+ selector: 'app-root',
+ templateUrl: './app.component.html',
+ styleUrls: [ './app.component.css' ]
+} )
+export class AppComponent implements OnInit, AfterViewInit {
+
+ appTitle = 'Truevision';
+ pageTitle = '';
+ private destroyed$ = new Subject();
+
+ constructor (
+ public title: Title,
+ private router: Router,
+ private activeRoute: ActivatedRoute,
+ private routePartsService: RoutePartsService,
+ private themeService: ThemeService,
+ private layout: LayoutService,
+ private renderer: Renderer2,
+ private appService: AppService,
+ private analytics: AnalyticsService,
+ private translate: TranslateService
+ ) {
+ }
+
+ get production () {
+ return environment.production;
+ }
+
+ ngOnInit () {
+
+ this.analytics.init();
+
+ this.analytics.send( 'app-opened', {} );
+
+ this.changePageTitle();
+
+ this.trackPageChanges();
+
+ this.translate.setDefaultLang( 'en' );
+
+ }
+
+ ngAfterViewInit () {
+ this.layout.applyMatTheme( this.renderer );
+ }
+
+ changePageTitle () {
+ this.router.events.pipe( filter( event => event instanceof NavigationEnd ) ).subscribe( ( routeChange ) => {
+ var routeParts = this.routePartsService.generateRouteParts( this.activeRoute.snapshot );
+ if ( !routeParts.length )
+ return this.title.setTitle( this.appTitle );
+ // Extract title from parts;
+ this.pageTitle = routeParts
+ .reverse()
+ .map( ( part ) => part.title )
+ .reduce( ( partA, partI ) => {
+ return `${ partA } > ${ partI }`;
+ } );
+ this.pageTitle += ` | ${ this.appTitle }`;
+ this.title.setTitle( this.pageTitle );
+ } );
+ }
+
+ private trackPageChanges () {
+ this.router.events
+ .pipe(
+ filter( ( event: RouterEvent ) => event instanceof NavigationEnd ),
+ takeUntil( this.destroyed$ ),
+ )
+ .subscribe( ( event: NavigationEnd ) => {
+ this.analytics.trackPageView( event.url );
+ } );
+ }
+}
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
new file mode 100644
index 00000000..573ba6df
--- /dev/null
+++ b/src/app/app.module.ts
@@ -0,0 +1,79 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { ErrorHandler, NgModule } from '@angular/core';
+import { BrowserModule, HAMMER_GESTURE_CONFIG } from '@angular/platform-browser';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+import { GestureConfig, MatTreeModule } from '@angular/material';
+import { PERFECT_SCROLLBAR_CONFIG, PerfectScrollbarConfigInterface, PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
+
+import { SharedModule } from './shared/shared.module';
+import { AppComponent } from './app.component';
+import { AppRoutingModule } from './app.routing';
+
+import { HttpClient, HttpClientModule } from '@angular/common/http';
+import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
+import { TranslateHttpLoader } from '@ngx-translate/http-loader';
+import { ErrorHandlerService } from './shared/services/error-handler.service';
+
+import { TvMapModule } from './modules/tv-map/tv-map.module';
+import { ThreeJsModule } from './modules/three-js/three-js.module';
+import { CoreModule } from './core/core.module';
+import { NgxElectronModule } from 'ngx-electron';
+import { FormsModule } from '@angular/forms';
+import { SessionsModule } from './views/sessions/sessions.module';
+import { HashLocationStrategy, LocationStrategy } from '@angular/common';
+
+import { SatPopoverModule } from '@ncstate/sat-popover';
+import { EditorModule } from './views/editor/editor.module';
+
+// AoT requires an exported function for factories
+export function HttpLoaderFactory ( httpClient: HttpClient ) {
+ return new TranslateHttpLoader( httpClient, './assets/i18n/', '.json' );
+}
+
+const DEFAULT_PERFECT_SCROLLBAR_CONFIG: PerfectScrollbarConfigInterface = {
+ suppressScrollX: true
+};
+
+@NgModule( {
+ imports: [
+ AppRoutingModule,
+ MatTreeModule,
+ BrowserModule,
+ NoopAnimationsModule,
+ SharedModule,
+ HttpClientModule,
+ PerfectScrollbarModule,
+ FormsModule,
+
+ CoreModule,
+ EditorModule,
+ TvMapModule,
+ ThreeJsModule,
+ NgxElectronModule,
+
+ SessionsModule,
+ SatPopoverModule,
+
+ TranslateModule.forRoot( {
+ loader: {
+ provide: TranslateLoader,
+ useFactory: HttpLoaderFactory,
+ deps: [ HttpClient ]
+ }
+ } ),
+ ],
+ declarations: [ AppComponent ],
+ providers: [
+ { provide: ErrorHandler, useClass: ErrorHandlerService },
+ { provide: HAMMER_GESTURE_CONFIG, useClass: GestureConfig },
+ { provide: PERFECT_SCROLLBAR_CONFIG, useValue: DEFAULT_PERFECT_SCROLLBAR_CONFIG },
+ { provide: LocationStrategy, useClass: HashLocationStrategy }
+ ],
+ bootstrap: [ AppComponent ],
+ exports: []
+} )
+export class AppModule {
+}
diff --git a/src/app/app.routing.ts b/src/app/app.routing.ts
new file mode 100644
index 00000000..02a8dcfd
--- /dev/null
+++ b/src/app/app.routing.ts
@@ -0,0 +1,79 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+import { AuthGuard } from './core/guards/auth.guard';
+import { EditorLayoutComponent } from './views/editor/layout/editor-layout.component';
+import { EditorComponent } from './views/editor/editor.component';
+import { SplashComponent } from './shared/components/splash/splash.component';
+import { SignupComponent } from './views/sessions/signup/signup.component';
+import { SigninComponent } from './views/sessions/signin/signin.component';
+import { ForgotPasswordComponent } from './views/sessions/forgot-password/forgot-password.component';
+import { LockscreenComponent } from './views/sessions/lockscreen/lockscreen.component';
+import { NotFoundComponent } from './views/sessions/not-found/not-found.component';
+import { ErrorComponent } from './views/sessions/error/error.component';
+
+const appRoutes: Routes = [
+ {
+ path: '',
+ redirectTo: '/editor',
+ pathMatch: 'full'
+ },
+ {
+ path: 'splash',
+ component: SplashComponent,
+ },
+ {
+ path: 'editor',
+ canActivate: [ AuthGuard ],
+ component: EditorLayoutComponent,
+ children: [
+ {
+ path: '', component: EditorComponent,
+ data: {
+ title: 'Editor',
+ breadcrumb: ''
+ }
+ },
+ ]
+ },
+ {
+ path: 'sessions',
+ children: [ {
+ path: 'signup',
+ component: SignupComponent,
+ data: { title: 'Signup' }
+ }, {
+ path: 'signin',
+ component: SigninComponent,
+ data: { title: 'Signin' }
+ }, {
+ path: 'forgot-password',
+ component: ForgotPasswordComponent,
+ data: { title: 'Forgot password' }
+ }, {
+ path: 'lockscreen',
+ component: LockscreenComponent,
+ data: { title: 'Lockscreen' }
+ }, {
+ path: '404',
+ component: NotFoundComponent,
+ data: { title: 'Not Found' }
+ }, {
+ path: 'error',
+ component: ErrorComponent,
+ data: { title: 'Error' }
+ } ]
+ },
+
+];
+
+@NgModule( {
+ imports: [ RouterModule.forRoot( appRoutes, { enableTracing: false, useHash: false } ) ],
+ exports: [ RouterModule ]
+} )
+export class AppRoutingModule {
+
+}
diff --git a/src/app/core/analytics/analytics.service.ts b/src/app/core/analytics/analytics.service.ts
new file mode 100644
index 00000000..0f74c647
--- /dev/null
+++ b/src/app/core/analytics/analytics.service.ts
@@ -0,0 +1,98 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Injectable } from '@angular/core';
+import { MixpanelService } from './mixpanel.service';
+import { environment } from '../../../environments/environment';
+import { AuthService } from '../services/auth.service';
+import { Environment } from '../utils/environment';
+import { Router, RouterEvent, NavigationEnd } from '@angular/router';
+import { filter, takeUntil } from 'rxjs/operators';
+import { Subject } from 'rxjs';
+
+@Injectable( {
+ providedIn: 'root'
+} )
+export class AnalyticsService {
+
+ private destroyed$ = new Subject();
+
+ constructor ( private mixpanel: MixpanelService, private auth: AuthService, private router: Router ) {
+
+ if ( Environment.production ) this.mixpanel.init( environment.mixpanel_id, this.auth.email );
+
+ if ( this.email != null ) this.setEmail( this.email );
+
+ this.auth.currentUser.subscribe( e => this.onUserChanged() );
+
+ }
+
+ get email () {
+
+ return this.auth.email;
+
+ }
+
+ init () {
+
+ if ( Environment.production ) this.trackPageChanges();
+
+ }
+
+ private trackPageChanges () {
+
+ this.router.events
+ .pipe(
+ filter( ( event: RouterEvent ) => event instanceof NavigationEnd ),
+ takeUntil( this.destroyed$ ),
+ )
+ .subscribe( ( event: NavigationEnd ) => {
+ this.trackPageView( event.url );
+ } );
+
+ }
+
+ onUserChanged (): void {
+
+ if ( this.email != null ) this.setEmail( this.email );
+
+ }
+
+ send ( event: string, options: any ) {
+
+ if ( !Environment.production ) return;
+
+ this.mixpanel.track( event, options );
+
+ }
+
+ trackError ( error: Error ) {
+
+ if ( !Environment.production ) return;
+
+ this.mixpanel.track( 'error', {
+ name: error.name,
+ message: error.message,
+ stack: error.stack
+ } );
+
+ }
+
+ setEmail ( email: string ) {
+
+ if ( Environment.production ) this.mixpanel.setEmail( email );
+
+ }
+
+
+ trackPageView ( url: string ) {
+
+ if ( !Environment.production ) return;
+
+ this.mixpanel.track( 'pageview', {
+ url: url
+ } );
+
+ }
+}
diff --git a/src/app/core/analytics/mixpanel.service.ts b/src/app/core/analytics/mixpanel.service.ts
new file mode 100644
index 00000000..8cae2867
--- /dev/null
+++ b/src/app/core/analytics/mixpanel.service.ts
@@ -0,0 +1,44 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Injectable } from '@angular/core';
+import * as mixpanel from 'mixpanel-browser';
+
+@Injectable( {
+ providedIn: 'root'
+} )
+export class MixpanelService {
+
+
+ constructor () {
+ }
+
+ /**
+ * Initialize mixpanel.
+ *
+ * @param {string} userToken
+ * @memberof MixpanelService
+ */
+ init ( userToken: string, identifier: string = null ): void {
+ mixpanel.init( userToken );
+ mixpanel.identify( identifier || userToken );
+ }
+
+ /**
+ * Push new action to mixpanel.
+ *
+ * @param {string} id Name of the action to track.
+ * @param {*} [action={}] Actions object with custom properties.
+ * @memberof MixpanelService
+ */
+ track ( id: string, action: any = {} ): void {
+ mixpanel.track( id, action );
+ }
+
+ setEmail ( email: string ) {
+ mixpanel.people.set( {
+ $email: email
+ } );
+ }
+}
diff --git a/src/app/core/analytics/track.directive.ts b/src/app/core/analytics/track.directive.ts
new file mode 100644
index 00000000..087a7ab7
--- /dev/null
+++ b/src/app/core/analytics/track.directive.ts
@@ -0,0 +1,39 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Directive, Input, HostListener } from '@angular/core';
+import { AnalyticsService } from './analytics.service';
+
+@Directive( {
+ selector: '[appTrack]'
+} )
+export class TrackDirective {
+
+ @Input( 'appTrack' ) eventName: string;
+
+ @Input() event: string;
+ @Input() action: string;
+
+ constructor ( private analytics: AnalyticsService ) { }
+
+ @HostListener( 'click' ) onMouseEnter () {
+
+ const event = this.eventName || this.event;
+
+ if ( this.action != null ) {
+
+ const options = {
+ action: this.action
+ };
+
+ this.analytics.send( event, options );
+
+ } else {
+
+ this.analytics.send( event, null );
+
+ }
+ }
+
+}
diff --git a/src/app/core/asset-importer.service.ts b/src/app/core/asset-importer.service.ts
new file mode 100644
index 00000000..6d4a73fd
--- /dev/null
+++ b/src/app/core/asset-importer.service.ts
@@ -0,0 +1,198 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Injectable } from '@angular/core';
+import { ElectronService } from 'ngx-electron';
+import * as THREE from 'three';
+import { SnackBar } from 'app/services/snack-bar.service';
+import { SceneService } from './services/scene.service';
+
+import 'three/examples/js/loaders/OBJLoader';
+import 'three/examples/js/loaders/FBXLoader';
+import 'three/examples/js/loaders/ColladaLoader';
+import { FileService } from 'app/services/file.service';
+
+
+@Injectable( {
+ providedIn: 'root'
+} )
+export class AssetImporterService {
+
+ private fs;
+ private path;
+
+ // causing bugs need to remove these
+ // private successCallback;
+ // private errorCallback;
+
+ constructor ( private electronService: ElectronService ) {
+ }
+
+ public import ( filepath: string, successCallback: Function, errorCallback: Function ) {
+
+ this.fs = this.electronService.remote.require( 'fs' );
+ this.path = this.electronService.remote.require( 'path' );
+
+ const fileExtension = this.path.extname( filepath );
+
+ switch ( fileExtension ) {
+ case '.obj':
+ this.importOBJ( filepath, successCallback, errorCallback );
+ break;
+
+ case '.dae':
+ this.importCollada( filepath, successCallback, errorCallback );
+ break;
+
+ case '.gltf':
+ this.importGLTF( filepath, successCallback, errorCallback );
+ break;
+
+ case '.glb':
+ this.importGLTF( filepath, successCallback, errorCallback );
+ break;
+
+ case '.fbx':
+ this.importFBX( filepath, successCallback, errorCallback );
+ break;
+
+ default:
+ SnackBar.error( 'unknown 3d format. Please use any of obj, dae, fbx formats' );
+ break;
+ }
+
+ }
+
+ importOBJ ( filepath: string, success: Function, error: Function ) {
+
+ var loader = new THREE.OBJLoader();
+
+ this.fs.readFile( filepath, 'utf-8', ( err, data ) => {
+
+ if ( err ) {
+ error( 'An error ocurred reading the file :' + err.message );
+ return;
+ }
+
+ const group = loader.parse( data );
+
+ success( group );
+
+ } )
+
+ }
+
+ importGLTF ( filepath: string, success: Function, error: Function ) {
+
+ const loader = new THREE.GLTFLoader();
+
+ const dir = filepath.split( '/' ).slice( 0, -1 ).join( '/' ) + "/";
+
+ loader.load( `file:///${ filepath }`, ( gltf ) => {
+
+ success( gltf.scene );
+
+ }, () => {
+
+ // on progress
+
+ }, ( err ) => {
+
+ console.error( err );
+
+ error( err );
+
+ } )
+
+ // this.fs.readFile( filepath, 'ascii', ( err, data ) => {
+
+ // if ( err ) {
+ // error( 'An error ocurred reading the file :' + err.message );
+ // return;
+ // }
+
+ // const dir = filepath.split( '/' ).slice( 0, -1 ).join( '/' ) + "/";
+
+ // loader.parse( data, `file:///${ dir }`, ( gltf ) => {
+
+ // success( gltf.scene );
+
+ // }, ( err ) => {
+
+ // error( err )
+
+ // } );
+
+ // } )
+
+ }
+
+ importCollada ( filepath: string, success: Function, error: Function ) {
+
+ var loader = new THREE.ColladaLoader();
+
+ this.fs.readFile( filepath, 'utf-8', ( err, data ) => {
+
+ if ( err ) {
+ error( 'An error ocurred reading the file :' + err.message );
+ return;
+ }
+
+ const group = loader.parse( data );
+
+ success( group.scene );
+
+ } );
+
+ }
+
+ importFBX ( filepath: string, success: Function, error: Function ) {
+
+ SnackBar.error( "FBX files are not supported" );
+
+ // var loader = new THREE.FBXLoader();
+
+ // loader.load( 'assets/TrafficCone01.fbx', function ( object ) {
+ // // loader.load( 'assets/Box.fbx', function ( object ) {
+
+ // object.traverse( ( child: any ) => {
+
+ // if ( child.isMesh ) {
+
+ // child.castShadow = true;
+ // child.receiveShadow = true;
+
+ // }
+
+ // } );
+
+ // SceneService.add( object );
+
+ // }, ( e ) => console.debug( e ), ( e ) => console.error( e ) );
+
+ // this.fs.readFile( filepath, 'utf-8', ( err, data ) => {
+
+ // if ( err ) {
+ // error( 'An error ocurred reading the file :' + err.message );
+ // return;
+ // }
+
+ // try {
+
+ // success( loader.parse( data, filepath ) );
+
+ // } catch ( error ) {
+
+ // console.error( error );
+
+ // error( 'Could not import file, ' + error.message );
+
+ // }
+
+
+ // } );
+
+ }
+
+}
diff --git a/src/app/core/commands/add-connection-command.ts b/src/app/core/commands/add-connection-command.ts
new file mode 100644
index 00000000..cd331c89
--- /dev/null
+++ b/src/app/core/commands/add-connection-command.ts
@@ -0,0 +1,90 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseCommand } from './base-command';
+import { TvRoad } from 'app/modules/tv-map/models/tv-road.model';
+import { TvLaneSide } from 'app/modules/tv-map/models/tv-common';
+import { SceneService } from '../services/scene.service';
+import { JunctionEntryObject } from 'app/modules/three-js/objects/junction-entry.object';
+import { TvJunction } from 'app/modules/tv-map/models/tv-junction';
+import { TvJunctionLaneLink } from 'app/modules/tv-map/models/tv-junction-lane-link';
+import { TvJunctionConnection } from 'app/modules/tv-map/models/tv-junction-connection';
+import { RoadFactory } from '../factories/road-factory.service';
+import { ManeuverTool } from '../tools/maneuver-tool';
+import { LanePathFactory } from '../factories/lane-path-factory.service';
+
+export class AddConnectionCommand extends BaseCommand {
+
+ private connectingRoad: TvRoad;
+ private connection: TvJunctionConnection;
+ private link: TvJunctionLaneLink;
+
+ constructor (
+ private entry: JunctionEntryObject,
+ private exit: JunctionEntryObject,
+ private junction: TvJunction,
+ private tool: ManeuverTool
+ ) {
+
+ super();
+
+ }
+
+ execute (): void {
+
+ const laneWidth = this.entry.lane.getWidthValue( 0 );
+
+ this.connectingRoad = this.tool.createConnectingRoad( this.entry, this.exit, TvLaneSide.RIGHT, laneWidth, this.junction );
+
+ const result = this.tool.createConnections( this.junction, this.entry, this.connectingRoad, this.exit );
+
+ this.connection = result.connection;
+
+ this.link = result.link;
+
+ const lane = this.connectingRoad.getFirstLaneSection().getLaneById( -1 );
+
+ // tslint:disable-next-line: max-line-length
+ this.link.lanePath = LanePathFactory.createPathForLane( this.entry.road, this.connectingRoad, lane, result.connection, result.link );
+
+ this.tool.connectingRoad = this.connectingRoad;
+
+ this.tool.lanePathObject = result.link.lanePath;
+
+ RoadFactory.rebuildRoad( this.connectingRoad );
+
+ SceneService.add( result.link.lanePath );
+ }
+
+ undo (): void {
+
+ this.openDrive.removeRoad( this.connectingRoad );
+
+ this.junction.removeConnection( this.connection, this.entry.road, this.exit.road );
+
+ this.tool.connectingRoad = null;
+
+ this.tool.lanePathObject = null;
+
+ SceneService.remove( this.link.lanePath );
+
+ }
+
+ redo (): void {
+
+ this.openDrive.addRoadInstance( this.connectingRoad );
+
+ this.junction.addConnection( this.connection );
+
+ this.tool.updateNeighbors( this.junction, this.entry, this.connectingRoad, this.exit );
+
+ this.tool.connectingRoad = this.connectingRoad;
+
+ this.tool.lanePathObject = this.link.lanePath;
+
+ RoadFactory.rebuildRoad( this.connectingRoad );
+
+ SceneService.add( this.link.lanePath );
+ }
+}
\ No newline at end of file
diff --git a/src/app/core/commands/add-lane-command.ts b/src/app/core/commands/add-lane-command.ts
new file mode 100644
index 00000000..f0c7e520
--- /dev/null
+++ b/src/app/core/commands/add-lane-command.ts
@@ -0,0 +1,37 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseCommand } from './base-command';
+import { TvLaneSection } from '../../modules/tv-map/models/tv-lane-section';
+import { TvLane } from '../../modules/tv-map/models/tv-lane';
+import { TvMapSourceFile } from '../../modules/tv-map/services/tv-map-source-file';
+
+export class AddLaneCommand extends BaseCommand {
+
+ constructor ( private laneSection: TvLaneSection, private lane: TvLane ) {
+ super();
+ }
+
+ execute (): void {
+
+ this.laneSection.addLaneInstance( this.lane, true );
+
+ TvMapSourceFile.roadNetworkChanged.emit( this.openDrive );
+ }
+
+ undo (): void {
+
+ this.laneSection.removeLaneById( this.lane.id );
+
+ TvMapSourceFile.roadNetworkChanged.emit( this.openDrive );
+
+ }
+
+ redo (): void {
+
+ this.execute();
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/app/core/commands/add-lane-offset-command.ts b/src/app/core/commands/add-lane-offset-command.ts
new file mode 100644
index 00000000..e1e0d3e8
--- /dev/null
+++ b/src/app/core/commands/add-lane-offset-command.ts
@@ -0,0 +1,43 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseCommand } from './base-command';
+import { NodeFactoryService } from '../factories/node-factory.service';
+import { SceneService } from '../services/scene.service';
+import { SetInspectorCommand } from './set-inspector-command';
+import { LaneOffsetNode } from 'app/modules/three-js/objects/control-point';
+
+export class AddLaneOffsetCommand extends BaseCommand {
+
+ private command: SetInspectorCommand;
+
+ constructor ( private node: LaneOffsetNode ) {
+
+ super();
+
+ }
+
+ execute (): void {
+
+ this.node.road.addLaneOffsetInstance( this.node.laneOffset );
+
+ SceneService.add( this.node );
+
+ // ( new SetInspectorCommand( LaneOffsetInspector, new LaneOffsetInspectorData( this.node, this.node.road ) ) ).execute();
+ }
+
+ undo (): void {
+
+ this.node.road.removeLaneOffset( this.node.laneOffset );
+
+ SceneService.remove( this.node );
+ }
+
+ redo (): void {
+
+ this.execute();
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/app/core/commands/add-object-command.ts b/src/app/core/commands/add-object-command.ts
new file mode 100644
index 00000000..337c5312
--- /dev/null
+++ b/src/app/core/commands/add-object-command.ts
@@ -0,0 +1,35 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseCommand } from './base-command';
+import { Object3D } from 'three';
+import { SceneService } from '../services/scene.service';
+
+export class AddObjectCommand extends BaseCommand {
+
+ constructor ( private object: Object3D ) {
+
+ super();
+
+ }
+
+ execute (): void {
+
+ SceneService.add( this.object );
+
+ }
+
+ undo (): void {
+
+ SceneService.remove( this.object );
+
+ }
+
+ redo (): void {
+
+ this.execute();
+
+ }
+
+}
diff --git a/src/app/core/commands/add-road-command.ts b/src/app/core/commands/add-road-command.ts
new file mode 100644
index 00000000..ad62f2c0
--- /dev/null
+++ b/src/app/core/commands/add-road-command.ts
@@ -0,0 +1,53 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { OdBaseCommand } from './od-base-command';
+import { TvRoad } from '../../modules/tv-map/models/tv-road.model';
+import { AppInspector } from 'app/core/inspector';
+import { RoadInspector } from '../../views/inspectors/road-inspector/road-inspector.component';
+import { SceneService } from 'app/core/services/scene.service';
+import { RoadControlPoint } from 'app/modules/three-js/objects/road-control-point';
+
+export class AddRoadCommand extends OdBaseCommand {
+
+ constructor ( private road: TvRoad, private point: RoadControlPoint ) {
+
+ super();
+
+ }
+
+ execute (): void {
+
+ AppInspector.setInspector( RoadInspector, { road: this.road, controlPoint: this.point } );
+
+ }
+
+ undo (): void {
+
+ this.road.spline.removeControlPoint( this.point );
+
+ SceneService.remove( this.point );
+
+ this.road.spline.hide();
+
+ this.openDrive.removeRoad( this.road );
+
+ AppInspector.clear();
+ }
+
+ redo (): void {
+
+ this.road.spline.addControlPoint( this.point );
+
+ SceneService.add( this.point );
+
+ this.road.spline.show();
+
+ this.openDrive.addRoadInstance( this.road );
+
+ AppInspector.setInspector( RoadInspector, { road: this.road, controlPoint: this.point } );
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/app/core/commands/add-road-geometry-command.ts b/src/app/core/commands/add-road-geometry-command.ts
new file mode 100644
index 00000000..ce2ed890
--- /dev/null
+++ b/src/app/core/commands/add-road-geometry-command.ts
@@ -0,0 +1,38 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { OdBaseCommand } from './od-base-command';
+import { TvRoad } from '../../modules/tv-map/models/tv-road.model';
+import { TvAbstractRoadGeometry } from '../../modules/tv-map/models/geometries/tv-abstract-road-geometry';
+import { TvMapBuilder } from '../../modules/tv-map/builders/od-builder.service';
+
+export class AddRoadGeometryCommand extends OdBaseCommand {
+
+ constructor ( private road: TvRoad, private geometry: TvAbstractRoadGeometry ) {
+ super();
+ }
+
+ execute (): void {
+
+ this.road.addGeometry( this.geometry );
+
+ TvMapBuilder.buildRoad( this.openDrive.gameObject, this.road );
+
+ }
+
+ undo (): void {
+
+ this.road.removeGeometryByUUID( this.geometry.uuid );
+
+ TvMapBuilder.buildRoad( this.openDrive.gameObject, this.road );
+ }
+
+ redo (): void {
+
+ this.execute();
+
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/app/core/commands/add-road-object-command.ts b/src/app/core/commands/add-road-object-command.ts
new file mode 100644
index 00000000..bdd48d3a
--- /dev/null
+++ b/src/app/core/commands/add-road-object-command.ts
@@ -0,0 +1,42 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvRoadObject } from '../../modules/tv-map/models/tv-road-object';
+import { OdBaseCommand } from './od-base-command';
+import { SceneService } from '../services/scene.service';
+import { Object3D } from 'three';
+
+export class AddRoadObjectCommand extends OdBaseCommand {
+
+ constructor ( private roadId: number, private roadObject: TvRoadObject, private objects: Object3D[] = [] ) {
+ super();
+ }
+
+ execute (): void {
+
+ SceneService.add( this.roadObject.mesh, false );
+
+ this.openDrive.getRoadById( this.roadId ).addRoadObjectInstance( this.roadObject );
+ }
+
+ undo (): void {
+
+ SceneService.remove( this.roadObject.mesh, false );
+
+ this.openDrive.getRoadById( this.roadId ).removeRoadObjectById( this.roadObject.id );
+
+ this.objects.forEach( object => SceneService.remove( object ) );
+
+ }
+
+ redo (): void {
+
+ this.execute();
+
+ this.objects.forEach( object => SceneService.add( object ) );
+
+ }
+
+
+}
diff --git a/src/app/core/commands/add-road-point-command.ts b/src/app/core/commands/add-road-point-command.ts
new file mode 100644
index 00000000..62cc49be
--- /dev/null
+++ b/src/app/core/commands/add-road-point-command.ts
@@ -0,0 +1,41 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { OdBaseCommand } from './od-base-command';
+import { TvRoad } from '../../modules/tv-map/models/tv-road.model';
+import { RoadFactory } from 'app/core/factories/road-factory.service';
+import { AppInspector } from 'app/core/inspector';
+import { RoadInspector } from '../../views/inspectors/road-inspector/road-inspector.component';
+import { RoadControlPoint } from 'app/modules/three-js/objects/road-control-point';
+
+export class AddRoadPointCommand extends OdBaseCommand {
+
+ constructor ( private road: TvRoad, private newPoint: RoadControlPoint, private oldPoint: RoadControlPoint ) {
+
+ super();
+
+ }
+
+ execute (): void {
+
+ AppInspector.setInspector( RoadInspector, { road: this.road, controlPoint: this.newPoint } );
+
+ }
+
+ undo (): void {
+
+ RoadFactory.removeControlPoint( this.road, this.newPoint );
+
+ AppInspector.setInspector( RoadInspector, { road: this.road, controlPoint: this.oldPoint } );
+
+ }
+
+ redo (): void {
+
+ RoadFactory.addControlPointNew( this.road, this.newPoint );
+
+ AppInspector.setInspector( RoadInspector, { road: this.road, controlPoint: this.newPoint } );
+ }
+
+}
\ No newline at end of file
diff --git a/src/app/core/commands/add-roadmark-node.ts b/src/app/core/commands/add-roadmark-node.ts
new file mode 100644
index 00000000..3c73c69f
--- /dev/null
+++ b/src/app/core/commands/add-roadmark-node.ts
@@ -0,0 +1,51 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseCommand } from './base-command';
+import { SceneService } from '../services/scene.service';
+import { TvLane } from 'app/modules/tv-map/models/tv-lane';
+import { OdRoadMarkBuilder } from 'app/modules/tv-map/builders/od-road-mark-builder';
+import { TvRoad } from 'app/modules/tv-map/models/tv-road.model';
+import { TvLaneRoadMark } from 'app/modules/tv-map/models/tv-lane-road-mark';
+
+export class AddRoadmarkNodeCommand extends BaseCommand {
+
+ private road: TvRoad;
+
+ constructor ( private lane: TvLane, private roadMark: TvLaneRoadMark, private roadMarkbuilder: OdRoadMarkBuilder ) {
+
+ super();
+
+ this.road = this.openDrive.getRoadById( this.lane.roadId );
+
+ }
+
+ execute (): void {
+
+ this.lane.addRoadMarkInstance( this.roadMark );
+
+ SceneService.add( this.roadMark.node );
+
+ this.roadMarkbuilder.buildRoad( this.road );
+
+ }
+
+ undo (): void {
+
+ const index = this.lane.roadMark.findIndex( roadmark => roadmark.uuid === this.roadMark.uuid );
+
+ this.lane.roadMark.splice( index, 1 );
+
+ SceneService.remove( this.roadMark.node );
+
+ this.roadMarkbuilder.buildRoad( this.road );
+ }
+
+ redo (): void {
+
+ this.execute();
+
+ }
+
+}
diff --git a/src/app/core/commands/add-signal-command.ts b/src/app/core/commands/add-signal-command.ts
new file mode 100644
index 00000000..de493b06
--- /dev/null
+++ b/src/app/core/commands/add-signal-command.ts
@@ -0,0 +1,47 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { OdBaseCommand } from './od-base-command';
+import { TvRoadSignal } from '../../modules/tv-map/models/tv-road-signal.model';
+import { ObjectSelection } from 'app/core/selection';
+import { TvMapBuilder } from '../../modules/tv-map/builders/od-builder.service';
+
+export class AddSignalCommand extends OdBaseCommand {
+
+ constructor ( public signal: TvRoadSignal ) {
+ super();
+ }
+
+ get road () {
+
+ return this.openDrive.getRoadById( this.signal.roadId );
+
+ }
+
+ execute (): void {
+
+ TvMapBuilder.makeRoadSignal( this.road, this.signal );
+
+ this.road.addSignal( this.signal );
+
+ // ObjectSelection.ActiveGameObject = this.signal.GameObject;
+
+ }
+
+ undo (): void {
+
+ this.signal.gameObject.parent.remove( this.signal.gameObject );
+
+ this.road.removeSignal( this.signal );
+
+ ObjectSelection.removeActive();
+
+ }
+
+ redo (): void {
+
+ this.execute();
+
+ }
+}
diff --git a/src/app/core/commands/add-width-node-command.ts b/src/app/core/commands/add-width-node-command.ts
new file mode 100644
index 00000000..b59b8a44
--- /dev/null
+++ b/src/app/core/commands/add-width-node-command.ts
@@ -0,0 +1,74 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseCommand } from './base-command';
+import { TvRoad } from 'app/modules/tv-map/models/tv-road.model';
+import { LaneWidthNode } from 'app/modules/three-js/objects/control-point';
+import { LineType, OdLaneReferenceLineBuilder } from 'app/modules/tv-map/builders/od-lane-reference-line-builder';
+import { SceneService } from '../services/scene.service';
+import { TvMapBuilder } from 'app/modules/tv-map/builders/od-builder.service';
+import { SnackBar } from 'app/services/snack-bar.service';
+
+export class AddWidthNodeCommand extends BaseCommand {
+
+ constructor (
+ private node: LaneWidthNode,
+ private laneHelper: OdLaneReferenceLineBuilder
+ ) {
+
+ super();
+
+ }
+
+ execute (): void {
+
+ this.node.updateLaneWidthValues();
+
+ SceneService.add( this.node );
+
+ this.rebuild( this.node.road );
+
+ }
+
+ undo (): void {
+
+ const index = this.node.lane.width.findIndex( laneWidth => laneWidth.uuid === this.node.laneWidth.uuid );
+
+ if ( index === -1 ) SnackBar.error( "Unexpected error. Not able to find this node" );
+ if ( index === -1 ) return;
+
+ this.node.lane.width.splice( index, 1 );
+
+ this.node.updateLaneWidthValues();
+
+ SceneService.remove( this.node );
+
+ this.rebuild( this.node.road );
+
+ }
+
+ redo (): void {
+
+ this.node.lane.addWidthRecordInstance( this.node.laneWidth );
+
+ this.node.updateLaneWidthValues();
+
+ SceneService.add( this.node );
+
+ this.rebuild( this.node.road );
+
+ }
+
+ rebuild ( road: TvRoad ): void {
+
+ SceneService.removeWithChildren( road.gameObject, true );
+
+ TvMapBuilder.buildRoad( this.openDrive.gameObject, road );
+
+ // not sure which is better
+ // this.laneHelper.redraw();
+ this.laneHelper.drawRoad( road, LineType.DASHED );
+ }
+
+}
diff --git a/src/app/core/commands/base-command.ts b/src/app/core/commands/base-command.ts
new file mode 100644
index 00000000..883638bf
--- /dev/null
+++ b/src/app/core/commands/base-command.ts
@@ -0,0 +1,23 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { ICommand, ICommandCallback } from './i-command';
+import { TvMapSourceFile } from '../../modules/tv-map/services/tv-map-source-file';
+
+
+export abstract class BaseCommand implements ICommand {
+
+ callbacks?: ICommandCallback;
+
+ abstract execute (): void;
+
+ abstract undo (): void;
+
+ abstract redo (): void;
+
+ get openDrive () {
+ return TvMapSourceFile.openDrive;
+ }
+
+}
diff --git a/src/app/core/commands/delete-link-command.ts b/src/app/core/commands/delete-link-command.ts
new file mode 100644
index 00000000..dbde33b6
--- /dev/null
+++ b/src/app/core/commands/delete-link-command.ts
@@ -0,0 +1,79 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseCommand } from './base-command';
+import { SceneService } from '../services/scene.service';
+import { LanePathObject, TvJunctionLaneLink } from 'app/modules/tv-map/models/tv-junction-lane-link';
+import { TvJunctionConnection } from 'app/modules/tv-map/models/tv-junction-connection';
+import { TvRoad } from 'app/modules/tv-map/models/tv-road.model';
+import { RoadFactory } from '../factories/road-factory.service';
+import { SnackBar } from 'app/services/snack-bar.service';
+
+export class DeleteLinkCommand extends BaseCommand {
+
+ private connectingRoad: TvRoad;
+
+ constructor ( private connection: TvJunctionConnection, private link: TvJunctionLaneLink, private lanePathObject: LanePathObject ) {
+
+ super();
+
+ }
+
+ execute (): void {
+
+ if ( !this.link.lanePath ) SnackBar.error( "Link mesh not found" );
+ if ( !this.link.lanePath ) return;
+
+ const index = this.connection.laneLink.findIndex( item => item.lanePath.id === this.link.lanePath.id );
+
+ if ( index === -1 ) SnackBar.error( "Link index not found" );
+ if ( index === -1 ) return;
+
+ this.connectingRoad = this.openDrive.getRoadById( this.connection.connectingRoad );
+
+ this.openDrive.removeRoad( this.connectingRoad );
+
+ this.link.hide();
+
+ SceneService.remove( this.link.lanePath );
+
+ this.connection.removeLinkAtIndex( index );
+
+ const junction = this.openDrive.getJunctionById( this.connectingRoad.junction );
+
+ if ( !junction ) SnackBar.error( "Junction not found");
+
+ const outgoingRoad = this.openDrive.getRoadById( this.connectingRoad.successor.elementId );
+
+ junction.removeConnection( this.connection, this.lanePathObject.incomingRoad, outgoingRoad );
+ }
+
+ undo (): void {
+
+ if ( !this.link.lanePath ) return;
+
+ this.link.lanePath.show();
+
+ SceneService.add( this.link.lanePath );
+
+ this.openDrive.addRoadInstance( this.connectingRoad );
+
+ this.connection.addLaneLink( this.link );
+
+ const junction = this.openDrive.getJunctionById( this.connectingRoad.junction );
+
+ if ( !junction ) console.error( "junction not found with id ", this.connectingRoad.junction );
+
+ junction.addConnection( this.connection );
+
+ RoadFactory.rebuildRoad( this.connectingRoad );
+ }
+
+ redo (): void {
+
+ this.execute();
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/app/core/commands/i-command.ts b/src/app/core/commands/i-command.ts
new file mode 100644
index 00000000..81a2e17c
--- /dev/null
+++ b/src/app/core/commands/i-command.ts
@@ -0,0 +1,25 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+export interface ICommand {
+
+ callbacks?: ICommandCallback;
+
+ execute (): void;
+
+ undo (): void;
+
+ redo (): void;
+
+}
+
+export interface ICommandCallback {
+
+ onExecute (): void;
+
+ onUndo (): void;
+
+ onRedo (): void;
+
+}
\ No newline at end of file
diff --git a/src/app/core/commands/join-road-node-command.ts b/src/app/core/commands/join-road-node-command.ts
new file mode 100644
index 00000000..8e556a94
--- /dev/null
+++ b/src/app/core/commands/join-road-node-command.ts
@@ -0,0 +1,70 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { OdBaseCommand } from './od-base-command';
+import { TvRoad } from '../../modules/tv-map/models/tv-road.model';
+import { AppInspector } from 'app/core/inspector';
+import { RoadInspector } from '../../views/inspectors/road-inspector/road-inspector.component';
+import { RoadFactory } from 'app/core/factories/road-factory.service';
+import { RoadNode } from 'app/modules/three-js/objects/road-node';
+
+export class JoinRoadNodeCommand extends OdBaseCommand {
+
+ private joiningRoad: TvRoad;
+ private lastInspector;
+ private lastInspectorData;
+
+ constructor (
+ private firstNode: RoadNode,
+ private secondNode: RoadNode
+ ) {
+
+ super();
+
+ this.lastInspector = AppInspector.currentInspector;
+ this.lastInspectorData = AppInspector.currentInspectorData;
+
+ }
+
+ get firstRoad () { return this.openDrive.getRoadById( this.firstNode.roadId ) }
+
+ get secondRoad () { return this.openDrive.getRoadById( this.secondNode.roadId ) }
+
+ execute (): void {
+
+ this.joiningRoad = RoadFactory.joinRoadNodes( this.firstRoad, this.firstNode, this.secondRoad, this.secondNode );
+
+ AppInspector.setInspector( RoadInspector, { road: this.joiningRoad, node: this.secondNode } );
+
+ }
+
+ undo (): void {
+
+ // remove from opendrive
+ // remove and clear the splines points
+ // remove the entire game object
+
+ this.openDrive.roads.delete( this.joiningRoad.id );
+
+ this.openDrive.gameObject.remove( this.joiningRoad.gameObject );
+
+ RoadFactory.removeRoadConnections( this.firstRoad, this.joiningRoad );
+ RoadFactory.removeRoadConnections( this.secondRoad, this.joiningRoad );
+
+ AppInspector.setInspector( this.lastInspector, this.lastInspectorData );
+ }
+
+ redo (): void {
+
+ this.openDrive.roads.set( this.joiningRoad.id, this.joiningRoad );
+
+ this.openDrive.gameObject.add( this.joiningRoad.gameObject );
+
+ RoadFactory.makeRoadConnections( this.firstRoad, this.firstNode, this.secondRoad, this.secondNode, this.joiningRoad );
+
+ AppInspector.setInspector( RoadInspector, { road: this.joiningRoad, node: this.secondNode } );
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/app/core/commands/multi-cmds-command.ts b/src/app/core/commands/multi-cmds-command.ts
new file mode 100644
index 00000000..aa74222e
--- /dev/null
+++ b/src/app/core/commands/multi-cmds-command.ts
@@ -0,0 +1,32 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseCommand } from './base-command';
+import { ICommand } from './i-command';
+
+export class MultiCmdsCommand extends BaseCommand {
+
+ constructor ( private commands: ICommand[] ) {
+ super();
+ }
+
+ execute (): void {
+
+ this.commands.forEach( command => command.execute() );
+
+ }
+
+ undo (): void {
+
+ this.commands.forEach( command => command.undo() );
+
+ }
+
+ redo (): void {
+
+ this.commands.forEach( command => command.redo() );
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/app/core/commands/od-base-command.ts b/src/app/core/commands/od-base-command.ts
new file mode 100644
index 00000000..60498646
--- /dev/null
+++ b/src/app/core/commands/od-base-command.ts
@@ -0,0 +1,21 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseCommand } from 'app/core/commands/base-command';
+import { TvMapSourceFile } from '../../modules/tv-map/services/tv-map-source-file';
+import { TvRoad } from '../../modules/tv-map/models/tv-road.model';
+
+export abstract class OdBaseCommand extends BaseCommand {
+
+ abstract execute (): void;
+
+ abstract undo (): void;
+
+ abstract redo (): void;
+
+ protected getRoad ( roadId: number ): TvRoad {
+ return TvMapSourceFile.openDrive.getRoadById( roadId );
+ }
+
+}
diff --git a/src/app/core/commands/remove-lane-command.ts b/src/app/core/commands/remove-lane-command.ts
new file mode 100644
index 00000000..c8a4495d
--- /dev/null
+++ b/src/app/core/commands/remove-lane-command.ts
@@ -0,0 +1,40 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseCommand } from './base-command';
+import { TvLaneSection } from '../../modules/tv-map/models/tv-lane-section';
+import { TvLane } from '../../modules/tv-map/models/tv-lane';
+import { TvMapSourceFile } from '../../modules/tv-map/services/tv-map-source-file';
+
+export class RemoveLaneCommand extends BaseCommand {
+
+ constructor ( private laneSection: TvLaneSection, private lane: TvLane ) {
+
+ super();
+
+ }
+
+ execute (): void {
+
+ this.laneSection.removeLaneById( this.lane.id );
+
+ TvMapSourceFile.roadNetworkChanged.emit( this.openDrive );
+
+ }
+
+ undo (): void {
+
+ this.laneSection.addLaneInstance( this.lane, true );
+
+ TvMapSourceFile.roadNetworkChanged.emit( this.openDrive );
+
+ }
+
+ redo (): void {
+
+ this.execute();
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/app/core/commands/remove-roadmark-command.ts b/src/app/core/commands/remove-roadmark-command.ts
new file mode 100644
index 00000000..4336876b
--- /dev/null
+++ b/src/app/core/commands/remove-roadmark-command.ts
@@ -0,0 +1,71 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseCommand } from './base-command';
+import { TvLaneRoadMark } from '../../modules/tv-map/models/tv-lane-road-mark';
+import { TvLane } from '../../modules/tv-map/models/tv-lane';
+import { OdRoadMarkBuilder } from '../../modules/tv-map/builders/od-road-mark-builder';
+
+export class RemoveRoadmarkCommand extends BaseCommand {
+
+ private index: number;
+
+ private roadMarkBuilder = new OdRoadMarkBuilder( null );
+
+ constructor ( private roadmark: TvLaneRoadMark, private lane: TvLane ) {
+
+ super();
+
+ }
+
+ execute (): void {
+
+ this.lane.gameObject.remove( this.roadmark.gameObject );
+
+ this.removeFromLane();
+
+ this.rebuild();
+ }
+
+ undo (): void {
+
+ this.lane.gameObject.add( this.roadmark.gameObject );
+
+ this.lane.addRoadMarkInstance( this.roadmark );
+
+ this.rebuild();
+ }
+
+ redo (): void {
+
+ this.execute();
+
+ }
+
+ private rebuild () {
+
+ this.openDrive.roads.forEach( road => {
+
+ this.roadMarkBuilder.buildRoad( road );
+
+ } );
+
+ }
+
+ private removeFromLane () {
+
+ this.lane.getRoadMarks().forEach( ( ( value, i ) => {
+
+ if ( value.sOffset === this.roadmark.sOffset ) {
+
+ this.index = i;
+
+ }
+
+ } ) );
+
+ this.lane.getRoadMarks().splice( this.index, 1 );
+
+ }
+}
diff --git a/src/app/core/commands/remove-signal-command.ts b/src/app/core/commands/remove-signal-command.ts
new file mode 100644
index 00000000..34e31da1
--- /dev/null
+++ b/src/app/core/commands/remove-signal-command.ts
@@ -0,0 +1,43 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { OdBaseCommand } from './od-base-command';
+import { TvRoadSignal } from '../../modules/tv-map/models/tv-road-signal.model';
+import { TvMapBuilder } from '../../modules/tv-map/builders/od-builder.service';
+import { ObjectSelection } from 'app/core/selection';
+import { AppInspector } from '../inspector';
+
+export class RemoveSignalCommand extends OdBaseCommand {
+
+ constructor ( public signal: TvRoadSignal ) {
+ super();
+ }
+
+ execute (): void {
+
+ this.signal.gameObject.parent.remove( this.signal.gameObject );
+
+ this.getRoad( this.signal.roadId ).removeSignal( this.signal );
+
+ ObjectSelection.removeActive();
+
+ AppInspector.clear();
+ }
+
+ undo (): void {
+
+ TvMapBuilder.makeRoadSignal( this.getRoad( this.signal.roadId ), this.signal );
+
+ this.getRoad( this.signal.roadId ).addSignal( this.signal );
+
+ ObjectSelection.ActiveGameObject = this.signal.gameObject;
+
+ }
+
+ redo (): void {
+
+ this.execute();
+
+ }
+}
diff --git a/src/app/core/commands/remove-width-node-command.ts b/src/app/core/commands/remove-width-node-command.ts
new file mode 100644
index 00000000..15b3f9d5
--- /dev/null
+++ b/src/app/core/commands/remove-width-node-command.ts
@@ -0,0 +1,79 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseCommand } from './base-command';
+import { TvRoad } from 'app/modules/tv-map/models/tv-road.model';
+import { LaneWidthNode } from 'app/modules/three-js/objects/control-point';
+import { LineType, OdLaneReferenceLineBuilder } from 'app/modules/tv-map/builders/od-lane-reference-line-builder';
+import { SceneService } from '../services/scene.service';
+import { TvMapBuilder } from 'app/modules/tv-map/builders/od-builder.service';
+import { SnackBar } from 'app/services/snack-bar.service';
+import { SetInspectorCommand } from './set-inspector-command';
+import { LaneWidthInspector } from 'app/views/inspectors/lane-width-inspector/lane-width-inspector.component';
+
+export class RemoveWidthNodeCommand extends BaseCommand {
+
+ constructor (
+ private node: LaneWidthNode,
+ private laneHelper?: OdLaneReferenceLineBuilder
+ ) {
+
+ super();
+
+ if ( !laneHelper ) {
+
+ this.laneHelper = new OdLaneReferenceLineBuilder();
+
+ }
+
+ }
+
+ execute (): void {
+
+ const index = this.node.lane.width.findIndex( laneWidth => laneWidth.uuid === this.node.laneWidth.uuid );
+
+ if ( index === -1 ) SnackBar.error( "Unexpected error. Not able to find this node" );
+ if ( index === -1 ) return;
+
+ this.node.lane.width.splice( index, 1 );
+
+ this.node.updateLaneWidthValues();
+
+ SceneService.remove( this.node );
+
+ this.rebuild( this.node.road );
+
+ ( new SetInspectorCommand( LaneWidthInspector, { lane: this.node.lane } ) ).execute();
+ }
+
+ undo (): void {
+
+ this.node.lane.addWidthRecordInstance( this.node.laneWidth );
+
+ this.node.updateLaneWidthValues();
+
+ SceneService.add( this.node );
+
+ this.rebuild( this.node.road );
+
+ ( new SetInspectorCommand( LaneWidthInspector, { lane: this.node.lane, node: this.node } ) ).execute();
+
+ }
+
+ redo (): void {
+
+ this.execute();
+
+ }
+
+ rebuild ( road: TvRoad ): void {
+
+ SceneService.removeWithChildren( road.gameObject, true );
+
+ TvMapBuilder.buildRoad( this.openDrive.gameObject, road );
+
+ this.laneHelper.drawRoad( road, LineType.DASHED, true );
+ }
+
+}
diff --git a/src/app/core/commands/set-inspector-command.ts b/src/app/core/commands/set-inspector-command.ts
new file mode 100644
index 00000000..5b8e02be
--- /dev/null
+++ b/src/app/core/commands/set-inspector-command.ts
@@ -0,0 +1,41 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseCommand } from './base-command';
+import { AppInspector } from '../inspector';
+import { Type } from '@angular/core';
+import { IComponent } from '../game-object';
+
+export class SetInspectorCommand extends BaseCommand {
+
+ private oldInspector: Type;
+ private oldInspectorData: any;
+
+ constructor ( public newInspector: Type, public newInspectorData: any ) {
+
+ super();
+
+ this.oldInspector = AppInspector.currentInspector;
+
+ this.oldInspectorData = AppInspector.currentInspectorData;
+ }
+
+ execute (): void {
+
+ AppInspector.setInspector( this.newInspector, this.newInspectorData );
+
+ }
+
+ undo (): void {
+
+ AppInspector.setInspector( this.oldInspector, this.oldInspectorData );
+
+ }
+
+ redo (): void {
+
+ this.execute();
+
+ }
+}
diff --git a/src/app/core/commands/set-lane-property-command.ts b/src/app/core/commands/set-lane-property-command.ts
new file mode 100644
index 00000000..a14dd937
--- /dev/null
+++ b/src/app/core/commands/set-lane-property-command.ts
@@ -0,0 +1,38 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseCommand } from './base-command';
+import { TvLane } from '../../modules/tv-map/models/tv-lane';
+
+export class SetLanePropertyCommand extends BaseCommand {
+
+ private readonly oldValue: any;
+
+ constructor ( private lane: TvLane, private attribute: any, private newValue: any ) {
+
+ super();
+
+ this.oldValue = lane[ attribute ];
+ }
+
+ execute (): void {
+
+ this.lane[ this.attribute ] = this.newValue;
+
+ }
+
+ undo (): void {
+
+ this.lane[ this.attribute ] = this.oldValue;
+
+ }
+
+ redo (): void {
+
+ this.execute();
+
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/app/core/commands/set-roadmark-value-command.ts b/src/app/core/commands/set-roadmark-value-command.ts
new file mode 100644
index 00000000..123ecb22
--- /dev/null
+++ b/src/app/core/commands/set-roadmark-value-command.ts
@@ -0,0 +1,57 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseCommand } from './base-command';
+import { TvLaneRoadMark } from '../../modules/tv-map/models/tv-lane-road-mark';
+import { OdRoadMarkBuilder } from '../../modules/tv-map/builders/od-road-mark-builder';
+
+export class SetRoadmarkValueCommand extends BaseCommand {
+
+ private readonly oldValue: any;
+
+ private roadMarkBuilder = new OdRoadMarkBuilder();
+
+ constructor (
+ private roadmark: TvLaneRoadMark,
+ private attribute: any,
+ private newValue: any,
+ ) {
+
+ super();
+
+ this.oldValue = this.roadmark[ this.attribute ];
+
+ }
+
+ execute (): void {
+
+ this.roadmark[ this.attribute ] = this.newValue;
+
+ this.rebuild();
+
+ }
+
+ undo (): void {
+
+ this.roadmark[ this.attribute ] = this.oldValue;
+
+ this.rebuild();
+
+ }
+
+ redo (): void {
+
+ this.execute();
+
+ }
+
+ private rebuild () {
+
+ this.openDrive.roads.forEach( road => {
+
+ this.roadMarkBuilder.buildRoad( road );
+
+ } );
+ }
+}
diff --git a/src/app/core/commands/set-tool-command.ts b/src/app/core/commands/set-tool-command.ts
new file mode 100644
index 00000000..c1ce7303
--- /dev/null
+++ b/src/app/core/commands/set-tool-command.ts
@@ -0,0 +1,39 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseCommand } from './base-command';
+import { ToolManager } from 'app/core/tools/tool-manager';
+import { BaseTool } from 'app/core/tools/base-tool';
+
+export class SetToolCommand extends BaseCommand {
+
+ private oldTool: BaseTool;
+
+ constructor ( private newTool: BaseTool ) {
+
+ super();
+
+ this.oldTool = ToolManager.currentTool;
+
+ }
+
+ execute (): void {
+
+ ToolManager.currentTool = this.newTool;
+
+ }
+
+ undo (): void {
+
+ ToolManager.currentTool = this.oldTool;
+
+ }
+
+ redo (): void {
+
+ ToolManager.currentTool = this.newTool;
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/app/core/commands/show-lane-marking-command.ts b/src/app/core/commands/show-lane-marking-command.ts
new file mode 100644
index 00000000..5c6cde1a
--- /dev/null
+++ b/src/app/core/commands/show-lane-marking-command.ts
@@ -0,0 +1,123 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseCommand } from './base-command';
+import { TvLane } from 'app/modules/tv-map/models/tv-lane';
+import { OdLaneReferenceLineBuilder } from 'app/modules/tv-map/builders/od-lane-reference-line-builder';
+import { NodeFactoryService } from '../factories/node-factory.service';
+import { SceneService } from '../services/scene.service';
+import { TvRoad } from 'app/modules/tv-map/models/tv-road.model';
+import { SnackBar } from 'app/services/snack-bar.service';
+
+export class ShowLaneMarkingCommand extends BaseCommand {
+
+ private road: TvRoad;
+
+ private oldRoad: TvRoad;
+
+ constructor ( private lane: TvLane, private oldLane: TvLane, private laneHelper: OdLaneReferenceLineBuilder ) {
+
+ super();
+
+ if ( lane ) {
+
+ this.road = this.openDrive.getRoadById( this.lane.roadId );
+
+ }
+
+ if ( oldLane ) {
+
+ this.oldRoad = this.openDrive.getRoadById( this.oldLane.roadId );
+
+ }
+ }
+
+ execute (): void {
+
+ if ( this.oldRoad ) {
+
+ this.hideNodes( this.oldRoad );
+
+ }
+
+ if ( this.road ) {
+
+ this.showNodes( this.road );
+
+ }
+ }
+
+ undo (): void {
+
+ if ( this.road ) {
+
+ this.hideNodes( this.road );
+
+ }
+
+ if ( this.oldRoad ) {
+
+ this.showNodes( this.oldRoad );
+
+ }
+ }
+
+ redo (): void {
+
+ this.execute();
+
+ }
+
+ private showNodes ( road: TvRoad ) {
+
+ if ( road.isJunction ) SnackBar.error( "LaneMark Editing on junction roads is currently not supported" );
+
+ if ( road.isJunction ) return;
+
+ road.laneSections.forEach( laneSection => {
+
+ laneSection.lanes.forEach( lane => {
+
+ lane.getRoadMarks().forEach( roadmark => {
+
+ if ( roadmark.node ) {
+
+ roadmark.node.visible = true;
+
+ } else {
+
+ roadmark.node = NodeFactoryService.createRoadMarkNode( lane, roadmark );
+
+ SceneService.add( roadmark.node );
+
+ }
+
+ } )
+
+ } )
+
+ } );
+
+ this.laneHelper.drawRoad( road );
+ }
+
+ private hideNodes ( road: TvRoad ) {
+
+ road.laneSections.forEach( laneSection => {
+
+ laneSection.lanes.forEach( lane => {
+
+ lane.getRoadMarks().forEach( roadmark => {
+
+ if ( roadmark.node ) roadmark.node.visible = false;
+
+ } );
+
+ } );
+
+ } );
+
+ this.laneHelper.clear();
+ }
+}
\ No newline at end of file
diff --git a/src/app/core/commands/update-lane-offset-distance-command.ts b/src/app/core/commands/update-lane-offset-distance-command.ts
new file mode 100644
index 00000000..c962bbda
--- /dev/null
+++ b/src/app/core/commands/update-lane-offset-distance-command.ts
@@ -0,0 +1,69 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseCommand } from './base-command';
+import { LaneWidthNode, LaneOffsetNode } from 'app/modules/three-js/objects/control-point';
+import { NodeFactoryService } from '../factories/node-factory.service';
+import { SceneService } from '../services/scene.service';
+import { OdLaneReferenceLineBuilder, LineType } from 'app/modules/tv-map/builders/od-lane-reference-line-builder';
+import { TvRoad } from 'app/modules/tv-map/models/tv-road.model';
+import { TvMapBuilder } from 'app/modules/tv-map/builders/od-builder.service';
+
+export class UpdateLaneOffsetDistanceCommand extends BaseCommand {
+
+ constructor (
+ private node: LaneOffsetNode,
+ private newDistance: number,
+ private oldDistance?: number,
+ private laneHelper?: OdLaneReferenceLineBuilder
+ ) {
+
+ super();
+
+ if ( !this.oldDistance ) {
+
+ this.oldDistance = this.node.laneOffset.s;
+
+ }
+
+ }
+
+ execute (): void {
+
+ this.node.laneOffset.s = this.newDistance;
+
+ NodeFactoryService.updateLaneOffsetNode( this.node );
+
+ this.rebuild( this.node.road );
+
+
+ }
+
+ undo (): void {
+
+ this.node.laneOffset.s = this.oldDistance;
+
+ NodeFactoryService.updateLaneOffsetNode( this.node );
+
+ this.rebuild( this.node.road );
+
+ }
+
+ redo (): void {
+
+ this.execute();
+
+ }
+
+ rebuild ( road: TvRoad ): void {
+
+ SceneService.removeWithChildren( road.gameObject, true );
+
+ TvMapBuilder.buildRoad( this.openDrive.gameObject, road );
+
+ this.laneHelper.drawRoad( road, LineType.DASHED, true );
+
+ }
+
+}
diff --git a/src/app/core/commands/update-lane-offset-value-command.ts b/src/app/core/commands/update-lane-offset-value-command.ts
new file mode 100644
index 00000000..7318e86e
--- /dev/null
+++ b/src/app/core/commands/update-lane-offset-value-command.ts
@@ -0,0 +1,64 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseCommand } from './base-command';
+import { LaneOffsetNode } from 'app/modules/three-js/objects/control-point';
+import { SceneService } from '../services/scene.service';
+import { OdLaneReferenceLineBuilder, LineType } from 'app/modules/tv-map/builders/od-lane-reference-line-builder';
+import { TvRoad } from 'app/modules/tv-map/models/tv-road.model';
+import { TvMapBuilder } from 'app/modules/tv-map/builders/od-builder.service';
+
+export class UpdateLaneOffsetValueCommand extends BaseCommand {
+
+ constructor (
+ private node: LaneOffsetNode,
+ private newOffset: number,
+ private oldOffset?: number,
+ private laneHelper?: OdLaneReferenceLineBuilder
+ ) {
+
+ super();
+
+ if ( !this.oldOffset ) {
+
+ this.oldOffset = this.node.laneOffset.a;
+
+ }
+
+ }
+
+ execute (): void {
+
+ this.node.laneOffset.a = this.newOffset;
+
+ this.rebuild( this.node.road );
+
+
+ }
+
+ undo (): void {
+
+ this.node.laneOffset.a = this.oldOffset;
+
+ this.rebuild( this.node.road );
+
+ }
+
+ redo (): void {
+
+ this.execute();
+
+ }
+
+ rebuild ( road: TvRoad ): void {
+
+ SceneService.removeWithChildren( road.gameObject, true );
+
+ TvMapBuilder.buildRoad( this.openDrive.gameObject, road );
+
+ this.laneHelper.drawRoad( road, LineType.DASHED, true );
+
+ }
+
+}
diff --git a/src/app/core/commands/update-road-point-command.ts b/src/app/core/commands/update-road-point-command.ts
new file mode 100644
index 00000000..a8997329
--- /dev/null
+++ b/src/app/core/commands/update-road-point-command.ts
@@ -0,0 +1,105 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { OdBaseCommand } from './od-base-command';
+import { TvRoad } from '../../modules/tv-map/models/tv-road.model';
+import { RoadFactory } from 'app/core/factories/road-factory.service';
+import { Vector3 } from 'three';
+import { RoadControlPoint } from 'app/modules/three-js/objects/road-control-point';
+
+export class UpdateRoadPointCommand extends OdBaseCommand {
+
+ constructor (
+ private road: TvRoad,
+ private point: RoadControlPoint,
+ private newPosition: Vector3,
+ private oldPosition: Vector3
+ ) {
+
+ super();
+
+ this.newPosition = this.newPosition.clone();
+
+ this.oldPosition = this.oldPosition.clone();
+ }
+
+ execute (): void {
+
+ // chanhe position of point
+ this.point.copyPosition( this.newPosition );
+
+ // update spline
+ this.road.spline.update();
+
+ // update geometry
+ RoadFactory.updateGeometry( this.road );
+
+ // build road
+ RoadFactory.rebuildRoad( this.road );
+
+ if ( this.road.successor && this.road.successor.elementType !== "junction" ) {
+
+ const successor = this.openDrive.getRoadById( this.road.successor.elementId );
+
+ successor.updateGeometryFromSpline();
+
+ RoadFactory.rebuildRoad( successor );
+
+ }
+
+ if ( this.road.predecessor && this.road.predecessor.elementType !== "junction" ) {
+
+ const predecessor = this.openDrive.getRoadById( this.road.predecessor.elementId );
+
+ predecessor.updateGeometryFromSpline();
+
+ RoadFactory.rebuildRoad( predecessor );
+
+ }
+
+ }
+
+ undo (): void {
+
+ // chanhe position of point
+ this.point.copyPosition( this.oldPosition );
+
+ // update spline
+ this.road.spline.update();
+
+ // update geometry
+ RoadFactory.updateGeometry( this.road );
+
+ // build road
+ RoadFactory.rebuildRoad( this.road );
+
+ if ( this.road.successor && this.road.successor.elementType !== "junction" ) {
+
+ const successor = this.openDrive.getRoadById( this.road.successor.elementId );
+
+ successor.updateGeometryFromSpline();
+
+ RoadFactory.rebuildRoad( successor );
+
+ }
+
+ if ( this.road.predecessor && this.road.predecessor.elementType !== "junction" ) {
+
+ const predecessor = this.openDrive.getRoadById( this.road.predecessor.elementId );
+
+ predecessor.updateGeometryFromSpline();
+
+ RoadFactory.rebuildRoad( predecessor );
+
+ }
+
+ }
+
+ redo (): void {
+
+ this.execute();
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/app/core/commands/update-roadmark-node.ts b/src/app/core/commands/update-roadmark-node.ts
new file mode 100644
index 00000000..4e6b8627
--- /dev/null
+++ b/src/app/core/commands/update-roadmark-node.ts
@@ -0,0 +1,46 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseCommand } from './base-command';
+import { OdRoadMarkBuilder } from 'app/modules/tv-map/builders/od-road-mark-builder';
+import { Vector3 } from 'three';
+import { LaneRoadMarkNode } from 'app/modules/three-js/objects/control-point';
+import { NodeFactoryService } from '../factories/node-factory.service';
+
+export class UpdateRoadmarkNodeCommand extends BaseCommand {
+
+ constructor (
+ private node: LaneRoadMarkNode,
+ private newPosition: Vector3,
+ private oldPosition: Vector3,
+ private roadMarkbuilder: OdRoadMarkBuilder
+ ) {
+
+ super();
+
+ }
+
+ execute (): void {
+
+ NodeFactoryService.updateRoadMarkNodeByPosition( this.node, this.newPosition );
+
+ this.roadMarkbuilder.buildRoad( this.openDrive.getRoadById( this.node.lane.roadId ) );
+
+ }
+
+ undo (): void {
+
+ NodeFactoryService.updateRoadMarkNodeByPosition( this.node, this.oldPosition );
+
+ this.roadMarkbuilder.buildRoad( this.openDrive.getRoadById( this.node.lane.roadId ) );
+
+ }
+
+ redo (): void {
+
+ this.execute();
+
+ }
+
+}
diff --git a/src/app/core/commands/update-width-node-distance-command.ts b/src/app/core/commands/update-width-node-distance-command.ts
new file mode 100644
index 00000000..4b784d74
--- /dev/null
+++ b/src/app/core/commands/update-width-node-distance-command.ts
@@ -0,0 +1,73 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseCommand } from './base-command';
+import { TvRoad } from 'app/modules/tv-map/models/tv-road.model';
+import { LaneWidthNode } from 'app/modules/three-js/objects/control-point';
+import { NodeFactoryService } from '../factories/node-factory.service';
+import { LineType, OdLaneReferenceLineBuilder } from 'app/modules/tv-map/builders/od-lane-reference-line-builder';
+import { SceneService } from '../services/scene.service';
+import { TvMapBuilder } from 'app/modules/tv-map/builders/od-builder.service';
+
+export class UpdateWidthNodeDistanceCommand extends BaseCommand {
+
+ constructor (
+ private node: LaneWidthNode,
+ private newDistance: number,
+ private oldDistance?: number,
+ private laneHelper?: OdLaneReferenceLineBuilder
+ ) {
+
+ super();
+
+ if ( !this.oldDistance ) {
+
+ this.oldDistance = this.node.laneWidth.s;
+
+ }
+
+ }
+
+ execute (): void {
+
+ this.node.laneWidth.s = this.node.s = this.newDistance;
+
+ this.node.updateLaneWidthValues();
+
+ NodeFactoryService.updateLaneWidthNodeLine( this.node );
+
+ this.rebuild( this.node.road );
+
+
+ }
+
+ undo (): void {
+
+ this.node.laneWidth.s = this.node.s = this.oldDistance;
+
+ this.node.updateLaneWidthValues();
+
+ NodeFactoryService.updateLaneWidthNodeLine( this.node );
+
+ this.rebuild( this.node.road );
+
+ }
+
+ redo (): void {
+
+ this.execute();
+
+ }
+
+ rebuild ( road: TvRoad ): void {
+
+ SceneService.removeWithChildren( road.gameObject, true );
+
+ TvMapBuilder.buildRoad( this.openDrive.gameObject, road );
+
+ this.laneHelper.drawRoad( road, LineType.DASHED, true );
+
+ }
+
+}
diff --git a/src/app/core/commands/update-width-node-position-command.ts b/src/app/core/commands/update-width-node-position-command.ts
new file mode 100644
index 00000000..69acd881
--- /dev/null
+++ b/src/app/core/commands/update-width-node-position-command.ts
@@ -0,0 +1,62 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseCommand } from './base-command';
+import { TvRoad } from 'app/modules/tv-map/models/tv-road.model';
+import { Vector3 } from 'three';
+import { LaneWidthNode } from 'app/modules/three-js/objects/control-point';
+import { NodeFactoryService } from '../factories/node-factory.service';
+import { LineType, OdLaneReferenceLineBuilder } from 'app/modules/tv-map/builders/od-lane-reference-line-builder';
+import { SceneService } from '../services/scene.service';
+import { TvMapBuilder } from 'app/modules/tv-map/builders/od-builder.service';
+
+export class UpdateWidthNodePositionCommand extends BaseCommand {
+
+ constructor (
+ private node: LaneWidthNode,
+ private newPosition: Vector3,
+ private oldPosition: Vector3,
+ private laneHelper: OdLaneReferenceLineBuilder
+ ) {
+
+ super();
+
+ }
+
+ execute (): void {
+
+ NodeFactoryService.updateLaneWidthNode( this.node, this.newPosition );
+
+ this.node.updateLaneWidthValues();
+
+ this.rebuild( this.openDrive.getRoadById( this.node.roadId ) );
+ }
+
+ undo (): void {
+
+ NodeFactoryService.updateLaneWidthNode( this.node, this.oldPosition );
+
+ this.node.updateLaneWidthValues();
+
+ this.rebuild( this.openDrive.getRoadById( this.node.roadId ) );
+
+ }
+
+ redo (): void {
+
+ this.execute();
+
+ }
+
+ rebuild ( road: TvRoad ): void {
+
+ SceneService.removeWithChildren( road.gameObject, true );
+
+ TvMapBuilder.buildRoad( this.openDrive.gameObject, road );
+
+ this.laneHelper.drawRoad( road, LineType.DASHED, true );
+
+ }
+
+}
diff --git a/src/app/core/commands/update-width-node-value-command.ts b/src/app/core/commands/update-width-node-value-command.ts
new file mode 100644
index 00000000..b985d5b1
--- /dev/null
+++ b/src/app/core/commands/update-width-node-value-command.ts
@@ -0,0 +1,73 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseCommand } from './base-command';
+import { TvRoad } from 'app/modules/tv-map/models/tv-road.model';
+import { LaneWidthNode } from 'app/modules/three-js/objects/control-point';
+import { NodeFactoryService } from '../factories/node-factory.service';
+import { LineType, OdLaneReferenceLineBuilder } from 'app/modules/tv-map/builders/od-lane-reference-line-builder';
+import { SceneService } from '../services/scene.service';
+import { TvMapBuilder } from 'app/modules/tv-map/builders/od-builder.service';
+
+export class UpdateWidthNodeValueCommand extends BaseCommand {
+
+
+ constructor (
+ private node: LaneWidthNode,
+ private newWidth: number,
+ private oldWidth?: number,
+ private laneHelper?: OdLaneReferenceLineBuilder
+ ) {
+
+ super();
+
+ if ( !this.oldWidth ) {
+
+ this.oldWidth = this.node.laneWidth.a;
+
+ }
+
+ }
+
+ execute (): void {
+
+ this.node.laneWidth.a = this.newWidth;
+
+ this.node.updateLaneWidthValues();
+
+ NodeFactoryService.updateLaneWidthNodeLine( this.node );
+
+ this.rebuild( this.node.road );
+
+ }
+
+ undo (): void {
+
+ this.node.laneWidth.a = this.oldWidth;
+
+ this.node.updateLaneWidthValues();
+
+ NodeFactoryService.updateLaneWidthNodeLine( this.node );
+
+ this.rebuild( this.node.road );
+
+ }
+
+ redo (): void {
+
+ this.execute();
+
+ }
+
+ rebuild ( road: TvRoad ): void {
+
+ SceneService.removeWithChildren( road.gameObject, true );
+
+ TvMapBuilder.buildRoad( this.openDrive.gameObject, road );
+
+ this.laneHelper.drawRoad( road, LineType.DASHED, true );
+
+ }
+
+}
diff --git a/src/app/core/components/abstract-field.component.ts b/src/app/core/components/abstract-field.component.ts
new file mode 100644
index 00000000..5178790f
--- /dev/null
+++ b/src/app/core/components/abstract-field.component.ts
@@ -0,0 +1,72 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { EventEmitter, Input, Output } from '@angular/core';
+
+export abstract class AbstractFieldComponent {
+
+ @Input() disabled: boolean = false;
+
+ @Input() abstract value: any;
+
+ @Input() label: string = '';
+
+ /**
+ * @deprecated use changed event instead
+ */
+ @Output() valueChanged = new EventEmitter();
+
+ @Output() changed = new EventEmitter();
+
+ onModelChanged ( $event: any ) {
+
+ this.value = $event;
+
+ this.valueChanged.emit( this.value );
+
+ this.changed.emit( this.value );
+
+ }
+
+ get isParameter (): boolean {
+
+ return typeof this.value === 'string' && this.value.indexOf( '$' ) !== -1;
+
+ }
+
+ get fieldValue () {
+
+ if ( this.isParameter ) {
+
+ // return OscSourceFile.scenario.findParameter( this.value ).value;
+
+ }
+
+ return this.value;
+ }
+
+ set fieldValue ( value ) {
+
+ if ( this.isParameter ) {
+
+ // OscSourceFile.scenario.findParameter( this.value ).value = value;
+
+ } else {
+
+ this.value = value;
+
+ }
+
+ }
+
+ onFieldValueChanged ( $event: any ) {
+
+ this.fieldValue = $event;
+
+ if ( !this.isParameter ) this.valueChanged.emit( this.fieldValue );
+
+ if ( !this.isParameter ) this.changed.emit( this.fieldValue );
+
+ }
+}
diff --git a/src/app/core/components/base-inspector.component.ts b/src/app/core/components/base-inspector.component.ts
new file mode 100644
index 00000000..c81005d5
--- /dev/null
+++ b/src/app/core/components/base-inspector.component.ts
@@ -0,0 +1,33 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvMapSourceFile } from '../../modules/tv-map/services/tv-map-source-file';
+import { HostListener } from '@angular/core';
+
+enum KEY_CODE {
+ DELETE = 46
+}
+
+export abstract class BaseInspector {
+
+ get openDrive () {
+ return TvMapSourceFile.openDrive;
+ }
+
+ @HostListener( 'window:keydown', [ '$event' ] )
+ baseOnKeyDown ( event: KeyboardEvent ) {
+
+ if ( event.keyCode === KEY_CODE.DELETE ) {
+
+ this.onDelete();
+
+ }
+
+ }
+
+
+ onDelete () {
+
+ }
+}
\ No newline at end of file
diff --git a/src/app/core/components/mono-behaviour.ts b/src/app/core/components/mono-behaviour.ts
new file mode 100644
index 00000000..f6c550d9
--- /dev/null
+++ b/src/app/core/components/mono-behaviour.ts
@@ -0,0 +1,130 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { AppService } from '../services/app.service';
+import { BaseEventData, PointerEventData, PointerMoveData } from 'app/events/pointer-event-data';
+import { Subscription } from 'rxjs';
+
+export class MonoBehaviour {
+
+ subscribed: boolean = false;
+
+ pointerClickedSubscriber: Subscription;
+ pointerMovedSubscriber: Subscription;
+ pointerEnterSubscriber: Subscription;
+ pointerExitSubscriber: Subscription;
+ pointerUpSubscriber: Subscription;
+ pointerDownSubscriber: Subscription;
+ pointerLeaveSubscriber: Subscription;
+ pointerOutSubscriber: Subscription;
+ beginDragSubscriber: Subscription;
+ endDragSubscriber: Subscription;
+ dragSubscriber: Subscription;
+ dropSubscriber: Subscription;
+ selectSubscriber: Subscription;
+ deSelectSubscriber: Subscription;
+
+
+ constructor () {
+
+ this.subscribeToEvents();
+
+ }
+
+ subscribeToEvents () {
+
+ if ( this.subscribed ) return;
+
+ if ( !AppService.eventSystem ) return;
+
+ this.pointerClickedSubscriber = AppService.eventSystem.pointerClicked.subscribe( e => this.onPointerClicked( e ) );
+ this.pointerMovedSubscriber = AppService.eventSystem.pointerMoved.subscribe( e => this.onPointerMoved( e ) );
+ this.pointerEnterSubscriber = AppService.eventSystem.pointerEnter.subscribe( e => this.onPointerEnter( e ) );
+ this.pointerExitSubscriber = AppService.eventSystem.pointerExit.subscribe( e => this.onPointerExit( e ) );
+ this.pointerUpSubscriber = AppService.eventSystem.pointerUp.subscribe( e => this.onPointerUp( e ) );
+ this.pointerDownSubscriber = AppService.eventSystem.pointerDown.subscribe( e => this.onPointerDown( e ) );
+ this.pointerLeaveSubscriber = AppService.eventSystem.pointerLeave.subscribe( e => this.onPointerLeave( e ) );
+ this.pointerOutSubscriber = AppService.eventSystem.pointerOut.subscribe( e => this.onPointerOut( e ) );
+ this.beginDragSubscriber = AppService.eventSystem.beginDrag.subscribe( e => this.onBeginDrag( e ) );
+ this.endDragSubscriber = AppService.eventSystem.endDrag.subscribe( e => this.onEndDrag( e ) );
+ this.dragSubscriber = AppService.eventSystem.drag.subscribe( e => this.onDrag( e ) );
+ this.dropSubscriber = AppService.eventSystem.drop.subscribe( e => this.onDrop( e ) );
+ this.selectSubscriber = AppService.eventSystem.select.subscribe( e => this.onSelect( e ) );
+ this.deSelectSubscriber = AppService.eventSystem.deSelect.subscribe( e => this.onDeSelect( e ) );
+
+ this.subscribed = true;
+ }
+
+ unsubscribeToEvents () {
+
+ if ( !this.subscribed ) return;
+
+ this.pointerClickedSubscriber.unsubscribe();
+ this.pointerMovedSubscriber.unsubscribe();
+ this.pointerEnterSubscriber.unsubscribe();
+ this.pointerExitSubscriber.unsubscribe();
+ this.pointerUpSubscriber.unsubscribe();
+ this.pointerDownSubscriber.unsubscribe();
+ this.pointerLeaveSubscriber.unsubscribe();
+ this.pointerOutSubscriber.unsubscribe();
+ this.beginDragSubscriber.unsubscribe();
+ this.endDragSubscriber.unsubscribe();
+ this.dragSubscriber.unsubscribe();
+ this.dropSubscriber.unsubscribe();
+ this.selectSubscriber.unsubscribe();
+ this.deSelectSubscriber.unsubscribe();
+
+ this.subscribed = false;
+ }
+
+ onPointerClicked ( pointerEventData: PointerEventData ): void { /*Debug.log( 'clicked' )*/
+ }
+
+ onPointerMoved ( pointerEventData: PointerMoveData ): void { /*Debug.log( 'moved' )*/
+ }
+
+ onPointerEnter ( pointerEventData: PointerEventData ): void { /*Debug.log( 'enter' )*/
+ }
+
+ onPointerExit ( pointerEventData: PointerEventData ): void { /*Debug.log( 'exit' )*/
+ }
+
+ onPointerDown ( pointerEventData: PointerEventData ): void { /*Debug.log( 'down' )*/
+ }
+
+ onPointerUp ( pointerEventData: PointerEventData ): void { /*Debug.log( 'up' )*/
+ }
+
+ onPointerOut ( pointerEventData: PointerEventData ): void { /*Debug.log( 'out' )*/
+ }
+
+ onPointerLeave ( pointerEventData: PointerEventData ): void { /*Debug.log( 'leave' )*/
+ }
+
+ onBeginDrag ( pointerEventData: PointerEventData ): void { /*Debug.log( 'begin-drag' )*/
+ }
+
+ onEndDrag ( pointerEventData: PointerEventData ): void { /*Debug.log( 'end-drag' )*/
+ }
+
+ onDrag ( pointerEventData: PointerEventData ): void { /*Debug.log( 'drag' )*/
+ }
+
+ onDrop ( pointerEventData: PointerEventData ): void { /*Debug.log( 'drop' )*/
+ }
+
+ onDeSelect ( baseEventData: BaseEventData ): any {
+
+ // if ( baseEventData.object != null ) Debug.log( 'deselect', baseEventData.object.id );
+
+ }
+
+ onSelect ( baseEventData: BaseEventData ): any {
+
+ // Debug.log( 'selected', baseEventData.object.id );
+
+ }
+
+
+}
diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts
new file mode 100644
index 00000000..27777c86
--- /dev/null
+++ b/src/app/core/core.module.ts
@@ -0,0 +1,30 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+
+import { ApiService } from './services/api.service';
+import { HTTP_INTERCEPTORS } from '@angular/common/http';
+import { HttpTokenInterceptor } from './interceptors/http.token.interceptor';
+import { JwtService } from './services/jwt.service';
+import { MinPipe } from './pipes/min.pipe';
+import { MaxPipe } from './pipes/max.pipe';
+import { TimeOutInterceptor } from './interceptors/time-out-interceptor';
+
+@NgModule( {
+ declarations: [ MinPipe, MaxPipe ],
+ imports: [
+ CommonModule
+ ],
+ providers: [
+ { provide: HTTP_INTERCEPTORS, useClass: HttpTokenInterceptor, multi: true },
+ { provide: HTTP_INTERCEPTORS, useClass: TimeOutInterceptor, multi: true },
+ ApiService,
+ JwtService
+ ],
+ exports: [ MinPipe, MaxPipe ]
+} )
+export class CoreModule {
+}
diff --git a/src/app/core/editors/abstract-shape-editor.ts b/src/app/core/editors/abstract-shape-editor.ts
new file mode 100644
index 00000000..a16629b0
--- /dev/null
+++ b/src/app/core/editors/abstract-shape-editor.ts
@@ -0,0 +1,434 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { AppService } from 'app/core/services/app.service';
+import * as THREE from 'three';
+import { Color, Object3D, PointsMaterial, Vector2, Vector3 } from 'three';
+import { MouseButton, PointerEventData } from 'app/events/pointer-event-data';
+import { COLOR } from 'app/shared/utils/colors.service';
+import { BaseEventData } from '../../events/pointer-event-data';
+import { EventEmitter } from '@angular/core';
+import { IShapeEditor } from './i-shape-editor';
+import { SceneService } from '../services/scene.service';
+import { KeyboardInput } from 'app/core/input';
+import { Subscription } from 'rxjs';
+import { Debug } from 'app/core/utils/debug';
+import { AnyControlPoint, NewDistanceNode } from '../../modules/three-js/objects/control-point';
+import { OdTextures } from 'app/modules/tv-map/builders/od.textures';
+
+export abstract class AbstractShapeEditor implements IShapeEditor {
+
+ public controlPointSelected = new EventEmitter();
+ public controlPointUnselected = new EventEmitter();
+ public controlPointHovered = new EventEmitter();
+
+ /**
+ * Fire when control point is added
+ */
+ public controlPointAdded = new EventEmitter();
+
+ /**
+ * Fired everytime mouse is moving the control point
+ */
+ public controlPointMoved = new EventEmitter();
+
+ /**
+ * Fired after mouse is up and control point position is updated
+ */
+ public controlPointUpdated = new EventEmitter();
+
+ /**
+ * Fire when control point is removed
+ */
+ public controlPointRemoved = new EventEmitter();
+
+ public curveGeometryChanged = new EventEmitter>();
+ public curveGeometryAdded = new EventEmitter>();
+
+ public pickingEnabled: boolean = true;
+
+ public currentPoint: AnyControlPoint;
+
+ protected pointerDownAt: THREE.Vector3;
+ protected isDragging: boolean;
+
+ protected DEFAULT_CONTROL_POINT_COLOR = COLOR.BLUE;
+ protected HOVERED_CONTROL_POINT_COLOR = COLOR.YELLOW;
+ protected SELECTED_CONTROL_POINT_COLOR = COLOR.RED;
+
+ protected DEFAULT_LINE_COLOR = COLOR.RED;
+ protected HIGHLIGHT_LINE_COLOR = COLOR.BLUE;
+
+ protected object: Object3D;
+ protected material = new THREE.LineBasicMaterial( { color: this.DEFAULT_LINE_COLOR, depthTest: false } );
+ protected pointerIsDown: boolean;
+
+ // subscribers
+ private pointerMovedSubscriber: Subscription;
+ private pointerClickedSubscriber: Subscription;
+ private pointerUpSubscriber: Subscription;
+ private pointerDownSubscriber: Subscription;
+ private selectSubscriber: Subscription;
+ private deSelectSubscriber: Subscription;
+ private pointerEnterSubscriber: Subscription;
+ private pointerExitSubscriber: Subscription;
+ private controlPointSelectedSubscriber: Subscription;
+ private controlPointUnselectedSubcriber: Subscription;
+ private controlPointHoveredSubcriber: Subscription;
+ private isEnabled: boolean = false;
+ private _controlPoints: AnyControlPoint[] = [];
+
+ constructor () {
+
+ this.enable();
+
+ }
+
+ get controlPoints (): AnyControlPoint[] {
+ return this._controlPoints;
+ }
+
+ set controlPoints ( value: AnyControlPoint[] ) {
+ this._controlPoints = value;
+ }
+
+ public get controlPointCount (): number {
+
+ return this._controlPoints.length;
+
+ }
+
+ public get controlPointPositions (): Vector3[] {
+
+ const positions: Vector3[] = [];
+
+ this._controlPoints.forEach( ( point ) => {
+ positions.push( point.position );
+ } );
+
+ return positions;
+ }
+
+ public get vector2ControlPoints (): Vector2[] {
+
+ const positions: Vector2[] = [];
+
+ this._controlPoints.forEach( ( point ) => {
+ positions.push( new Vector2( point.position.x, point.position.y ) );
+ } );
+
+ return positions;
+ }
+
+ protected get lastControlPoint () {
+ return this._controlPoints[ this._controlPoints.length - 1 ];
+ };
+
+ public abstract draw ();
+
+ destroy () {
+
+ this.controlPoints.forEach( cp => this.unSelectControlPoint( cp ) );
+
+ this.removeAllControlPoints();
+
+ this.disable();
+
+ SceneService.remove( this.object );
+
+ }
+
+ highlight () {
+
+ this.material.color = new Color( this.HIGHLIGHT_LINE_COLOR );
+ this.material.needsUpdate = true;
+
+ }
+
+ removeHighlight () {
+
+ this.material.color = new Color( this.DEFAULT_LINE_COLOR );
+ this.material.needsUpdate = true;
+ }
+
+ enable () {
+
+ if ( this.isEnabled ) return;
+
+ const events = AppService.eventSystem;
+
+ if ( events ) {
+
+ this.pointerMovedSubscriber = events.pointerMoved.subscribe( e => this.onPointerMoved( e ) );
+ this.pointerClickedSubscriber = events.pointerClicked.subscribe( e => this.onPointerClicked( e ) );
+ this.pointerUpSubscriber = events.pointerUp.subscribe( e => this.onPointerUp( e ) );
+ this.pointerDownSubscriber = events.pointerDown.subscribe( e => this.onPointerDown( e ) );
+ this.selectSubscriber = events.select.subscribe( e => this.onSelect( e ) );
+ this.deSelectSubscriber = events.deSelect.subscribe( e => this.onDeSelect( e ) );
+ this.pointerEnterSubscriber = events.pointerEnter.subscribe( e => this.onPointerEnter( e ) );
+ this.pointerExitSubscriber = events.pointerExit.subscribe( e => this.onPointerExit( e ) );
+
+ }
+
+
+ this.controlPointSelectedSubscriber = this.controlPointSelected.subscribe( e => this.onControlPointSelected( e ) );
+ this.controlPointUnselectedSubcriber = this.controlPointUnselected.subscribe( e => this.onControlPointUnselected( e ) );
+ this.controlPointHoveredSubcriber = this.controlPointHovered.subscribe( e => this.onControlPointHovered( e ) );
+
+ this.isEnabled = true;
+ }
+
+ disable () {
+
+ Debug.log( 'eventd disabled' );
+
+ this.pointerMovedSubscriber.unsubscribe();
+ this.pointerClickedSubscriber.unsubscribe();
+ this.pointerUpSubscriber.unsubscribe();
+ this.pointerDownSubscriber.unsubscribe();
+ this.selectSubscriber.unsubscribe();
+ this.deSelectSubscriber.unsubscribe();
+ this.pointerEnterSubscriber.unsubscribe();
+ this.pointerExitSubscriber.unsubscribe();
+
+ this.controlPointSelectedSubscriber.unsubscribe();
+ this.controlPointUnselectedSubcriber.unsubscribe();
+
+ this.isEnabled = false;
+ }
+
+ onPointerClicked ( e: PointerEventData ): void {
+
+ if ( this.isDragging ) return;
+
+ }
+
+ onSelect ( e: BaseEventData ) {
+
+ if ( !this.pickingEnabled ) return;
+
+ this.pickControlPoint( e );
+
+ }
+
+ pickControlPoint ( e: BaseEventData ) {
+
+ if ( e.object != null && e.object.type == 'Points' ) {
+
+ this.unSelectControlPoint( this.currentPoint );
+
+ this.selectControlPoint( e.object as AnyControlPoint );
+
+ } else {
+
+ this.unSelectControlPoint( this.currentPoint );
+ }
+
+ }
+
+ selectControlPoint ( point: AnyControlPoint ) {
+
+ if ( point != null ) {
+
+ this.currentPoint = point;
+
+ this.controlPointSelected.emit( this.currentPoint );
+
+ }
+ }
+
+ unSelectControlPoint ( point: AnyControlPoint ) {
+
+ if ( point != null ) {
+
+ this.controlPointUnselected.emit( point );
+
+ this.currentPoint = null;
+
+ }
+
+ }
+
+ onPointerUp ( e: PointerEventData ) {
+
+ this.pointerIsDown = false;
+
+ this.isDragging = false;
+
+ if ( this.currentPoint != null ) this.controlPointUpdated.emit( this.currentPoint );
+ }
+
+ onPointerMoved ( e: PointerEventData ): void {
+
+ if ( e.point != null && this.pointerIsDown && this._controlPoints.length > 1 ) {
+
+ this.isDragging = true;
+
+ if ( this.currentPoint != null ) this.currentPoint.copyPosition( e.point );
+
+ if ( this._controlPoints.length > 1 ) this.draw();
+
+ this.controlPointMoved.emit( this.currentPoint );
+ }
+
+ }
+
+ onPointerExit ( e: any ): any {
+
+ }
+
+ onPointerEnter ( e: any ): any {
+
+ }
+
+ onPointerDown ( e: PointerEventData ): any {
+
+ if ( e.button == MouseButton.RIGHT ) return;
+
+ this.pointerIsDown = true;
+
+ this.pointerDownAt = e.point;
+
+ // if ( e.object != null && e.object.userData.is_selectable == true ) return;
+
+ if ( e.button == MouseButton.LEFT && KeyboardInput.isShiftKeyDown && e.point != null ) {
+
+ this.addControlPoint( e.point );
+
+ if ( this._controlPoints.length > 1 ) this.draw();
+ }
+
+ }
+
+ addControlPoint ( position: THREE.Vector3, parent?: Object3D, size?: number ): AnyControlPoint {
+
+ const point = this.createControlPoint( position, parent, size );
+
+ this._controlPoints.push( point );
+
+ this.controlPointAdded.emit( point );
+
+ return point;
+ }
+
+ removeControlPoint ( point: AnyControlPoint ) {
+
+ const index = this._controlPoints.indexOf( point );
+
+ this._controlPoints.splice( index, 1 );
+
+ this.controlPointRemoved.emit( point );
+
+ SceneService.remove( point );
+ }
+
+ removeAllControlPoints () {
+
+ this._controlPoints.forEach( object => {
+
+ object.visible = false;
+
+ // SceneService.remove( object );
+
+ } );
+
+ this._controlPoints.splice( 0, this._controlPoints.length );
+
+ }
+
+ protected createControlPoint ( position: Vector3, parent?: Object3D, size?: number ) {
+
+ const dotGeometry = new THREE.Geometry();
+
+ dotGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ) );
+
+ const dotMaterial = new PointsMaterial( {
+ size: size || 10,
+ sizeAttenuation: false,
+ map: OdTextures.point,
+ alphaTest: 0.5,
+ transparent: true,
+ color: this.DEFAULT_CONTROL_POINT_COLOR,
+ depthTest: false
+ } );
+
+ const object = new AnyControlPoint( dotGeometry, dotMaterial );
+
+ object.setPosition( position.clone() );
+
+ object.userData.is_button = true;
+ object.userData.is_control_point = true;
+ object.userData.is_selectable = true;
+
+ object.renderOrder = 3;
+
+ SceneService.add( object );
+
+ return object;
+
+ }
+
+ public createDistanceNode ( roadId: number, laneId: number, s: number, t, position: Vector3, parent?: Object3D ) {
+
+ const dotGeometry = new THREE.Geometry();
+
+ dotGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ) );
+
+ const dotMaterial = new PointsMaterial( {
+ size: 10,
+ sizeAttenuation: false,
+ map: OdTextures.point,
+ alphaTest: 0.5,
+ transparent: true,
+ color: this.DEFAULT_CONTROL_POINT_COLOR,
+ depthTest: false
+ } );
+
+ const object = new NewDistanceNode( roadId, laneId, s, t, dotGeometry, dotMaterial );
+
+ object.setPosition( position.clone() );
+
+ object.userData.is_button = true;
+ object.userData.is_control_point = true;
+ object.userData.is_selectable = true;
+
+ object.renderOrder = 3;
+
+ SceneService.add( object );
+
+ return object;
+
+ }
+
+ private onDeSelect ( e: any ) {
+
+ }
+
+ onControlPointHovered ( e: AnyControlPoint ) {
+
+ e.onMouseOver();
+
+ }
+
+ onControlPointUnhovered ( e: AnyControlPoint ) {
+
+ e.onMouseOut();
+
+ }
+
+ private onControlPointSelected ( e: AnyControlPoint ) {
+
+ e.select();
+
+ AppService.three.disableControls();
+ }
+
+ private onControlPointUnselected ( e: AnyControlPoint ) {
+
+ e.unselect();
+
+ AppService.three.enableControls();
+ }
+
+}
diff --git a/src/app/core/editors/box-editor.ts b/src/app/core/editors/box-editor.ts
new file mode 100644
index 00000000..30a76193
--- /dev/null
+++ b/src/app/core/editors/box-editor.ts
@@ -0,0 +1,158 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { AbstractShapeEditor } from './abstract-shape-editor';
+import { MouseButton, PointerEventData, PointerMoveData } from 'app/events/pointer-event-data';
+import * as THREE from 'three';
+import { Line, BoxBufferGeometry, MeshBasicMaterial, Mesh } from 'three';
+import { SceneService } from '../services/scene.service';
+import { KeyboardInput } from 'app/core/input';
+import { AnyControlPoint } from 'app/modules/three-js/objects/control-point';
+import { AppService } from '../services/app.service';
+import { EventEmitter } from '@angular/core';
+
+export interface BoxCreatedEvent {
+ mesh?: Mesh;
+ geometry?: BoxBufferGeometry;
+ height?: number;
+ width?: number;
+ length?: number;
+}
+
+export class BoxEditor extends AbstractShapeEditor {
+
+ boxGeometry = new BoxBufferGeometry();
+ boxMaterial = new MeshBasicMaterial( { color: 'red' } );
+ boxMesh: Mesh;
+
+ height: number;
+ width: number;
+ length: number = 1;
+
+ boxCreated = new EventEmitter();
+
+ boxes: Mesh[] = [];
+
+ creating = false;
+
+ constructor ( public keepAspectRatio: boolean = false ) {
+
+ super();
+
+ }
+
+ disable () {
+
+ super.disable();
+
+ AppService.three.enableControls();
+
+ }
+
+ draw () {
+
+ this.boxGeometry = new BoxBufferGeometry( this.height, this.width, this.length );
+
+ if ( this.boxMesh ) SceneService.remove( this.boxMesh );
+
+ this.boxMesh = new Mesh( this.boxGeometry, this.boxMaterial );
+
+ this.boxMesh.position.copy( this.pointerDownAt );
+
+ SceneService.add( this.boxMesh );
+ }
+
+ onPointerDown ( e: PointerEventData ) {
+
+ AppService.three.disableControls();
+
+ if ( e.button !== MouseButton.LEFT && !KeyboardInput.isShiftKeyDown ) return;
+
+ this.pointerIsDown = true;
+ this.pointerDownAt = e.point;
+
+ }
+
+ onPointerUp ( e: PointerEventData ) {
+
+ AppService.three.enableControls();
+
+ if ( e.button !== MouseButton.LEFT || !this.creating ) return;
+
+ this.creating = false;
+
+ this.pointerIsDown = false;
+
+ if ( this.boxMesh ) SceneService.remove( this.boxMesh );
+
+ const cp = this.addControlPoint( this.boxMesh.position );
+
+ const box = new Mesh( this.boxGeometry, this.boxMaterial );
+
+ box.position.copy( this.boxMesh.position );
+
+ this.boxes.push( box );
+
+ this.boxCreated.emit( {
+ mesh: box,
+ geometry: this.boxGeometry,
+ height: this.length,
+ width: this.height,
+ length: this.width,
+ } );
+
+ cp.updated.subscribe( e => {
+
+ box.position.copy( e.position );
+
+ } );
+
+ this.controlPointSelected.subscribe( e => {
+
+
+ } );
+
+ }
+
+ onPointerMoved ( e: PointerMoveData ) {
+
+ if ( this.pointerIsDown && KeyboardInput.isShiftKeyDown ) {
+
+ this.creating = true;
+
+ // this.height = this.pointerDownAt.distanceTo( e.point );
+ // this.width = this.pointerDownAt.distanceTo( e.point );
+ // this.depth = this.pointerDownAt.distanceTo( e.point );
+
+ const startPoint = this.pointerDownAt;
+ const endPoint = e.point;
+
+ // const tmpPoint = startPoint.clone();
+
+ // tmpPoint.x = Math.min( startPoint.x, endPoint.x );
+ // tmpPoint.y = Math.max( startPoint.y, endPoint.y );
+ // endPoint.x = Math.max( startPoint.x, endPoint.x );
+ // endPoint.y = Math.min( startPoint.y, endPoint.y );
+
+
+ this.width = Math.abs( startPoint.y - endPoint.y ) * 2;
+ this.height = Math.abs( startPoint.x - endPoint.x ) * 2;
+
+ this.draw();
+ }
+
+ if ( this.pointerIsDown && this.currentPoint != null ) {
+
+ e.point.z = 0;
+
+ this.currentPoint.copyPosition( e.point );
+
+ this.controlPointMoved.emit( this.currentPoint );
+
+
+ }
+
+ }
+
+}
diff --git a/src/app/core/editors/cubic-bezier-curve-editor.ts b/src/app/core/editors/cubic-bezier-curve-editor.ts
new file mode 100644
index 00000000..d552ee0f
--- /dev/null
+++ b/src/app/core/editors/cubic-bezier-curve-editor.ts
@@ -0,0 +1,121 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { KeyboardInput } from '../input';
+import * as THREE from 'three';
+import { Curve, Object3D } from 'three';
+import { MouseButton, PointerEventData, PointerMoveData } from '../../events/pointer-event-data';
+import { AbstractShapeEditor } from './abstract-shape-editor';
+import { SceneService } from '../services/scene.service';
+import { AnyControlPoint } from '../../modules/three-js/objects/control-point';
+
+export class CubicBezierCurveEditor extends AbstractShapeEditor {
+
+ private curve: Curve;
+ private referenceLine: Object3D;
+ private v0: AnyControlPoint;
+ private v1: AnyControlPoint;
+ private v2: AnyControlPoint;
+ private v3: AnyControlPoint;
+
+ constructor () {
+
+ super();
+
+ }
+
+ public draw () {
+ this.drawCurve();
+ }
+
+ onPointerMoved ( e: PointerMoveData ): void {
+
+ if ( e.point != null && this.pointerIsDown && this.controlPoints.length > 1 ) {
+
+ this.isDragging = true;
+
+ if ( this.currentPoint != null ) this.currentPoint.copyPosition( e.point );
+
+ // this.lastControlPoint.position.copy( e.point );
+
+ if ( this.controlPoints.length > 1 ) this.drawCurve();
+
+ }
+
+ }
+
+ onPointerDown ( e: PointerEventData ) {
+
+ this.pointerIsDown = true;
+
+ this.pointerDownAt = e.point;
+
+ if ( this.controlPoints.length >= 4 ) return;
+
+ if ( e.object != null && e.object.userData.is_selectable == true ) return;
+
+ if ( e.button == MouseButton.LEFT && KeyboardInput.isShiftKeyDown && e.point != null ) {
+
+ this.addControlPoint( e.point );
+
+ if ( this.controlPoints.length > 1 ) this.draw();
+ }
+ }
+
+ drawCurve () {
+
+ if ( this.referenceLine != null ) SceneService.remove( this.referenceLine, false );
+
+ this.updateBezierPoints();
+
+ this.curve = new THREE.CubicBezierCurve3( this.v0.position, this.v1.position, this.v2.position, this.v3.position );
+
+ let points = this.curve.getPoints( 50 );
+
+ let geometry = new THREE.BufferGeometry().setFromPoints( points );
+
+ let material = new THREE.LineBasicMaterial( { color: 0xff0000 } );
+
+ // Create the final object to add to the scene
+ this.referenceLine = new THREE.Line( geometry, material );
+
+ SceneService.add( this.referenceLine, false );
+
+ if ( this.controlPoints.length == 2 ) {
+
+ this.curveGeometryAdded.emit( this.curve );
+
+ } else if ( this.controlPoints.length > 2 ) {
+
+ this.curveGeometryChanged.emit( this.curve );
+
+ }
+ }
+
+ private updateBezierPoints () {
+
+ if ( this.controlPoints.length == 2 ) {
+
+ this.v0 = this.controlPoints[ 0 ];
+ this.v1 = this.controlPoints[ 0 ];
+ this.v2 = this.controlPoints[ 1 ];
+ this.v3 = this.controlPoints[ 1 ];
+
+ } else if ( this.controlPoints.length == 3 ) {
+
+ this.v0 = this.controlPoints[ 0 ];
+ this.v1 = this.controlPoints[ 0 ];
+ this.v2 = this.controlPoints[ 1 ];
+ this.v3 = this.controlPoints[ 2 ];
+
+ } else if ( this.controlPoints.length == 4 ) {
+
+ this.v0 = this.controlPoints[ 0 ];
+ this.v1 = this.controlPoints[ 1 ];
+ this.v2 = this.controlPoints[ 2 ];
+ this.v3 = this.controlPoints[ 3 ];
+
+ }
+ }
+}
diff --git a/src/app/core/editors/distance-node-editor.ts b/src/app/core/editors/distance-node-editor.ts
new file mode 100644
index 00000000..03911688
--- /dev/null
+++ b/src/app/core/editors/distance-node-editor.ts
@@ -0,0 +1,138 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { AbstractShapeEditor } from './abstract-shape-editor';
+import { MouseButton, PointerEventData, PointerMoveData } from '../../events/pointer-event-data';
+import { KeyboardInput } from '../input';
+import { AnyControlPoint, NewDistanceNode } from 'app/modules/three-js/objects/control-point';
+import { TvMapQueries } from 'app/modules/tv-map/queries/tv-map-queries';
+import { TvPosTheta } from '../../modules/tv-map/models/tv-pos-theta';
+
+export class DistanceNodeEditor extends AbstractShapeEditor {
+
+
+ constructor ( private maxControlPoints: number = 1000 ) {
+
+ super();
+
+ }
+
+ draw () {
+
+ this.curveGeometryAdded.emit( null );
+
+ }
+
+
+ onPointerDown ( e: PointerEventData ) {
+
+ if ( e.button == MouseButton.RIGHT ) return;
+
+ this.pointerIsDown = true;
+
+ this.pointerDownAt = e.point;
+
+ if ( this.controlPoints.length >= this.maxControlPoints ) return;
+
+ if ( e.object != null && e.object.userData.is_selectable === true ) return;
+
+ if ( e.button === MouseButton.LEFT && KeyboardInput.isShiftKeyDown && e.point != null ) {
+
+ e.point.z = 0;
+
+ this.addControlPoint( e.point );
+
+ this.draw();
+ }
+ }
+
+ onPointerMoved ( e: PointerMoveData ): void {
+
+ const direction = 'sCoordinate';
+
+ if ( e.point != null && this.pointerIsDown && this.controlPoints.length > 0 ) {
+
+ this.isDragging = true;
+
+ if ( this.currentPoint != null ) {
+
+ const roadPos = new TvPosTheta();
+ const lanePos = new TvPosTheta();
+
+ const node = ( this.currentPoint as NewDistanceNode );
+
+ e.point.z = 0;
+
+ const position = e.point;
+
+ // this gets the road and the s and t values
+ const road = TvMapQueries.getRoadByCoords( position.x, position.y, roadPos );
+
+ // this get the lane from road, s and t values
+ // roadPos is only used to read
+ const result = TvMapQueries.getLaneByCoords( position.x, position.y, roadPos );
+
+ if ( road ) {
+
+ let finalPosition = null;
+
+ if ( direction == 'sCoordinate' ) {
+
+ // fixed t and only s-value will change
+ // finalPosition = OpenDriveQueries.getLanePosition( node.roadId, node.laneId, roadPos.s, node.t, lanePos );
+ finalPosition = TvMapQueries.getLanePosition( node.roadId, node.laneId, roadPos.s, 0, lanePos );
+
+ } else if ( direction == 'tCoordinate' ) {
+
+ // fixed s and only t-value will change
+ finalPosition = TvMapQueries.getLanePosition( node.roadId, node.laneId, node.s, roadPos.t, lanePos );
+
+ }
+
+ node.s = roadPos.s;
+
+ // node.t = roadPos.t;
+
+ this.currentPoint.copyPosition( finalPosition );
+
+ this.controlPointMoved.emit( this.currentPoint );
+
+ }
+ }
+ }
+ }
+
+ addControlPoint ( position: THREE.Vector3 ): AnyControlPoint {
+
+ const roadPos = new TvPosTheta();
+ const lanePos = new TvPosTheta();
+
+ // this gets the road and the s and t values
+ const road = TvMapQueries.getRoadByCoords( position.x, position.y, roadPos );
+
+ // cant create as road not found
+ if ( !road ) return;
+
+ // this get the lane from road, s and t values
+ // roadPos is only used to read
+ const result = TvMapQueries.getLaneByCoords( position.x, position.y, roadPos );
+
+ // cant create as road or lane not found
+ if ( !result.road || !result.lane ) return;
+
+ // now get the exact position in middle of the lane
+ const finalPosition = TvMapQueries.getLanePosition( road.id, result.lane.id, roadPos.s, 0, lanePos );
+
+ // create the distance node
+ const point = this.createDistanceNode( result.road.id, result.lane.id, roadPos.s, roadPos.t, position, road.gameObject );
+
+ point.copyPosition( finalPosition );
+
+ this.controlPoints.push( point );
+
+ this.controlPointAdded.emit( point );
+
+ return point;
+ }
+}
diff --git a/src/app/core/editors/i-shape-editor.ts b/src/app/core/editors/i-shape-editor.ts
new file mode 100644
index 00000000..ab9b1139
--- /dev/null
+++ b/src/app/core/editors/i-shape-editor.ts
@@ -0,0 +1,8 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+export interface IShapeEditor {
+
+
+}
\ No newline at end of file
diff --git a/src/app/core/editors/line-editor.ts b/src/app/core/editors/line-editor.ts
new file mode 100644
index 00000000..2a450bc3
--- /dev/null
+++ b/src/app/core/editors/line-editor.ts
@@ -0,0 +1,87 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { AbstractShapeEditor } from './abstract-shape-editor';
+import { MouseButton, PointerEventData } from 'app/events/pointer-event-data';
+import * as THREE from 'three';
+import { Line } from 'three';
+import { SceneService } from '../services/scene.service';
+import { KeyboardInput } from 'app/core/input';
+import { AnyControlPoint } from 'app/modules/three-js/objects/control-point';
+
+export class LineEditor extends AbstractShapeEditor {
+
+ public curve: THREE.LineCurve3;
+
+ private readonly maxPoints: number;
+
+ constructor ( maxLines: number = 1 ) {
+
+ super();
+
+ this.maxPoints = maxLines * 2;
+
+ }
+
+ public draw () {
+
+ if ( this.object != null ) SceneService.remove( this.object, false );
+
+ this.curve = new THREE.LineCurve3( this.controlPointPositions[ 0 ], this.controlPointPositions[ 1 ] );
+
+ const points = this.curve.getPoints( 2 );
+
+ const material = new THREE.LineBasicMaterial( { color: '#afbeff' } );
+
+ const geometry = new THREE.BufferGeometry().setFromPoints( points );
+
+ this.object = new THREE.Line( geometry, material );
+
+ this.object.name = 'Line';
+
+ this.object.userData.is_selectable = false;
+
+ this.object.renderOrder = 3;
+
+ SceneService.add( this.object, false );
+
+ this.curveGeometryChanged.emit( this.curve );
+ }
+
+ onPointerDown ( e: PointerEventData ) {
+
+ if ( e.button === MouseButton.RIGHT ) return;
+
+ this.pointerIsDown = true;
+
+ this.pointerDownAt = e.point;
+
+ if ( this.controlPoints.length >= this.maxPoints ) return;
+
+ if ( e.button === MouseButton.LEFT && KeyboardInput.isShiftKeyDown && e.point != null ) {
+
+ this.addControlPoint( e.point );
+
+ if ( this.controlPoints.length > 1 ) this.draw();
+
+ } else if ( e.button === MouseButton.LEFT ) {
+
+ for ( const i of e.intersections ) {
+
+ if ( i.object.type === 'Points' ) {
+
+ this.unSelectControlPoint( this.currentPoint );
+
+ this.selectControlPoint( i.object as AnyControlPoint );
+
+ break;
+
+ }
+
+ }
+
+ }
+ }
+
+}
diff --git a/src/app/core/editors/point-editor.ts b/src/app/core/editors/point-editor.ts
new file mode 100644
index 00000000..eb8fddaa
--- /dev/null
+++ b/src/app/core/editors/point-editor.ts
@@ -0,0 +1,95 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { AbstractShapeEditor } from './abstract-shape-editor';
+import { MouseButton, PointerEventData, PointerMoveData } from '../../events/pointer-event-data';
+import { KeyboardInput } from '../input';
+import { PickingHelper } from '../services/picking-helper.service';
+
+export class PointEditor extends AbstractShapeEditor {
+
+
+ constructor ( private maxControlPoints: number = 1000 ) {
+
+ super();
+
+ }
+
+ draw () {
+
+ this.curveGeometryAdded.emit( null );
+
+ }
+
+ onPointerDown ( e: PointerEventData ) {
+
+ if ( e.button == MouseButton.RIGHT ) return;
+
+ this.pointerIsDown = true;
+
+ this.pointerDownAt = e.point;
+
+ if ( this.controlPoints.length >= this.maxControlPoints ) return;
+
+ this.currentPoint = this.getNearestControlPoint( e );
+
+ if ( this.currentPoint ) {
+
+ this.currentPoint.select();
+
+ this.controlPointSelected.emit( this.currentPoint );
+
+ }
+
+ if ( e.object != null && e.object.userData.is_selectable === true ) return;
+
+ if ( e.button === MouseButton.LEFT && KeyboardInput.isShiftKeyDown && e.point != null ) {
+
+ e.point.z = 0;
+
+ this.addControlPoint( e.point );
+
+ this.draw();
+ }
+ }
+
+ onPointerMoved ( e: PointerEventData ): void {
+
+ this.controlPoints.forEach( cp => cp.onMouseOut() );
+
+ if ( e.point != null && this.pointerIsDown && this.controlPoints.length > 0 ) {
+
+ this.isDragging = true;
+
+ if ( this.currentPoint != null ) {
+
+ e.point.z = 0;
+
+ this.currentPoint.copyPosition( e.point );
+
+ this.controlPointMoved.emit( this.currentPoint );
+
+ }
+
+ } else if ( e.point != null && !this.pointerIsDown && this.controlPoints.length > 0 ) {
+
+ const controlPoint = this.getNearestControlPoint( e );
+
+ if ( controlPoint ) {
+
+ controlPoint.onMouseOver();
+
+ }
+
+ }
+
+ }
+
+ getNearestControlPoint ( e: PointerEventData ) {
+
+ const maxDistance = Math.max( 0.5, e.approxCameraDistance * 0.01 );
+
+ return PickingHelper.findNearest( e.point, this.controlPoints, maxDistance );
+ }
+}
diff --git a/src/app/core/editors/polygon-editor.ts b/src/app/core/editors/polygon-editor.ts
new file mode 100644
index 00000000..acbcac07
--- /dev/null
+++ b/src/app/core/editors/polygon-editor.ts
@@ -0,0 +1,119 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { AbstractShapeEditor } from './abstract-shape-editor';
+import * as THREE from 'three';
+import { SceneService } from '../services/scene.service';
+
+export class PolygonEditor extends AbstractShapeEditor {
+
+ public draw () {
+ this.drawPolygon();
+ }
+
+ group: THREE.Group;
+ line: any;
+
+ constructor () {
+
+ super();
+
+ }
+
+ public drawPolygon () {
+
+ if ( this.group != null ) SceneService.remove( this.group );
+ if ( this.line != null ) SceneService.remove( this.line );
+
+ this.group = new THREE.Group();
+
+ var splineShape = new THREE.Shape();
+
+ let v0 = this.vector2ControlPoints[ 0 ];
+
+ splineShape.moveTo( v0.x, v0.y );
+
+ splineShape.splineThru( this.vector2ControlPoints );
+
+ var extrudeSettings = { depth: 8, bevelEnabled: true, bevelSegments: 2, steps: 2, bevelSize: 1, bevelThickness: 1 };
+
+ this.addShape( splineShape, 0xf08000, 0, 0, 0, 0, 0, 0, 1 );
+
+ SceneService.add( this.group );
+
+ }
+
+ addShape ( shape: THREE.Shape, color: number, x: number, y: number, z: number, rx: number, ry: number, rz: number, s: number ) {
+
+ // flat shape with texture
+ // note: default UVs generated by THREE.ShapeBufferGeometry are simply the x- and y-coordinates of the vertices
+ const loader = new THREE.TextureLoader();
+ const texture = loader.load( "assets/textures/terrain/grasslight-big.jpg" );
+ texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
+ texture.repeat.set( 25, 25 );
+ texture.anisotropy = 16;
+
+ let geometry1 = new THREE.ShapeBufferGeometry( shape );
+ var mesh = new THREE.Mesh( geometry1, new THREE.MeshLambertMaterial( { map: texture } ) );
+ mesh.position.set( x, y, z - 175 );
+ mesh.rotation.set( rx, ry, rz );
+ mesh.scale.set( s, s, s );
+ this.group.add( mesh );
+
+ // // flat shape
+ // var geometry2 = new THREE.ShapeBufferGeometry( shape );
+ // var mesh = new THREE.Mesh( geometry2, new THREE.MeshPhongMaterial( { color: color, side: THREE.DoubleSide } ) );
+ // mesh.position.set( x, y, z - 125 );
+ // mesh.rotation.set( rx, ry, rz );
+ // mesh.scale.set( s, s, s );
+ // this.group.add( mesh );
+
+ // // extruded shape
+ // var geometry3 = new THREE.ExtrudeBufferGeometry( shape, extrudeSettings );
+ // var mesh = new THREE.Mesh( geometry3, new THREE.MeshPhongMaterial( { color: color, opacity: 0.8 } ) );
+ // mesh.position.set( x, y, z - 10 );
+ // mesh.rotation.set( rx, ry, rz );
+ // mesh.scale.set( s, s, s );
+ // this.group.add( mesh );
+
+ this.addLineShape( shape, color, x, y, z, rx, ry, rz, s );
+ }
+
+ addLineShape ( shape, color, x, y, z, rx, ry, rz, s ) {
+
+ // lines
+ shape.autoClose = true;
+ var points = shape.getPoints();
+ var spacedPoints = shape.getSpacedPoints( 50 );
+ var geometryPoints = new THREE.BufferGeometry().setFromPoints( points );
+
+ // solid line
+ let line = this.line = new THREE.Line( geometryPoints, new THREE.LineBasicMaterial( { color: color } ) );
+ line.position.set( x, y, z - 25 );
+ line.rotation.set( rx, ry, rz );
+ line.scale.set( s, s, s );
+ this.group.add( line );
+
+ // // line from equidistance sampled points
+ // var line = new THREE.Line( geometrySpacedPoints, new THREE.LineBasicMaterial( { color: color } ) );
+ // line.position.set( x, y, z + 25 );
+ // line.rotation.set( rx, ry, rz );
+ // line.scale.set( s, s, s );
+ // this.group.add( line );
+
+ // // vertices from real points
+ // var particles = new THREE.Points( geometryPoints, new THREE.PointsMaterial( { color: color, size: 4 } ) );
+ // particles.position.set( x, y, z + 75 );
+ // particles.rotation.set( rx, ry, rz );
+ // particles.scale.set( s, s, s );
+ // this.group.add( particles );
+
+ // // equidistance sampled points
+ // var particles = new THREE.Points( geometrySpacedPoints, new THREE.PointsMaterial( { color: color, size: 4 } ) );
+ // particles.position.set( x, y, z + 125 );
+ // particles.rotation.set( rx, ry, rz );
+ // particles.scale.set( s, s, s );
+ // this.group.add( particles );
+ }
+}
diff --git a/src/app/core/editors/polyline-editor.ts b/src/app/core/editors/polyline-editor.ts
new file mode 100644
index 00000000..8a3d02e9
--- /dev/null
+++ b/src/app/core/editors/polyline-editor.ts
@@ -0,0 +1,42 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { AbstractShapeEditor } from './abstract-shape-editor';
+import * as THREE from 'three';
+import { CatmullRomCurve3 } from 'three';
+import { SceneService } from '../services/scene.service';
+
+export class PolyLineEditor extends AbstractShapeEditor {
+
+ private curve: CatmullRomCurve3;
+
+ constructor () {
+
+ super();
+
+ }
+
+ public draw () {
+ this.drawSpline();
+ }
+
+ drawSpline () {
+
+ if ( this.object != null ) SceneService.remove( this.object, false );
+
+ this.curve = new THREE.CatmullRomCurve3( this.controlPointPositions, false, 'catmullrom', 0 );
+
+ let geometry = new THREE.BufferGeometry().setFromPoints( this.curve.points );
+
+ // Create the final object to add to the scene
+ this.object = new THREE.Line( geometry, this.material );
+
+ this.object.renderOrder = 2;
+
+ SceneService.add( this.object, false );
+
+ this.curveGeometryChanged.emit( this.curve );
+ }
+
+}
diff --git a/src/app/core/editors/shape-editor-factory.ts b/src/app/core/editors/shape-editor-factory.ts
new file mode 100644
index 00000000..cb243e39
--- /dev/null
+++ b/src/app/core/editors/shape-editor-factory.ts
@@ -0,0 +1,14 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { LineEditor } from './line-editor';
+
+export class ShapeEditorFactory {
+
+ static createLineEditor (): LineEditor {
+ return new LineEditor();
+ }
+
+
+}
diff --git a/src/app/core/editors/spline-curve-editor.ts b/src/app/core/editors/spline-curve-editor.ts
new file mode 100644
index 00000000..f585dbbb
--- /dev/null
+++ b/src/app/core/editors/spline-curve-editor.ts
@@ -0,0 +1,42 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { AbstractShapeEditor } from './abstract-shape-editor';
+import * as THREE from 'three';
+import { Curve, Object3D } from 'three';
+import { SceneService } from '../services/scene.service';
+
+export class SplineCurveEditor extends AbstractShapeEditor {
+
+ private curve: Curve;
+ private line: Object3D;
+
+ constructor () {
+
+ super();
+
+ }
+
+ public draw () {
+
+ if ( this.line != null ) SceneService.remove( this.line, false );
+
+ // Create a sine-like wave
+ this.curve = new THREE.CatmullRomCurve3( this.controlPointPositions, false, 'catmullrom', 0 );
+
+ // let points = this.curve.getPoints( 50 );
+
+ let geometry = new THREE.BufferGeometry().setFromPoints( this.controlPointPositions );
+
+ let material = new THREE.LineBasicMaterial( { color: 0xff0000 } );
+
+ // Create the final object to add to the scene
+ this.line = new THREE.Line( geometry, material );
+
+ this.line.renderOrder = 3;
+
+ SceneService.add( this.line, false );
+ }
+
+}
diff --git a/src/app/core/factories/asset-factory.service.ts b/src/app/core/factories/asset-factory.service.ts
new file mode 100644
index 00000000..b28b916b
--- /dev/null
+++ b/src/app/core/factories/asset-factory.service.ts
@@ -0,0 +1,168 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Injectable } from '@angular/core';
+import { Texture } from 'three';
+import { TvRoadSign } from 'app/modules/tv-map/models/tv-road-sign.model';
+import { TvMaterial } from 'app/modules/three-js/objects/tv-material.model';
+import { FileService } from 'app/services/file.service';
+import { SnackBar } from 'app/services/snack-bar.service';
+import { MetadataFactory } from './metadata-factory.service';
+import { PropModel } from '../models/prop-model.model';
+import { AssetDatabase } from 'app/services/asset-database';
+import { AppService } from '../services/app.service';
+import { TvMap } from 'app/modules/tv-map/models/tv-map.model';
+import { TvRoadMarking, MarkingTypes } from 'app/modules/tv-map/services/tv-marking.service';
+
+@Injectable( {
+ providedIn: 'root'
+} )
+export class AssetFactory {
+
+ private static get fileService (): FileService {
+
+ return AppService.file;
+
+ }
+
+ static getMeta ( guid: string ) {
+
+ return AssetDatabase.getMetadata( guid );
+
+ }
+
+ static createNewScene ( path: string, name: string = 'New Scene' ) {
+
+ try {
+
+ const scene = new TvMap();
+
+ const result = this.fileService.createFile( path, name, 'scene', AppService.exporter.export( scene ) );
+
+ const meta = MetadataFactory.createMetadata( result.fileName, 'scene', result.filePath );
+
+ AssetDatabase.setInstance( meta.guid, scene );
+
+ } catch ( error ) {
+
+ SnackBar.error( error );
+
+ }
+ }
+
+ static createNewFolder ( path: string, name: string = 'New Folder' ) {
+
+ try {
+
+ const result = this.fileService.createFolder( path, name );
+
+ const meta = MetadataFactory.createFolderMetadata( result.name, result.path );
+
+ AssetDatabase.setInstance( meta.guid, meta );
+
+ return result;
+
+ } catch ( error ) {
+
+ SnackBar.error( error );
+
+ }
+
+ }
+
+ static createNewMaterial ( path: string, name: string = 'NewMaterial' ) {
+
+ try {
+
+ const material = TvMaterial.new();
+
+ const result = this.fileService.createFile( path, material.name, 'material', material.toJSONString() );
+
+ const meta = MetadataFactory.createMetadata( result.fileName, 'material', result.filePath );
+
+ AssetDatabase.setInstance( meta.guid, material );
+
+ } catch ( error ) {
+
+ SnackBar.error( error );
+
+ }
+
+ }
+
+ static createNewRoadMarking ( path: string, name: string = 'NewRoadMarking' ) {
+
+ try {
+
+ const marking = new TvRoadMarking( name, MarkingTypes.point, null );
+
+ const result = this.fileService.createFile( path, marking.name, TvRoadMarking.extension, marking.toJSONString() );
+
+ const meta = MetadataFactory.createMetadata( result.fileName, TvRoadMarking.extension, result.filePath );
+
+ AssetDatabase.setInstance( meta.guid, marking );
+
+ } catch ( error ) {
+
+ SnackBar.error( error );
+
+ }
+
+ }
+
+ static updateRoadMarking ( path: string, marking: TvRoadMarking ) {
+
+ try {
+
+ this.fileService.fs.writeFileSync( path, marking.toJSONString() );
+
+ } catch ( error ) {
+
+ SnackBar.error( error );
+
+ }
+
+ }
+
+ static updateMaterial ( path: string, material: TvMaterial ) {
+
+ this.fileService.fs.writeFileSync( path, material.toJSONString() );
+
+ }
+
+ static createNewSign ( name: string = 'NewSign', path: string ) {
+
+ try {
+
+ const sign = new TvRoadSign( name, null );
+
+ const result = this.fileService.createFile( path, sign.name, 'sign', sign.toJSONString() );
+
+ const meta = MetadataFactory.createMetadata( result.fileName, 'sign', result.filePath );
+
+ AssetDatabase.setInstance( meta.guid, sign );
+
+ } catch ( error ) {
+
+ SnackBar.error( error );
+
+ }
+
+ }
+
+ static updatePropModelByGuid ( guid: string, prop: PropModel ): void {
+
+ const meta = this.getMeta( guid );
+
+ this.fileService.fs.writeFileSync( meta.path, JSON.stringify( prop ) );
+ }
+
+ static updateTexture ( guid: string, texture: Texture ): void {
+
+ const meta = this.getMeta( guid );
+
+ MetadataFactory.createTextureMetadata( meta.guid, meta.path, texture );
+ }
+
+}
diff --git a/src/app/core/factories/inspector-factory.service.ts b/src/app/core/factories/inspector-factory.service.ts
new file mode 100644
index 00000000..3ce91e4e
--- /dev/null
+++ b/src/app/core/factories/inspector-factory.service.ts
@@ -0,0 +1,90 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Injectable, Type } from '@angular/core';
+import { AppInspector } from '../inspector';
+import { PropInstanceInspectorComponent } from 'app/views/inspectors/prop-instance-inspector/prop-instance-inspector.component';
+import { PropModelInspectorComponent } from 'app/views/inspectors/prop-model-inspector/prop-model-inspector.component';
+import { FileService } from 'app/services/file.service';
+import { IComponent } from '../game-object';
+import { MaterialInspector } from 'app/views/inspectors/material-inspector/material-inspector.component';
+import { TextureInspector } from 'app/views/inspectors/texture-inspector/texture-inspector.component';
+import { RoadSignInspector } from 'app/views/inspectors/road-sign-inspector/road-sign-inspector.component';
+import { RoadStyleInspector } from 'app/views/inspectors/road-style-inspector/road-style-inspector.component';
+import { RoadMarkingInspector } from 'app/views/inspectors/road-marking-inspector/road-marking-inspector.component';
+import { TvRoadMarking } from 'app/modules/tv-map/services/tv-marking.service';
+
+export enum InspectorType {
+ prop_model_inspector = 'prop_model_inspector',
+ prop_instance_inspector = 'prop_instance_inspector',
+}
+
+@Injectable( {
+ providedIn: 'root'
+} )
+export class InspectorFactoryService {
+
+ constructor () { }
+
+ static setByType ( type: InspectorType, data: any ) {
+
+ switch ( type ) {
+
+ case InspectorType.prop_instance_inspector:
+ AppInspector.setInspector( PropInstanceInspectorComponent, data );
+ break;
+
+ case InspectorType.prop_model_inspector:
+ AppInspector.setInspector( PropModelInspectorComponent, data );
+ break;
+
+ default:
+ break;
+ }
+
+ }
+
+ static getInspectorByExtension ( extension ): Type {
+
+ let inspector: Type;
+
+ switch ( extension ) {
+
+ case 'obj': inspector = PropModelInspectorComponent; break;
+
+ case 'fbx': inspector = PropModelInspectorComponent; break;
+
+ case 'gltf': inspector = PropModelInspectorComponent; break;
+
+ case 'glb': inspector = PropModelInspectorComponent; break;
+
+ case 'png': inspector = TextureInspector; break;
+
+ case 'jpg': inspector = TextureInspector; break;
+
+ case 'jpeg': inspector = TextureInspector; break;
+
+ case 'svg': inspector = TextureInspector; break;
+
+ case 'material': inspector = MaterialInspector; break;
+
+ case 'sign': inspector = RoadSignInspector; break;
+
+ case 'roadstyle': inspector = RoadStyleInspector; break;
+
+ case TvRoadMarking.extension: inspector = RoadMarkingInspector; break;
+
+ default: break;
+ }
+
+ return inspector;
+ }
+
+ static getInpectorByFilename ( filename: string ): Type {
+
+ const extension = FileService.getExtension( filename );
+
+ return this.getInspectorByExtension( extension );
+ }
+}
diff --git a/src/app/core/factories/lane-path-factory.service.ts b/src/app/core/factories/lane-path-factory.service.ts
new file mode 100644
index 00000000..697787d1
--- /dev/null
+++ b/src/app/core/factories/lane-path-factory.service.ts
@@ -0,0 +1,214 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Injectable } from '@angular/core';
+import { TvRoad } from 'app/modules/tv-map/models/tv-road.model';
+import { TvJunctionConnection } from 'app/modules/tv-map/models/tv-junction-connection';
+import { LanePathObject, TvJunctionLaneLink } from 'app/modules/tv-map/models/tv-junction-lane-link';
+import { BufferGeometry, Line, LineBasicMaterial, Shape } from 'three';
+import { AutoSplinePath, ExplicitSplinePath } from '../shapes/cubic-spline-curve';
+import { ExplicitSpline } from '../shapes/explicit-spline';
+import { AutoSpline } from '../shapes/auto-spline';
+import { AbstractSpline } from '../shapes/abstract-spline';
+import { TvLane } from 'app/modules/tv-map/models/tv-lane';
+
+@Injectable( {
+ providedIn: 'root'
+} )
+export class LanePathFactory {
+
+ static create (
+ incomingRoad: TvRoad,
+ connectingRoad: TvRoad,
+ connection: TvJunctionConnection,
+ link: TvJunctionLaneLink
+ ) {
+
+ const pathObject = new LanePathObject( incomingRoad, connectingRoad, connection, link );
+
+ const lane = connectingRoad.getLaneSectionAt( 0 ).getLaneById( link.to );
+
+ const width = connectingRoad.getLaneSectionAt( 0 ).getWidthUptoCenter( lane, 0 );
+
+ const spline = connectingRoad.spline;
+
+ const shape = new Shape(); shape.moveTo( 0, -0.3 ); shape.lineTo( 0, 0.3 );
+
+ if ( spline.controlPointPositions.length < 2 ) return;
+
+ let offset = width;
+
+ if ( lane.id < 0 ) offset *= -1;
+
+ const path = this.getPath( spline, offset );
+
+ const lineMaterial = new LineBasicMaterial( {
+ color: 0x00ffff,
+ linewidth: 100,
+ opacity: 0.5,
+ transparent: true,
+ } );
+
+ const lineGeometry = new BufferGeometry().setFromPoints( path.getSpacedPoints( 50 ) );
+
+ pathObject.mesh = new Line( lineGeometry, lineMaterial );
+
+ pathObject.mesh.castShadow = true;
+
+ pathObject.mesh.renderOrder = 3;
+
+ pathObject.mesh.frustumCulled = false;
+
+ pathObject.mesh[ 'tag' ] = LanePathObject.tag;
+
+ pathObject.add( pathObject.mesh );
+
+ return pathObject;
+ }
+
+ static createPathForLane (
+ incomingRoad: TvRoad,
+ connectingRoad: TvRoad,
+ lane: TvLane,
+ connection: TvJunctionConnection,
+ link: TvJunctionLaneLink
+ ) {
+
+ // console.trace( "create-path-lane", incomingRoad.id, connectingRoad.id, lane.id );
+
+ const pathObject = new LanePathObject( incomingRoad, connectingRoad, connection, link );
+
+ const width = connectingRoad.getFirstLaneSection().getWidthUptoCenter( lane, 0 );
+
+ const spline = connectingRoad.spline;
+
+ const shape = new Shape(); shape.moveTo( 0, -0.3 ); shape.lineTo( 0, 0.3 );
+
+ if ( spline.controlPointPositions.length < 2 ) return;
+
+ let offset = width;
+
+ if ( lane.id < 0 ) offset *= -1;
+
+ const path = this.getPath( spline, offset );
+
+ const lineMaterial = new LineBasicMaterial( {
+ color: 0x00ffff,
+ linewidth: 100,
+ opacity: 0.5,
+ transparent: true,
+ } );
+
+ const lineGeometry = new BufferGeometry().setFromPoints( path.getSpacedPoints( 50 ) );
+
+ pathObject.mesh = new Line( lineGeometry, lineMaterial );
+
+ pathObject.mesh.castShadow = true;
+
+ pathObject.mesh.renderOrder = 3;
+
+ pathObject.mesh.frustumCulled = false;
+
+ pathObject.mesh[ 'tag' ] = LanePathObject.tag;
+
+ pathObject.add( pathObject.mesh );
+
+ return pathObject;
+ }
+
+ /**
+ *
+ * @param connectingRoad
+ * @deprecated dont use this
+ */
+ static createFromConnectingRoad ( connectingRoad: TvRoad ) {
+
+ const pathObject = new LanePathObject( null, connectingRoad, null, null );
+
+ let lane = connectingRoad.getFirstLaneSection().getLaneById( 1 );
+
+ if ( !lane ) lane = connectingRoad.getFirstLaneSection().getLaneById( -1 );
+
+ const width = connectingRoad.getFirstLaneSection().getWidthUptoCenter( lane, 0 );
+
+ const spline = connectingRoad.spline;
+
+ const shape = new Shape(); shape.moveTo( 0, -0.3 ); shape.lineTo( 0, 0.3 );
+
+ if ( spline.controlPointPositions.length < 2 ) return;
+
+ let offset = width;
+
+ if ( lane.id < 0 ) offset *= -1;
+
+ const path = this.getPath( spline, offset );
+
+ const lineMaterial = new LineBasicMaterial( {
+ color: 0x00ffff,
+ linewidth: 100,
+ opacity: 0.5,
+ transparent: true,
+ } );
+
+ const lineGeometry = new BufferGeometry().setFromPoints( path.getSpacedPoints( 50 ) );
+
+ pathObject.mesh = new Line( lineGeometry, lineMaterial );
+
+ pathObject.mesh.castShadow = true;
+
+ pathObject.mesh.renderOrder = 3;
+
+ pathObject.mesh.frustumCulled = false;
+
+ pathObject.mesh[ 'tag' ] = LanePathObject.tag;
+
+ pathObject.add( pathObject.mesh );
+
+ return pathObject;
+ }
+
+ static update ( pathObject: LanePathObject ) {
+
+ if ( !pathObject ) return;
+
+ const connectingRoad = pathObject.connectingRoad;
+
+ let lane = connectingRoad.getFirstLaneSection().getLaneById( 1 );
+
+ if ( !lane ) lane = connectingRoad.getFirstLaneSection().getLaneById( -1 );
+
+ const width = connectingRoad.getFirstLaneSection().getWidthUptoCenter( lane, 0 );
+
+ const spline = connectingRoad.spline;
+
+ const shape = new Shape(); shape.moveTo( 0, -0.3 ); shape.lineTo( 0, 0.3 );
+
+ if ( spline.controlPointPositions.length < 2 ) return;
+
+ let offset = width;
+
+ if ( lane.id < 0 ) offset *= -1;
+
+ const path = this.getPath( spline, offset );
+
+ pathObject.mesh.geometry.dispose();
+
+ pathObject.mesh.geometry = new BufferGeometry().setFromPoints( path.getSpacedPoints( 50 ) );
+
+ return pathObject;
+ }
+
+ private static getPath ( spline: AbstractSpline, offset: number ) {
+
+ if ( spline instanceof AutoSpline ) {
+
+ return new AutoSplinePath( spline, offset );
+
+ } else if ( spline instanceof ExplicitSpline ) {
+
+ return new ExplicitSplinePath( spline, offset );
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/app/core/factories/metadata-factory.service.ts b/src/app/core/factories/metadata-factory.service.ts
new file mode 100644
index 00000000..616ca642
--- /dev/null
+++ b/src/app/core/factories/metadata-factory.service.ts
@@ -0,0 +1,236 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Injectable, Type } from '@angular/core';
+import { Metadata } from '../models/metadata.model';
+import { Vector3, Texture, MeshStandardMaterial, RepeatWrapping, UVMapping, ImageLoader, TextureLoader } from 'three';
+import { FileService } from 'app/services/file.service';
+import { FileNode } from 'app/views/editor/project-browser/file-node.model';
+import { SnackBar } from 'app/services/snack-bar.service';
+import { AssetDatabase } from 'app/services/asset-database';
+import { AppService } from '../services/app.service';
+import * as THREE from 'three';
+import { TvRoadMarking } from 'app/modules/tv-map/services/tv-marking.service';
+import { RoadStyle } from 'app/services/road-style.service';
+
+@Injectable( {
+ providedIn: 'root'
+} )
+export class MetadataFactory {
+
+ private static get fileService (): FileService {
+
+ return AppService.file;
+
+ }
+
+ static saveMetadataFile ( file: FileNode | string, metadata: Metadata ): void {
+
+ try {
+
+ let path = null;
+
+ if ( typeof ( file ) === 'string' ) path = file;
+
+ if ( typeof ( file ) === 'object' ) path = file.path;
+
+ if ( !path.includes( '.meta' ) ) path = path + '.meta';
+
+ this.fileService.fs.writeFileSync( path, JSON.stringify( metadata, null, 2 ) );
+
+ } catch ( error ) {
+
+ console.error( error );
+
+ SnackBar.error( "Error in writing .meta file. Please Reimport the asset.", "", 5000 );
+ }
+
+ }
+
+ static createMetadata ( fileName: string, ext: string, path: string ): Metadata {
+
+ const extension = ext || FileService.getExtension( path );
+
+ const guid = THREE.Math.generateUUID();
+
+ let metadata: Metadata;
+
+ switch ( extension ) {
+
+ case 'scene': metadata = this.createSceneMetadata( fileName, guid, path ); break;
+
+ case 'obj': metadata = this.createModelMetadata( fileName, guid, path ); break;
+
+ case 'fbx': metadata = this.createModelMetadata( fileName, guid, path ); break;
+
+ case 'gltf': metadata = this.createModelMetadata( fileName, guid, path ); break;
+
+ case 'glb': metadata = this.createModelMetadata( fileName, guid, path ); break;
+
+ case 'xodr': metadata = this.createOpenDriveMetadata( fileName, guid, path ); break;
+
+ case 'png': metadata = this.createTextureMetaInternal( guid, path ); break;
+ case 'jpg': metadata = this.createTextureMetaInternal( guid, path ); break;
+ case 'jpeg': metadata = this.createTextureMetaInternal( guid, path ); break;
+ case 'svg': metadata = this.createTextureMetaInternal( guid, path ); break;
+
+ case 'material': metadata = this.createMaterialMetadata( fileName, guid, path ); break;
+
+ case 'sign': metadata = this.createSignMetadata( fileName, guid, path ); break;
+
+ case TvRoadMarking.extension: metadata = this.createRoadMarkingMetadata( fileName, guid, path ); break;
+
+ case RoadStyle.extension: metadata = this.createRoadStyleMetadata( fileName, guid, path ); break;
+
+ }
+
+ if ( metadata ) this.saveMetadataFile( path, metadata );
+
+ if ( metadata ) AssetDatabase.setMetadata( guid, metadata );
+
+ return metadata;
+ }
+
+ static createRoadMarkingMetadata ( name: string, guid: string, path: string ): Metadata {
+
+ return {
+ guid: guid,
+ importer: "RoadMarkingImporter",
+ data: {},
+ path: path,
+ };
+
+ }
+
+ static createRoadStyleMetadata ( name: string, guid: string, path: string ): Metadata {
+
+ return {
+ guid: guid,
+ importer: RoadStyle.importer,
+ data: {},
+ path: path,
+ };
+
+ }
+
+ private static createTextureMetaInternal ( guid: string, path: string ): Metadata {
+
+ const texture = this.loadTexture( path );
+
+ const metadata = this.createTextureMetadata( guid, path, texture );
+
+ AssetDatabase.setInstance( metadata.guid, texture );
+
+ return metadata;
+ }
+
+ static createFolderMetadata ( name: string, path: string ): Metadata {
+
+ const guid = THREE.Math.generateUUID();
+
+ const metadata = { guid: guid, isFolder: true, path: path, importer: null, data: null };
+
+ this.saveMetadataFile( path, metadata );
+
+ AssetDatabase.setMetadata( guid, metadata );
+
+ return metadata;
+ }
+
+ static createSceneMetadata ( name: string, guid: string, path: string ) {
+
+ return {
+ guid: guid,
+ importer: 'SceneImporter',
+ data: {},
+ path: path
+ };
+
+ }
+
+ static createModelMetadata ( name: string, guid: string, path: string ) {
+
+ return {
+ guid: guid,
+ importer: 'ModelImporter',
+ data: { name: name, rotationVariance: new Vector3( 0, 0, 0 ), scaleVariance: new Vector3( 0, 0, 0 ) },
+ path: path
+ };
+
+ }
+
+ static createOpenDriveMetadata ( name: string, guid: string, path: string ) {
+
+ return {
+ guid: guid,
+ importer: "OpenDriveImporter",
+ data: {},
+ path: path
+ };
+
+ }
+
+ static createTextureMetadata ( guid: string, path: string, texture: Texture ) {
+
+ const data = texture.toJSON( undefined );
+
+ const version = data.metadata.version || 4.5;
+
+ data.metadata = null;
+
+ return {
+ guid: guid,
+ version: version,
+ type: "Texture",
+ importer: "TextureImporter",
+ data: data,
+ path: path
+ };
+
+ }
+
+ static createMaterialMetadata ( name: string, guid: string, path: string ) {
+
+ return {
+ guid: guid,
+ importer: "MaterialImporter",
+ data: {},
+ path: path,
+ };
+
+ }
+
+ static createSignMetadata ( name: string, guid: string, path: string ) {
+
+ return {
+ guid: guid,
+ importer: "SignImporter",
+ data: {},
+ path: path,
+ };
+
+ }
+
+ static loadTexture ( path: string ): Texture {
+
+ try {
+
+ const texture = new TextureLoader().load( path );
+
+ texture.wrapS = RepeatWrapping;
+ texture.wrapT = RepeatWrapping;
+ texture.mapping = UVMapping;
+ texture.repeat.set( 1, 1 );
+
+ return texture;
+
+ } catch ( error ) {
+
+ SnackBar.error( error );
+
+ return null;
+
+ }
+ }
+}
diff --git a/src/app/core/factories/node-factory.service.ts b/src/app/core/factories/node-factory.service.ts
new file mode 100644
index 00000000..29b757e1
--- /dev/null
+++ b/src/app/core/factories/node-factory.service.ts
@@ -0,0 +1,400 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Injectable } from '@angular/core';
+import { AnyControlPoint, LaneOffsetNode, LaneRoadMarkNode, LaneWidthNode } from 'app/modules/three-js/objects/control-point';
+import { RoadNode } from 'app/modules/three-js/objects/road-node';
+import { TvMapQueries } from 'app/modules/tv-map/queries/tv-map-queries';
+import { BufferGeometry, LineBasicMaterial, LineSegments, Vector3 } from 'three';
+import { COLOR } from 'app/shared/utils/colors.service';
+import { TvPosTheta } from 'app/modules/tv-map/models/tv-pos-theta';
+import { TvLaneWidth } from 'app/modules/tv-map/models/tv-lane-width';
+import { TvRoadLaneOffset } from 'app/modules/tv-map/models/tv-road-lane-offset';
+import { TvRoad } from 'app/modules/tv-map/models/tv-road.model';
+import { Maths } from 'app/utils/maths';
+import { SceneService } from '../services/scene.service';
+import { TvLane } from 'app/modules/tv-map/models/tv-lane';
+import { TvLaneRoadMark } from 'app/modules/tv-map/models/tv-lane-road-mark';
+import { SnackBar } from 'app/services/snack-bar.service';
+
+
+@Injectable( {
+ providedIn: 'root'
+} )
+export class NodeFactoryService {
+
+ constructor () {
+ }
+
+ static createLaneWidthNodeByPosition ( road: TvRoad, lane: TvLane, point: Vector3 ): LaneWidthNode {
+
+ const posTheta = new TvPosTheta();
+
+ // getting position on track in s/t coordinates
+ TvMapQueries.getRoadByCoords( point.x, point.y, posTheta );
+
+ // get the exisiting lane width at s
+ // and clone the lane width
+ const laneWidth = lane.getLaneWidthAt( posTheta.s ).clone( posTheta.s );
+
+ // add the with back to lane to
+ lane.addWidthRecordInstance( laneWidth );
+
+ // make mesh for the lane width node
+ return laneWidth.mesh = this.createLaneWidthNode( road, lane, laneWidth.s, laneWidth );
+ }
+
+ static createLaneWidthNode ( road: TvRoad, lane: TvLane, s: number, laneWidth: TvLaneWidth ): LaneWidthNode {
+
+ const node = new LaneWidthNode( road, lane, s, laneWidth );
+
+ const offset = laneWidth.getValue( s ) * 0.5;
+
+ const start = TvMapQueries.getLanePosition( road.id, lane.id, s, -offset );
+ const end = TvMapQueries.getLanePosition( road.id, lane.id, s, offset );
+
+ /////////////////////////////////////////
+
+ node.point = AnyControlPoint.create( 'point', end );
+
+ node.point.tag = LaneWidthNode.pointTag;
+
+ node.add( node.point );
+
+ /////////////////////////////////////////
+
+ const lineGeometry = new BufferGeometry().setFromPoints( [ start, end ] );
+
+ node.line = new LineSegments( lineGeometry, new LineBasicMaterial( { color: COLOR.DARKBLUE, opacity: 0.35 } ) );
+
+ node.line[ 'tag' ] = LaneWidthNode.lineTag;
+
+ node.line.renderOrder = 3;
+
+ node.add( node.line );
+
+ //////////////////////////////////////////
+
+ // group.position.copy( center );
+
+ return node;
+ }
+
+ // update s value of lane-width as per the restriction
+ static updateLaneWidthNode ( node: LaneWidthNode, point: Vector3, direction = 'sCoordinate' ): void {
+
+ const index = node.lane.getLaneWidthVector().findIndex( i => i.uuid === node.laneWidth.uuid );
+
+ if ( index === -1 ) SnackBar.error( "Unexpected error. Not able to find this node" );
+ if ( index === -1 ) return;
+
+ if ( index === 0 ) SnackBar.error( "First node cannot be edited. Please add a new node." );
+ if ( index === 0 ) return;
+
+ const minS = node.lane.width[ index - 1 ].s + 0.1;
+
+ // TODO: mke this the max s value as per lane section
+ let maxS = Number.MAX_SAFE_INTEGER;
+
+ if ( index + 1 < node.lane.width.length ) {
+
+ maxS = node.lane.width[ index + 1 ].s - 0.1;
+
+ }
+
+ const newPosition = new TvPosTheta();
+
+ const road = TvMapQueries.getRoadByCoords( point.x, point.y, newPosition );
+
+ // we are getting another road s value to ignore
+ if ( node.lane.roadId !== road.id ) return;
+
+ // our desired s value should lie between the previous node and the next node
+ const adjustedS = Maths.clamp( newPosition.s, minS, maxS );
+
+ // update s offset as per the new position on road
+ node.laneWidth.s = adjustedS;
+
+ // const offset = node.lane.getWidthValue( adjustedS ) * 0.5;
+
+ const offset = node.laneWidth.getValue( adjustedS ) * 0.5;
+
+ let sCoordinate = null;
+
+ if ( direction === 'sCoordinate' ) {
+
+ sCoordinate = adjustedS;
+
+ } else if ( direction === 'tCoordinate' ) {
+
+ sCoordinate = node.s;
+
+ }
+
+ const start = TvMapQueries.getLanePosition( node.roadId, node.laneId, sCoordinate, -offset );
+ const end = TvMapQueries.getLanePosition( node.roadId, node.laneId, sCoordinate, offset );
+
+
+ // TODO: can be improved
+ node.line.geometry.dispose();
+ node.line.geometry = new BufferGeometry().setFromPoints( [ start, end ] );
+
+ node.point.position.copy( end );
+
+ node.s = node.laneWidth.s = adjustedS;
+
+ // const roadPos = new OdPosTheta();
+ // const lanePos = new OdPosTheta();
+
+ // // this gets the road and the s and t values
+ // // const road = OpenDriveQueries.getRoadByCoords( point.x, point.y, roadPos );
+
+ // // this get the lane from road, s and t values
+ // // roadPos is only used to read
+ // // const result = OpenDriveQueries.getLaneByCoords( position.x, position.y, roadPos );
+
+ // if ( node.s < 0.1 ) return;
+
+ // if ( road ) {
+
+ // let finalPosition = null;
+ // let sCoordinate = null;
+
+ // if ( direction == 'sCoordinate' ) {
+
+ // sCoordinate = roadPos.s;
+
+ // } else if ( direction == 'tCoordinate' ) {
+
+ // sCoordinate = node.s;
+
+ // }
+
+ // const offset = laneWidth.getValue( roadPos.s ) * 0.5;
+
+ // const start = OpenDriveQueries.getLanePosition( node.roadId, node.laneId, sCoordinate, -offset );
+ // const end = OpenDriveQueries.getLanePosition( node.roadId, node.laneId, sCoordinate, offset );
+
+ // // TODO: can be improved
+ // node.line.geometry.dispose();
+ // node.line.geometry = new BufferGeometry().setFromPoints( [ start, end ] )
+
+ // node.point.position.copy( end );
+
+ // node.s = node.laneWidth.s = roadPos.s;
+ // }
+ }
+
+ /**
+ * Updates the lane width line position
+ * @param node LaneWidthNode
+ */
+ static updateLaneWidthNodeLine ( node: LaneWidthNode ) {
+
+ const laneWidth = node.laneWidth;
+
+ if ( node.s < 0 ) node.s = 0;
+
+ const offset = laneWidth.getValue( node.s ) * 0.5;
+
+ const start = TvMapQueries.getLanePosition( node.roadId, node.laneId, node.s, -offset );
+ const end = TvMapQueries.getLanePosition( node.roadId, node.laneId, node.s, offset );
+
+ // TODO: can be improved
+ node.line.geometry.dispose();
+ node.line.geometry = new BufferGeometry().setFromPoints( [ start, end ] );
+
+ node.point.position.copy( end );
+ }
+
+ static createLaneOffsetNode ( road: TvRoad, laneOffset: TvRoadLaneOffset ): LaneOffsetNode {
+
+ const node = new LaneOffsetNode( road, laneOffset );
+
+ const offset = laneOffset.getValue( laneOffset.s );
+
+ const start = TvMapQueries.findRoadById( road.id ).getPositionAt( laneOffset.s, 0 );
+
+ // const end = OpenDriveQueries.findRoadById( roadId ).getPositionAt( s, 0 );
+
+ /////////////////////////////////////////
+
+ node.point = AnyControlPoint.create( "point", start.toVector3() );
+
+ node.point.tag = LaneOffsetNode.pointTag;
+
+ node.add( node.point );
+
+ /////////////////////////////////////////
+
+ // const lineGeometry = new BufferGeometry().setFromPoints( [ start, end ] );
+
+ // node.line = new LineSegments( lineGeometry, new LineBasicMaterial( { color: COLOR.DARKBLUE, opacity: 0.35 } ) );
+
+ // node.line[ 'tag' ] = LaneOffsetNode.lineTag;
+
+ // node.line.renderOrder = 3;
+
+ // node.add( node.line );
+
+ //////////////////////////////////////////
+
+ // group.position.copy( center );
+
+ return node;
+ }
+
+ /**
+ * Updates the position of the node
+ * @param node
+ */
+ static updateLaneOffsetNode ( node: LaneOffsetNode ): LaneOffsetNode {
+
+ const offset = node.laneOffset.getValue( node.laneOffset.s );
+
+ const position = TvMapQueries.findRoadById( node.roadId ).getPositionAt( node.laneOffset.s, 0 );
+
+ node.point.copyPosition( position.toVector3() );
+
+ return node;
+ }
+
+ static createRoadMarkNode ( lane: TvLane, roadmark: TvLaneRoadMark ): LaneRoadMarkNode {
+
+ const node = new LaneRoadMarkNode( lane, roadmark );
+
+ const offset = lane.getWidthValue( roadmark.s ) * 0.5;
+
+ const position = TvMapQueries.getLanePosition( lane.roadId, lane.id, roadmark.s, offset );
+
+ node.point = AnyControlPoint.create( 'point', position );
+
+ node.point.tag = LaneRoadMarkNode.pointTag;
+
+ node.add( node.point );
+
+ return node;
+ }
+
+ static updateRoadMarkNodeByPosition ( node: LaneRoadMarkNode, point: Vector3 ) {
+
+ const index = node.lane.getRoadMarks().findIndex( roadmark => roadmark.uuid === node.roadmark.uuid );
+
+ if ( index === -1 ) SnackBar.error( "Unexpected error. Not able to find this node" );
+ if ( index === -1 ) return node;
+
+ if ( index === 0 ) SnackBar.error( "First node cannot be edited. Please add a new node." );
+ if ( index === 0 ) return node;
+
+ const minS = node.lane.roadMark[ index - 1 ].s + 0.1;
+
+ // TODO: mke this the max s value as per lane section
+ let maxS = Number.MAX_SAFE_INTEGER;
+
+ if ( index + 1 < node.lane.roadMark.length ) {
+
+ maxS = node.lane.roadMark[ index + 1 ].s - 0.1;
+
+ }
+
+ const newPosition = new TvPosTheta();
+
+ const road = TvMapQueries.getRoadByCoords( point.x, point.y, newPosition );
+
+ // we are getting another road s value to ignore
+ if ( node.lane.roadId !== road.id ) return node;
+
+ // our desired s value should lie between the previous node and the next node
+ const adjustedS = Maths.clamp( newPosition.s, minS, maxS );
+
+ // update s offset as per the new position on road
+ node.roadmark.sOffset = adjustedS;
+
+ const offset = node.lane.getWidthValue( adjustedS ) * 0.5;
+
+ const finalPosition = TvMapQueries.getLanePosition( node.lane.roadId, node.lane.id, adjustedS, offset );
+
+ node.point.copyPosition( finalPosition );
+
+ return node;
+ }
+
+ static createRoadNodes ( road: TvRoad ): void {
+
+ road.startNode = this.createRoadNode( road.id, "start" );
+ road.endNode = this.createRoadNode( road.id, "end" );
+
+ }
+
+ static updateRoadNodes ( road: TvRoad ): void {
+
+ if ( !road.startNode )
+ road.startNode = this.createRoadNode( road.id, "start" );
+ else
+ this.updateRoadNode( road.startNode );
+
+
+ if ( !road.endNode )
+ road.endNode = this.createRoadNode( road.id, "end" );
+ else
+ this.updateRoadNode( road.endNode );
+ }
+
+ static createRoadNode ( roadId: number, distance: 'start' | 'end' ) {
+
+ const road = TvMapQueries.findRoadById( roadId );
+
+ let s: number;
+
+ if ( distance == 'start' ) s = 0;
+
+ else if ( distance == 'end' ) s = road.length - Maths.Epsilon;
+
+ const node = new RoadNode( roadId, distance, s );
+
+ const result = TvMapQueries.getRoadWidthAt( road.id, s );
+
+ const start = TvMapQueries.getRoadPosition( road.id, s, result.leftSideWidth );
+ const end = TvMapQueries.getRoadPosition( road.id, s, -result.rightSideWidth );
+
+ const lineGeometry = new BufferGeometry().setFromPoints( [ start.toVector3(), end.toVector3() ] );
+
+ node.line = new LineSegments( lineGeometry, new LineBasicMaterial( {
+ color: RoadNode.defaultColor,
+ opacity: RoadNode.defaultOpacity,
+ linewidth: RoadNode.defaultWidth,
+ } ) );
+
+ node.line[ 'tag' ] = RoadNode.lineTag;
+
+ node.line.renderOrder = 3;
+
+ node.add( node.line );
+
+ SceneService.add( node );
+
+ return node;
+ }
+
+ static updateRoadNode ( node: RoadNode ): void {
+
+ const road = TvMapQueries.findRoadById( node.roadId );
+
+ let s: number;
+
+ if ( node.distance == 'start' ) s = 0;
+
+ else if ( node.distance == 'end' ) s = road.length - Maths.Epsilon;
+
+ const result = TvMapQueries.getRoadWidthAt( node.roadId, s );
+
+ const start = TvMapQueries.getRoadPosition( node.roadId, s, result.leftSideWidth );
+ const end = TvMapQueries.getRoadPosition( node.roadId, s, -result.rightSideWidth );
+
+ // TODO: can be improved
+ node.line.geometry.dispose();
+ node.line.geometry = new BufferGeometry().setFromPoints( [ start.toVector3(), end.toVector3() ] );
+
+ }
+}
diff --git a/src/app/core/factories/road-factory.service.ts b/src/app/core/factories/road-factory.service.ts
new file mode 100644
index 00000000..968a1d4f
--- /dev/null
+++ b/src/app/core/factories/road-factory.service.ts
@@ -0,0 +1,388 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvRoad } from 'app/modules/tv-map/models/tv-road.model';
+import { Vector3 } from 'three';
+import { SceneService } from '../services/scene.service';
+import { TvMapSourceFile } from 'app/modules/tv-map/services/tv-map-source-file';
+import { TvMapBuilder } from 'app/modules/tv-map/builders/od-builder.service';
+import { NodeFactoryService } from './node-factory.service';
+import { AppInspector } from '../inspector';
+import { RoadInspector } from 'app/views/inspectors/road-inspector/road-inspector.component';
+import { RoadNode } from 'app/modules/three-js/objects/road-node';
+import { TvContactPoint, TvLaneSide, TvRoadType } from 'app/modules/tv-map/models/tv-common';
+import { TvPosTheta } from 'app/modules/tv-map/models/tv-pos-theta';
+import { Maths } from 'app/utils/maths';
+import { TvLaneSection } from 'app/modules/tv-map/models/tv-lane-section';
+import { ExplicitSpline } from '../shapes/explicit-spline';
+import { RoadControlPoint } from 'app/modules/three-js/objects/road-control-point';
+
+export class RoadFactory {
+
+ static get openDrive () {
+ return TvMapSourceFile.openDrive;
+ }
+
+ static createRoad ( position: Vector3 ) {
+
+ const road = this.openDrive.addDefaultRoadWithType( TvRoadType.TOWN, 40 );
+
+ const point = this.addControlPoint( road, position );
+
+ return { road, point };
+ }
+
+ static removeRoad ( road: TvRoad ) {
+
+ this.openDrive.removeRoad( road );
+
+ }
+
+ static updateGeometryAndRebuild ( road: TvRoad ): void {
+
+ this.updateGeometry( road );
+
+ this.rebuildRoad( road );
+ }
+
+ static updateGeometry ( road: TvRoad ) {
+
+ road.updateGeometryFromSpline();
+
+ }
+
+ static rebuildRoad ( road: TvRoad ) {
+
+ this.openDrive.gameObject.remove( road.gameObject );
+
+ TvMapBuilder.buildRoad( this.openDrive.gameObject, road );
+
+ if ( !road.isJunction ) NodeFactoryService.updateRoadNodes( road );
+
+ }
+
+ static addControlPoint ( road: TvRoad, position: Vector3 ): RoadControlPoint {
+
+ let point: RoadControlPoint;
+
+ // TODO: Fix addControlPointAtNew
+
+ if ( road.spline instanceof ExplicitSpline ) {
+
+ point = road.spline.addControlPointAtNew( position );
+
+ } else {
+
+ point = new RoadControlPoint( road, position, "cp", 0, 0 );
+
+ point.mainObject = point.userData.road = road;
+
+ this.addControlPointNew( road, point );
+
+ }
+
+ return point;
+ }
+
+ static addControlPointNew ( road: TvRoad, point: RoadControlPoint ) {
+
+ // TODO: spline should take this responsibility
+ SceneService.add( point );
+
+ road.addControlPoint( point );
+
+ AppInspector.setInspector( RoadInspector, {
+ road: road,
+ controlPoint: point
+ } );
+
+ if ( road.spline.controlPoints.length > 1 ) {
+ this.updateGeometryAndRebuild( road );
+ };
+
+ }
+
+ static removeControlPoint ( road: TvRoad, cp: RoadControlPoint ) {
+
+ road.spline.removeControlPoint( cp );
+
+ SceneService.remove( cp );
+
+ if ( road.spline.controlPoints.length < 1 ) {
+
+ this.openDrive.gameObject.remove( road.gameObject );
+
+ // nothing to update, will throw error
+ // road.spline.update();
+
+ road.clearGeometries();
+
+
+ } else if ( road.spline.controlPoints.length === 1 ) {
+
+ this.openDrive.gameObject.remove( road.gameObject );
+
+ road.spline.update();
+
+ road.clearGeometries();
+
+ this.clearNodes( road );
+
+ } else if ( road.spline.controlPoints.length > 1 ) {
+
+ this.updateGeometryAndRebuild( road );
+
+ }
+ }
+
+ static clearNodes ( road: TvRoad ) {
+
+ if ( road.startNode ) {
+
+ SceneService.remove( road.startNode );
+
+ road.startNode = null;
+
+ }
+
+ if ( road.endNode ) {
+
+ SceneService.remove( road.endNode );
+
+ road.endNode = null;
+ }
+ }
+
+ static joinRoadNodes ( firstRoad: TvRoad, firstNode: RoadNode, secondRoad: TvRoad, secondNode: RoadNode ): TvRoad {
+
+ const distance = 20;
+
+ let laneSection: TvLaneSection, firstRoadS: number;
+
+ let firstPoint: RoadControlPoint, lastPoint: RoadControlPoint;
+
+ let secondPosition: TvPosTheta, thirdPosition: TvPosTheta, fourPosition: TvPosTheta, fivePosition: TvPosTheta;
+
+ if ( firstNode.distance === "start" ) {
+
+ firstRoadS = 0;
+
+ laneSection = firstRoad.getLaneSectionAt( 0 ).cloneAtS( 0, firstRoadS );
+
+ secondPosition = firstRoad.startPosition().clone().rotateDegree( 180 ).moveForward( distance );
+ thirdPosition = firstRoad.startPosition().clone().rotateDegree( 180 ).moveForward( distance + 20 ).addLateralOffset( 5 );
+
+ } else {
+
+ firstRoadS = firstRoad.length - Maths.Epsilon
+
+ laneSection = firstRoad.getLaneSectionAt( firstRoadS ).cloneAtS( 0, firstRoadS );
+
+ secondPosition = firstRoad.endPosition().clone().moveForward( distance );
+ thirdPosition = firstRoad.endPosition().clone().moveForward( distance + 20 ).addLateralOffset( 5 );
+
+ }
+
+ if ( secondNode.distance === "start" ) {
+
+ fourPosition = secondRoad.startPosition().clone().rotateDegree( 180 ).moveForward( distance + 20 ).addLateralOffset( 5 );
+ fivePosition = secondRoad.startPosition().clone().rotateDegree( 180 ).moveForward( distance )
+
+ } else {
+
+ fourPosition = secondRoad.endPosition().clone().moveForward( distance + 20 ).addLateralOffset( 5 );
+ fivePosition = secondRoad.endPosition().clone().moveForward( distance );
+
+ }
+
+ // Make control points required for road geometry
+ const joiningRoad = this.openDrive.addDefaultRoad();
+
+ if ( firstRoad.hasType ) {
+
+ const roadType = firstRoad.getRoadTypeAt( firstRoadS );
+
+ joiningRoad.setType( roadType.type, roadType.speed.max, roadType.speed.unit );
+
+ } else {
+
+ joiningRoad.setType( TvRoadType.TOWN, 40 );
+
+ }
+
+
+ // remove lane section as will create copy of first road
+ joiningRoad.clearLaneSections();
+
+ joiningRoad.addLaneSectionInstance( laneSection );
+
+ if ( firstNode.distance === "start" ) {
+
+ firstPoint = new RoadControlPoint( joiningRoad, firstRoad.spline.getFirstPoint().position.clone(), "cp", 0, 0 )
+
+ } else {
+
+ firstPoint = new RoadControlPoint( joiningRoad, firstRoad.spline.getLastPoint().position.clone(), "cp", 0, 0 )
+
+ }
+
+ if ( secondNode.distance === "start" ) {
+
+ lastPoint = new RoadControlPoint( joiningRoad, secondRoad.spline.getFirstPoint().position.clone(), "cp", 0, 0 );
+
+ } else {
+
+ lastPoint = new RoadControlPoint( joiningRoad, secondRoad.spline.getLastPoint().position.clone(), "cp", 0, 0 );
+
+ }
+
+ const secondPoint = new RoadControlPoint( joiningRoad, secondPosition.toVector3(), "cp", 0, 0 );
+ const thirdPoint = new RoadControlPoint( joiningRoad, thirdPosition.toVector3(), "cp", 0, 0 );
+ const fourthPoint = new RoadControlPoint( joiningRoad, fourPosition.toVector3(), "cp", 0, 0 );
+ const fifthPoint = new RoadControlPoint( joiningRoad, fivePosition.toVector3(), "cp", 0, 0 );
+
+ SceneService.add( firstPoint );
+ SceneService.add( secondPoint );
+ SceneService.add( thirdPoint );
+ SceneService.add( fifthPoint );
+ SceneService.add( fourthPoint );
+ SceneService.add( lastPoint );
+
+ joiningRoad.spline.addControlPoint( firstPoint );
+ joiningRoad.spline.addControlPoint( secondPoint );
+ joiningRoad.spline.addControlPoint( thirdPoint );
+ joiningRoad.spline.addControlPoint( fourthPoint );
+ joiningRoad.spline.addControlPoint( fifthPoint );
+ joiningRoad.spline.addControlPoint( lastPoint );
+
+
+ joiningRoad.updateGeometryFromSpline();
+
+ /////////////////////////////////////////////////////////////////////////////////
+
+
+ // TODO: add more logic to smoothen the geometry
+
+ // for ( let i = 0; i < roadC.geometries.length; i++ ) {
+
+ // const geometry = roadC.geometries[ i ];
+
+ // if ( geometry.geometryType === OdGeometryType.ARC ) {
+
+ // const arcGeometry = geometry as OdArcGeometry;
+
+ // if ( arcGeometry.attr_curvature < 0.01 ) {
+
+ // // first geometry so update second point
+ // if ( i < 1 ) {
+
+ // }
+
+ // }
+ // }
+
+ // }
+
+ /////////////////////////////////////////////////////////////////////////////////
+ //
+ // Update the road connections
+
+
+ this.makeRoadConnections( firstRoad, firstNode, secondRoad, secondNode, joiningRoad );
+
+ /////////////////////////////////////////////////////////////////////////////////
+
+
+ TvMapBuilder.buildRoad( this.openDrive.gameObject, joiningRoad );
+
+ return joiningRoad;
+ }
+
+ static makeRoadConnections ( firstRoad: TvRoad, firstNode: RoadNode, secondRoad: TvRoad, secondNode: RoadNode, joiningRoad: TvRoad ) {
+
+ if ( firstNode.distance === "start" ) {
+
+ // link will be negative as joining roaad will in opposite direction
+
+ firstRoad.setPredecessor( "road", joiningRoad.id, TvContactPoint.START );
+ firstRoad.getFirstLaneSection().lanes.forEach( lane => {
+ if ( lane.side !== TvLaneSide.CENTER ) lane.setPredecessor( -lane.id );
+ } );
+
+ joiningRoad.setPredecessor( "road", firstRoad.id, TvContactPoint.START );
+ joiningRoad.getFirstLaneSection().lanes.forEach( lane => {
+ if ( lane.side !== TvLaneSide.CENTER ) lane.setPredecessor( -lane.id );
+ } )
+
+ } else {
+
+ // links will be in same direction
+
+ firstRoad.setSuccessor( "road", joiningRoad.id, TvContactPoint.START );
+ firstRoad.getLastLaneSection().lanes.forEach( lane => {
+ if ( lane.side !== TvLaneSide.CENTER ) lane.setSuccessor( lane.id );
+ } );
+
+ joiningRoad.setPredecessor( "road", firstRoad.id, TvContactPoint.END );
+ joiningRoad.getFirstLaneSection().lanes.forEach( lane => {
+ if ( lane.side !== TvLaneSide.CENTER ) lane.setPredecessor( lane.id );
+ } )
+
+ }
+
+ if ( secondNode.distance === "start" ) {
+
+ secondRoad.setPredecessor( "road", joiningRoad.id, TvContactPoint.END );
+ secondRoad.getFirstLaneSection().lanes.forEach( lane => {
+ if ( lane.side !== TvLaneSide.CENTER ) lane.setPredecessor( lane.id );
+ } )
+
+ joiningRoad.setSuccessor( "road", secondRoad.id, TvContactPoint.START );
+ joiningRoad.getLastLaneSection().lanes.forEach( lane => {
+ if ( lane.side !== TvLaneSide.CENTER ) lane.setSuccessor( lane.id );
+ } )
+
+ } else {
+
+ secondRoad.setSuccessor( "road", joiningRoad.id, TvContactPoint.END );
+ secondRoad.getLastLaneSection().lanes.forEach( lane => {
+ if ( lane.side !== TvLaneSide.CENTER ) lane.setSuccessor( -lane.id );
+ } )
+
+ joiningRoad.setSuccessor( "road", secondRoad.id, TvContactPoint.END );
+ joiningRoad.getLastLaneSection().lanes.forEach( lane => {
+ if ( lane.side !== TvLaneSide.CENTER ) lane.setSuccessor( -lane.id );
+ } )
+
+ }
+ }
+
+ static removeRoadConnections ( firstRoad: TvRoad, secondRoad: TvRoad ) {
+
+ if ( firstRoad.predecessor && firstRoad.predecessor.elementId === secondRoad.id ) {
+
+ firstRoad.predecessor = null;
+
+ }
+
+ if ( firstRoad.successor && firstRoad.successor.elementId === secondRoad.id ) {
+
+ firstRoad.successor = null;
+
+ }
+
+ if ( secondRoad.predecessor && secondRoad.predecessor.elementId === firstRoad.id ) {
+
+ secondRoad.predecessor = null;
+
+ }
+
+ if ( secondRoad.successor && secondRoad.successor.elementId === firstRoad.id ) {
+
+ secondRoad.successor = null;
+
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/app/core/game-object.ts b/src/app/core/game-object.ts
new file mode 100644
index 00000000..754ec8bc
--- /dev/null
+++ b/src/app/core/game-object.ts
@@ -0,0 +1,134 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { ElementRef, Type } from '@angular/core';
+import * as THREE from 'three';
+import { BufferGeometry, Euler, Geometry, Material, Vector3 } from 'three';
+import { ITvObject, TvObjectType } from '../modules/tv-map/interfaces/i-tv-object';
+
+export interface IComponent {
+ data: any;
+}
+
+export class ComponentItem {
+ constructor ( public component: Type, public data: any ) {
+ }
+}
+
+export interface IComponentEditor {
+ componentContent: ElementRef;
+}
+
+export class Component {
+
+}
+
+export class Transform extends Component {
+
+ public position: Vector3 = new Vector3;
+ public eulerAngles: Euler = new Euler;
+ public scale: Vector3 = new Vector3;
+
+}
+
+export class SomeNewComponent extends Component {
+
+ public position: any;
+
+}
+
+export class GameObject extends THREE.Mesh implements ITvObject {
+
+ OpenDriveType: TvObjectType;
+
+ getType (): TvObjectType {
+ return this.OpenDriveType;
+ }
+
+ public IsGameObject = true;
+ public detectRaycast = true;
+ private active: boolean;
+ private tag: string;
+ private transform: Transform;
+ private components: Component[] = [];
+
+ constructor ( name?: string, geometry?: Geometry | BufferGeometry, material?: Material | Material[] ) {
+
+ super( geometry, material );
+
+ this.name = name;
+ this.transform = new Transform;
+ this.transform.position = this.position;
+ this.transform.eulerAngles = this.rotation;
+ this.transform.scale = this.scale;
+
+ this.userData.is_selectable = true;
+
+ // AppService.engine.add( this );
+
+ }
+
+ get Active () {
+ return this.active;
+ }
+
+ set Active ( value ) {
+ this.active = value;
+ }
+
+ get Tag () {
+ return this.tag;
+ }
+
+ set Tag ( value ) {
+ this.tag = value;
+ }
+
+ get Transform () {
+ return this.transform;
+ }
+
+ set Transform ( value ) {
+ this.transform = value;
+ }
+
+ public addComponent ( componentType: Type ): T {
+
+ const obj = new componentType;
+
+ this.components.push( obj );
+
+ return obj;
+
+ }
+
+ public getComponent ( componentType: Type ): Component {
+
+ for ( let i = 0; i < this.components.length; i++ ) {
+
+ const element: Component = this.components[i];
+
+ if ( element instanceof componentType ) {
+
+ return element;
+
+ }
+ }
+
+ }
+
+}
+
+export class Cube extends GameObject {
+
+ constructor ( width: number = 1, height: number = 1, length: number = 1 ) {
+ super(
+ 'Cube',
+ new THREE.BoxBufferGeometry( width, height, length ),
+ new THREE.MeshBasicMaterial( { color: 0x000000 }
+ )
+ );
+ }
+
+}
diff --git a/src/app/core/guards/auth.guard.ts b/src/app/core/guards/auth.guard.ts
new file mode 100644
index 00000000..b8632e5d
--- /dev/null
+++ b/src/app/core/guards/auth.guard.ts
@@ -0,0 +1,40 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Injectable } from '@angular/core';
+import { CanActivate, Router } from '@angular/router';
+import { AuthService } from '../services/auth.service';
+import { JwtService } from '../services/jwt.service';
+import { User } from '../models/user';
+
+@Injectable( {
+ providedIn: 'root'
+} )
+export class AuthGuard implements CanActivate {
+
+ private user: User;
+
+ constructor ( public auth: AuthService, private jwt: JwtService, public router: Router ) {
+
+ this.auth.currentUser.subscribe( user => this.user = user );
+
+ }
+
+ canActivate (): boolean {
+
+ if ( this.jwt.hasToken() && !this.jwt.isTokenExpired() ) {
+
+ return true;
+
+ } else {
+
+ this.router.navigate( [ '/sessions/signin' ] );
+
+ return false;
+
+ }
+
+ }
+
+}
diff --git a/src/app/core/helpers/road-plan-helper.spec.ts b/src/app/core/helpers/road-plan-helper.spec.ts
new file mode 100644
index 00000000..5b6a6be9
--- /dev/null
+++ b/src/app/core/helpers/road-plan-helper.spec.ts
@@ -0,0 +1,80 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TestBed } from '@angular/core/testing';
+import { RoadPlanHelper } from './road-plan-helper';
+import { Vector3 } from 'three';
+import { Maths } from '../../utils/maths';
+
+
+describe( 'RoadPlanHelper test', () => {
+
+ let helper: RoadPlanHelper;
+
+ beforeEach( () => TestBed.configureTestingModule( {} ) );
+
+ beforeEach( () => {
+ helper = new RoadPlanHelper( null );
+ } );
+
+ it( 'should give correct angle', () => {
+
+ let p1, p2, p3: Vector3;
+
+ p1 = new Vector3( 0, 0, 0 );
+ p2 = new Vector3( 10, 0, 0 );
+
+ ////////////////////////////////////////////////////////////////
+
+ p3 = new Vector3( 5, 5, 0 );
+
+ expect( Math.round( helper.calculateAngle( p1, p2, p3 ) ) ).toBe( 45 );
+
+ ////////////////////////////////////////////////////////////////
+
+ p3 = new Vector3( 10, 10, 0 );
+
+ expect( Math.round( helper.calculateAngle( p1, p2, p3 ) ) ).toBe( 90 );
+
+ ////////////////////////////////////////////////////////////////
+
+ p3 = new Vector3( 15, 5, 0 );
+
+ expect( Math.round( helper.calculateAngle( p1, p2, p3 ) ) ).toBe( 135 );
+
+ } );
+
+ it( 'should give correct area for triangle', () => {
+
+ let p1, p2, p3: Vector3;
+
+ const height = 10;
+ const base = 10;
+
+ p1 = new Vector3( 0, 0, 0 );
+ p2 = new Vector3( 0, height, 0 );
+ p3 = new Vector3( base, height, 0 );
+
+ expect( Maths.areaOfTriangle( p1, p2, p3 ) ).toBe( 50 );
+
+ } );
+
+ it( 'should give correct height for triangle', () => {
+
+ let p1, p2, p3: Vector3;
+
+ const height = 8;
+ const base = 15;
+
+ p1 = new Vector3( 0, 0, 0 );
+ p2 = new Vector3( 0, height, 0 );
+ p3 = new Vector3( base, height, 0 );
+
+ expect( Maths.heightOfTriangle( p1, p2, p3 ) ).toBe( 7.0588235294117645 );
+
+
+ } );
+
+
+} );
diff --git a/src/app/core/helpers/road-plan-helper.ts b/src/app/core/helpers/road-plan-helper.ts
new file mode 100644
index 00000000..5c851792
--- /dev/null
+++ b/src/app/core/helpers/road-plan-helper.ts
@@ -0,0 +1,233 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { AbstractShapeEditor } from '../editors/abstract-shape-editor';
+import { TvLineGeometry } from '../../modules/tv-map/models/geometries/tv-line-geometry';
+import { Object3D, Vector2, Vector3 } from 'three';
+import { TvMap } from '../../modules/tv-map/models/tv-map.model';
+import { TvRoad } from '../../modules/tv-map/models/tv-road.model';
+import { TvAbstractRoadGeometry } from '../../modules/tv-map/models/geometries/tv-abstract-road-geometry';
+import { TvArcGeometry } from '../../modules/tv-map/models/geometries/tv-arc-geometry';
+import { Maths } from '../../utils/maths';
+import { TvMapBuilder } from '../../modules/tv-map/builders/od-builder.service';
+import { TvMapSourceFile } from '../../modules/tv-map/services/tv-map-source-file';
+import { SceneService } from '../services/scene.service';
+import { TvSide } from '../../modules/tv-map/models/tv-common';
+import { TvSpiralGeometry } from 'app/modules/tv-map/models/geometries/tv-spiral-geometry';
+
+export class RoadPlanHelper {
+
+ constructor ( private shapeEditor: AbstractShapeEditor ) {
+
+ }
+
+ setControlPointUserData ( object: Object3D, roadId, geometry, index ): void {
+
+ object.userData.roadId = roadId;
+ object.userData.geometry = geometry;
+ object.userData.index = index;
+
+ }
+
+ createRoadControlPoints ( openDrive: TvMap ): void {
+
+ // create control points for all geometries
+ openDrive.roads.forEach( road => {
+
+ road.geometries.forEach( geometry => {
+
+ // if line
+ if ( geometry instanceof TvLineGeometry ) {
+
+ const start = this.shapeEditor.addControlPoint( geometry.startV3 );
+ const end = this.shapeEditor.addControlPoint( geometry.endV3 );
+
+ this.setControlPointUserData( start, road.id, geometry, 0 );
+ this.setControlPointUserData( end, road.id, geometry, 1 );
+
+ } else if ( geometry instanceof TvArcGeometry ) {
+
+ this.createArcControlPoints( road, geometry );
+ }
+
+
+ } );
+
+ } );
+
+ }
+
+ updateRoadGeometry ( point: Object3D, road: TvRoad, geometry: TvAbstractRoadGeometry, index: number ): void {
+
+ if ( geometry instanceof TvLineGeometry ) {
+
+ this.updateLineGeometry( point, road, geometry as TvLineGeometry, index );
+
+ } else if ( geometry instanceof TvArcGeometry ) {
+
+ this.updateArcGeometry( point, road, geometry as TvArcGeometry, index );
+
+ }
+
+ }
+
+ createLineGeometry ( controlPoint1: Object3D, controlPoint2: Object3D, road: TvRoad ) {
+
+ const start = controlPoint1.position;
+ const end = controlPoint2.position;
+
+ const hdg = Math.atan2( end.y - start.y, end.x - start.x );
+ const length = road.length = start.distanceTo( end );
+
+ const geometry = new TvLineGeometry( 0, start.x, start.y, hdg, length );
+ // const geometry = road.addGeometryLine( 0, start.x, start.y, hdg, length );
+
+ this.setControlPointUserData( controlPoint1, road.id, geometry, 0 );
+ this.setControlPointUserData( controlPoint2, road.id, geometry, 1 );
+
+ // OdBuilder.makeRoad( OdSourceFile.openDrive.gameObject, road );
+
+ return geometry;
+ }
+
+ createSpiralGeometry ( controlPoint1: Object3D, controlPoint2: Object3D, road: TvRoad ) {
+
+ const start = controlPoint1.position;
+ const end = controlPoint2.position;
+
+ const hdg = Math.atan2( end.y - start.y, end.x - start.x );
+ const length = road.length = start.distanceTo( end );
+
+ const geometry = new TvSpiralGeometry( 0, start.x, start.y, hdg, length, 0.005, -0.001 );
+ // const geometry = road.addGeometryLine( 0, start.x, start.y, hdg, length );
+
+ this.setControlPointUserData( controlPoint1, road.id, geometry, 0 );
+ this.setControlPointUserData( controlPoint2, road.id, geometry, 1 );
+
+ // OdBuilder.makeRoad( OdSourceFile.openDrive.gameObject, road );
+
+ return geometry;
+ }
+
+ createArcGeometry ( cp1: Object3D, cp2: Object3D, cp3: Object3D, road: TvRoad ) {
+
+ const start = cp1.position;
+ const middle = cp2.position;
+ const end = cp3.position;
+
+ const hdg = Math.atan2( middle.y - start.y, middle.x - start.x );
+ const length = road.length = start.distanceTo( end );
+
+ const height = Maths.heightOfTriangle( start, middle, end );
+ const base = start.distanceTo( end );
+
+ const radius = ( height / 2 ) + ( ( base * base ) / ( 8 * height ) );
+ let curvature = 1 / radius;
+
+ // make the curvature negative for right side i.e. for clockwise
+ if ( Maths.direction( start, middle, end ) == TvSide.RIGHT ) curvature *= -1;
+
+ const geometry = new TvArcGeometry( 0, start.x, start.y, hdg, length, curvature );
+
+ road.addGeometry( geometry );
+
+ this.setControlPointUserData( cp1, road.id, geometry, 0 );
+ this.setControlPointUserData( cp2, road.id, geometry, 1 );
+ this.setControlPointUserData( cp3, road.id, geometry, 2 );
+
+ geometry.cp1 = cp1;
+ geometry.cp2 = cp2;
+ geometry.cp3 = cp3;
+
+ TvMapBuilder.buildRoad( TvMapSourceFile.openDrive.gameObject, road );
+ }
+
+ // TODO: move this in MathUtils
+ public calculateAngle ( p1: Vector3, p2: Vector3, p3: Vector3 ) {
+
+ const A = new Vector2( p1.x, p1.y );
+ const B = new Vector2( p2.x, p2.y );
+ const C = new Vector2( p3.x, p3.y );
+
+ const a = A.distanceTo( B );
+ const b = B.distanceTo( C );
+ const c = A.distanceTo( C );
+
+ return Maths.Rad2Deg * Math.acos( ( a * a + b * b - c * c ) / ( 2 * a * b ) );
+ }
+
+ public updateArcGeometry ( point: Object3D, road: TvRoad, geometry: TvArcGeometry, index: number ) {
+
+ let start: Vector3 = geometry.cp1.position;
+ let middle: Vector3 = geometry.cp2.position;
+ let end: Vector3 = geometry.cp3.position;
+
+ const height = Maths.heightOfTriangle( start, middle, end );
+ const base = start.distanceTo( end );
+
+ const radius = ( height / 2 ) + ( ( base * base ) / ( 8 * height ) );
+
+ let curvature = 1 / radius;
+
+ // make the curvature negative for right side i.e. for clockwise
+ if ( Maths.direction( start, middle, end ) == TvSide.RIGHT ) curvature *= -1;
+
+ geometry.x = start.x;
+ geometry.y = start.y;
+ geometry.curvature = curvature;
+ geometry.hdg = Math.atan2( middle.y - start.y, middle.x - start.x );
+ geometry.length = road.length = start.distanceTo( end );
+
+ this.removeOldAndCreateNew( road );
+ }
+
+ private updateLineGeometry ( point: Object3D, road: TvRoad, geometry: TvLineGeometry, index: number ): void {
+
+ let start: Vector3;
+ let end: Vector3;
+
+ if ( index == 0 ) {
+
+ start = point.position;
+ end = geometry.endV3;
+
+ } else if ( index == 1 ) {
+
+ start = geometry.startV3;
+ end = point.position;
+
+ }
+
+ geometry.x = start.x;
+
+ geometry.y = start.y;
+
+ geometry.hdg = Math.atan2( end.y - start.y, end.x - start.x );
+
+ geometry.length = road.length = start.distanceTo( end );
+
+ this.removeOldAndCreateNew( road );
+ }
+
+ private createArcControlPoints ( road: TvRoad, geometry: TvArcGeometry ) {
+
+ const p1 = geometry.cp1 ? geometry.cp1.position : geometry.startV3;
+ const p2 = geometry.cp2 ? geometry.cp2.position : geometry.middleV3;
+ const p3 = geometry.cp3 ? geometry.cp3.position : geometry.endV3;
+
+ const start = geometry.cp1 = this.shapeEditor.addControlPoint( p1 );
+ const middle = geometry.cp2 = this.shapeEditor.addControlPoint( p2 );
+ const end = geometry.cp3 = this.shapeEditor.addControlPoint( p3 );
+
+ this.setControlPointUserData( start, road.id, geometry, 0 );
+ this.setControlPointUserData( middle, road.id, geometry, 1 );
+ this.setControlPointUserData( end, road.id, geometry, 2 );
+
+ }
+
+ private removeOldAndCreateNew ( road: TvRoad ) {
+ SceneService.removeWithChildren( road.gameObject, true );
+ TvMapBuilder.buildRoad( TvMapSourceFile.openDrive.gameObject, road );
+ }
+}
diff --git a/src/app/core/input.ts b/src/app/core/input.ts
new file mode 100644
index 00000000..917358a2
--- /dev/null
+++ b/src/app/core/input.ts
@@ -0,0 +1,37 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { EventEmitter } from '@angular/core';
+
+export class KeyboardInput {
+
+ public static keyUp = new EventEmitter();
+ public static keyDown = new EventEmitter();
+
+ public static isShiftKeyDown: boolean;
+
+ private static isKeyDown: boolean;
+ private static keyCode: number;
+
+ static OnKeyDown ( e: KeyboardEvent ) {
+
+ this.isKeyDown = true;
+ this.keyCode = e.keyCode;
+
+ this.keyUp.emit( e );
+
+ this.isShiftKeyDown = e.shiftKey;
+ }
+
+ static OnKeyUp ( e: KeyboardEvent ) {
+
+ this.isKeyDown = false;
+ this.keyCode = e.keyCode;
+
+ this.keyDown.emit( e );
+
+ this.isShiftKeyDown = e.shiftKey;
+ }
+
+}
\ No newline at end of file
diff --git a/src/app/core/inspector.ts b/src/app/core/inspector.ts
new file mode 100644
index 00000000..38b093f1
--- /dev/null
+++ b/src/app/core/inspector.ts
@@ -0,0 +1,55 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { EventEmitter, Type } from '@angular/core';
+import { ComponentItem, IComponent } from './game-object';
+
+export class AppInspector {
+
+ public static inspectorChanged = new EventEmitter();
+ public static inspectorCleared = new EventEmitter();
+
+ /**
+ * fired when new instance of inspector is created
+ */
+ public static inspectorCreated = new EventEmitter();
+
+ private static componentItem: ComponentItem;
+
+ public static currentInspector: Type;
+ public static currentInspectorData: any;
+
+ public static setInspector ( component: Type, data: any ) {
+
+ if ( !component ) this.clear();
+
+ if ( !component ) return;
+
+ this.currentInspector = component;
+
+ this.currentInspectorData = data;
+
+ this.componentItem = new ComponentItem( component, data );
+
+ this.inspectorChanged.emit( this.componentItem );
+
+ }
+
+ public static getInspector (): ComponentItem {
+
+ return this.componentItem;
+
+ }
+
+ static clear () {
+
+ this.currentInspector = null;
+
+ this.currentInspectorData = null;
+
+ this.inspectorCleared.emit( null );
+
+ }
+
+}
diff --git a/src/app/core/interceptors/http.token.interceptor.ts b/src/app/core/interceptors/http.token.interceptor.ts
new file mode 100644
index 00000000..c933fb53
--- /dev/null
+++ b/src/app/core/interceptors/http.token.interceptor.ts
@@ -0,0 +1,29 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Injectable, Injector } from '@angular/core';
+import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
+import { Observable } from 'rxjs';
+import { JwtService } from '../services/jwt.service';
+
+@Injectable()
+export class HttpTokenInterceptor implements HttpInterceptor {
+ constructor ( private jwtService: JwtService ) { }
+
+ intercept ( req: HttpRequest, next: HttpHandler ): Observable> {
+ const headersConfig = {
+ 'Content-Type': 'application/json',
+ 'Accept': 'application/json'
+ };
+
+ const token = this.jwtService.getToken();
+
+ if ( token ) {
+ headersConfig[ 'Authorization' ] = `Bearer ${ token }`;
+ }
+
+ const request = req.clone( { setHeaders: headersConfig } );
+ return next.handle( request );
+ }
+}
\ No newline at end of file
diff --git a/src/app/core/interceptors/time-out-interceptor.ts b/src/app/core/interceptors/time-out-interceptor.ts
new file mode 100644
index 00000000..fda1d005
--- /dev/null
+++ b/src/app/core/interceptors/time-out-interceptor.ts
@@ -0,0 +1,19 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
+import { Observable } from 'rxjs';
+import { timeout } from 'rxjs/operators';
+
+export class TimeOutInterceptor implements HttpInterceptor {
+
+ private defaultTimeout = 5000;
+
+ intercept ( req: HttpRequest, next: HttpHandler ): Observable> {
+
+ const timeoutValue = Number( req.headers.get( 'timeout' ) ) || this.defaultTimeout;
+
+ return next.handle( req ).pipe( timeout( timeoutValue ) );
+ }
+}
diff --git a/src/app/core/interfaces/shortcuts.ts b/src/app/core/interfaces/shortcuts.ts
new file mode 100644
index 00000000..272e3d29
--- /dev/null
+++ b/src/app/core/interfaces/shortcuts.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+export interface IKeyboardShortcut {
+
+ check ( e: KeyboardEvent ): boolean;
+
+ execute (): void;
+}
+
+export abstract class AbstractKeyboardShortcut implements IKeyboardShortcut {
+
+ abstract check ( e: KeyboardEvent ): boolean;
+ abstract execute (): void;
+
+}
\ No newline at end of file
diff --git a/src/app/core/models/file.ts b/src/app/core/models/file.ts
new file mode 100644
index 00000000..1ce05765
--- /dev/null
+++ b/src/app/core/models/file.ts
@@ -0,0 +1,26 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { FileUtils } from 'app/services/file-utils';
+
+export class IFile {
+
+ constructor (
+ public name?: string,
+ public path?: string,
+ public contents?: string,
+ public type?: string,
+ public online?: boolean,
+ public updatedAt?: Date
+ ) {
+ }
+
+ get directory () {
+ return FileUtils.getDirectoryFromPath( this.path );
+ }
+
+ get filename () {
+ return FileUtils.getFilenameFromPath( this.path );
+ }
+}
diff --git a/src/app/core/models/metadata.model.ts b/src/app/core/models/metadata.model.ts
new file mode 100644
index 00000000..41f245e8
--- /dev/null
+++ b/src/app/core/models/metadata.model.ts
@@ -0,0 +1,19 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+export interface Metadata {
+ guid: string;
+ path: string;
+ isFolder?: boolean;
+ importer: string;
+ data: any;
+ preview?: any;
+}
+
+export interface DynamicMeta {
+ guid: string;
+ path: string;
+ importer: string;
+ data: T;
+}
diff --git a/src/app/core/models/prop-instance.model.ts b/src/app/core/models/prop-instance.model.ts
new file mode 100644
index 00000000..1acb9c24
--- /dev/null
+++ b/src/app/core/models/prop-instance.model.ts
@@ -0,0 +1,14 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Object3D } from 'three';
+
+export class PropInstance {
+
+ constructor ( public guid: string, public object: Object3D ) {
+
+ }
+
+
+}
diff --git a/src/app/core/models/prop-model.model.ts b/src/app/core/models/prop-model.model.ts
new file mode 100644
index 00000000..c245aacc
--- /dev/null
+++ b/src/app/core/models/prop-model.model.ts
@@ -0,0 +1,15 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Object3D, Vector3 } from 'three';
+
+/**
+ * Prop class instance holds info about the prop
+ */
+export class PropModel {
+
+ constructor ( public guid: string, public rotationVariance: Vector3, public scaleVariance: Vector3 ) {
+
+ }
+}
diff --git a/src/app/core/models/user.ts b/src/app/core/models/user.ts
new file mode 100644
index 00000000..c4793fed
--- /dev/null
+++ b/src/app/core/models/user.ts
@@ -0,0 +1,6 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+export class User {
+}
diff --git a/src/app/core/pipes/max.pipe.ts b/src/app/core/pipes/max.pipe.ts
new file mode 100644
index 00000000..26697b28
--- /dev/null
+++ b/src/app/core/pipes/max.pipe.ts
@@ -0,0 +1,16 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({
+ name: 'max'
+})
+export class MaxPipe implements PipeTransform {
+
+ transform(value: any, args?: any): any {
+ return null;
+ }
+
+}
diff --git a/src/app/core/pipes/min.pipe.ts b/src/app/core/pipes/min.pipe.ts
new file mode 100644
index 00000000..63c12bf2
--- /dev/null
+++ b/src/app/core/pipes/min.pipe.ts
@@ -0,0 +1,23 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe( {
+ name: 'min'
+} )
+export class MinPipe implements PipeTransform {
+
+ // transform(value: any, args?: any): any {
+ // return null;
+ // }
+
+ transform ( value: number, min: number ): number {
+
+ var rtuenr = Math.max( value, min );;
+
+ return rtuenr;
+
+ }
+}
diff --git a/src/app/core/pipes/search.pipe.ts b/src/app/core/pipes/search.pipe.ts
new file mode 100644
index 00000000..c7b3bad0
--- /dev/null
+++ b/src/app/core/pipes/search.pipe.ts
@@ -0,0 +1,27 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe( {
+ name: 'search'
+} )
+export class SearchPipe implements PipeTransform {
+
+ transform ( items: string[], query: string ): string[] {
+
+ if ( !items ) return [];
+
+ if ( !query ) return items;
+
+ query = query.toLowerCase();
+
+ return items.filter( it => {
+
+ return it.toLowerCase().includes( query );
+
+ } );
+ }
+
+}
diff --git a/src/app/core/player.service.ts b/src/app/core/player.service.ts
new file mode 100644
index 00000000..678beda9
--- /dev/null
+++ b/src/app/core/player.service.ts
@@ -0,0 +1,106 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { EventEmitter, Injectable } from '@angular/core';
+import { Time } from './time';
+import { ToolManager } from './tools/tool-manager';
+
+export interface PlayerUpdateData {
+ time: number;
+ delta: number;
+}
+
+@Injectable( {
+ providedIn: 'root'
+} )
+export class PlayerService {
+
+ playerStarted = new EventEmitter();
+ playerResumed = new EventEmitter();
+ playerTick = new EventEmitter();
+ playerPaused = new EventEmitter();
+ playerStopped = new EventEmitter();
+
+ private prevTime: number;
+ private handle: number;
+ private paused: boolean;
+
+ constructor () {
+ }
+
+ play () {
+
+ ToolManager.disable();
+
+ this.prevTime = performance.now();
+
+ if ( this.paused ) {
+
+ this.playerResumed.emit();
+
+ } else {
+
+ this.playerStarted.emit();
+
+ }
+
+ this.paused = false;
+
+ const self = this;
+
+ ( function tick () {
+
+ self.handle = requestAnimationFrame( tick );
+
+ self.tick();
+
+ }() );
+ }
+
+ pause () {
+
+ this.playerPaused.emit();
+
+ this.paused = true;
+
+ cancelAnimationFrame( this.handle );
+ }
+
+ stop () {
+
+ cancelAnimationFrame( this.handle );
+
+ this.playerStopped.emit();
+
+ this.paused = false;
+
+ Time.reset();
+
+ ToolManager.enable();
+ }
+
+ tick () {
+
+ const time = performance.now();
+
+ try {
+
+ Time.deltaTime = Time.fixedDeltaTime / 1000;
+ Time.deltaTimeInMs = Time.fixedDeltaTime;
+ Time.time += Time.fixedDeltaTime;
+ Time.frameCount++;
+ Time.realTimeSinceStart = time;
+
+ this.playerTick.emit( { time: time, delta: Time.fixedDeltaTime } );
+
+ } catch ( e ) {
+
+ console.error( ( e.message || e ), ( e.stack || '' ) );
+
+ }
+
+ this.prevTime = time;
+
+ }
+}
diff --git a/src/app/core/selection.ts b/src/app/core/selection.ts
new file mode 100644
index 00000000..df3a3402
--- /dev/null
+++ b/src/app/core/selection.ts
@@ -0,0 +1,64 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { GameObject, Transform } from './game-object';
+import { EventEmitter } from '@angular/core';
+
+export class ObjectSelection {
+
+ static readonly selectionChange = new EventEmitter();
+
+ private static activeObject: THREE.Object3D;
+ private static activeGameObject: GameObject;
+ private static activeTransform: Transform;
+
+ static get ActiveObject () {
+
+ return this.activeObject
+
+ };
+
+ static set ActiveObject ( object ) {
+
+ this.activeObject = object
+ this.activeTransform.position = object.position;
+
+ this.selectionChange.emit( this.activeGameObject );
+
+ };
+
+ static get ActiveGameObject () {
+
+ return this.activeGameObject;
+
+ }
+
+ static set ActiveGameObject ( gameObject ) {
+
+
+ this.activeGameObject = gameObject
+ this.activeTransform = gameObject.Transform
+
+ this.selectionChange.emit( this.activeGameObject );
+
+ }
+
+ static get ActiveTransform () {
+
+ return this.activeTransform;
+
+ }
+
+ static removeActive (): any {
+
+ this.activeObject = null;
+ this.activeGameObject = null;
+ this.activeTransform = null;
+
+ this.selectionChange.emit( null );
+ }
+
+
+
+}
\ No newline at end of file
diff --git a/src/app/core/services/IEngine.ts b/src/app/core/services/IEngine.ts
new file mode 100644
index 00000000..25f6a5af
--- /dev/null
+++ b/src/app/core/services/IEngine.ts
@@ -0,0 +1,20 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import * as THREE from 'three';
+
+export interface IEngine {
+
+ add ( object: THREE.Object3D, raycasting?: boolean ): void;
+
+ remove ( object: THREE.Object3D, raycasting?: boolean ): void;
+
+ focus ( object: THREE.Object3D ): void;
+
+ reset (): void;
+
+ select ( object: THREE.Object3D ): void;
+
+ deselect ( object: THREE.Object3D ): void;
+}
\ No newline at end of file
diff --git a/src/app/core/services/abstract-reader.ts b/src/app/core/services/abstract-reader.ts
new file mode 100644
index 00000000..c49533db
--- /dev/null
+++ b/src/app/core/services/abstract-reader.ts
@@ -0,0 +1,70 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+export abstract class AbstractReader {
+
+ public static readAsOptionalArray ( items: any, callbackFn: ( xml: any, count: number ) => void ) {
+
+ if ( items != null ) {
+
+ if ( Array.isArray( items ) ) {
+
+ for ( const item of items ) {
+
+ callbackFn( item, items.length );
+
+ }
+
+ } else {
+
+ callbackFn( items, 1 );
+
+ }
+
+ }
+
+ }
+
+ public static readAsOptionalElement ( xml: any, callbackFn: ( xml: any ) => void ) {
+
+ if ( xml != null ) {
+
+ callbackFn( xml );
+
+ }
+
+ }
+
+ public readAsOptionalArray ( items: any, callbackFn: ( xml: any, count: number ) => void ) {
+
+ if ( items != null ) {
+
+ if ( Array.isArray( items ) ) {
+
+ for ( const item of items ) {
+
+ callbackFn( item, items.length );
+
+ }
+
+ } else {
+
+ callbackFn( items, 1 );
+
+ }
+
+ }
+
+ }
+
+ public readAsOptionalElement ( xml: any, callbackFn: ( xml: any ) => void ) {
+
+ if ( xml != null ) {
+
+ callbackFn( xml );
+
+ }
+
+ }
+}
diff --git a/src/app/core/services/api.service.ts b/src/app/core/services/api.service.ts
new file mode 100644
index 00000000..5de445c2
--- /dev/null
+++ b/src/app/core/services/api.service.ts
@@ -0,0 +1,48 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Injectable } from '@angular/core';
+import { environment } from '../../../environments/environment';
+import { HttpClient, HttpParams } from '@angular/common/http';
+import { Observable } from 'rxjs';
+
+import { throwError } from 'rxjs';
+import { catchError } from 'rxjs/operators';
+
+@Injectable()
+export class ApiService {
+
+ constructor (
+ private http: HttpClient
+ ) { }
+
+ private formatErrors ( error: any ) {
+ return throwError( error.error );
+ }
+
+ get ( path: string, params: HttpParams = new HttpParams() ): Observable {
+ return this.http.get( `${ environment.api_url }${ path }`, { params } )
+ .pipe( catchError( this.formatErrors ) );
+ }
+
+ put ( path: string, body: object = {} ): Observable {
+ return this.http.put(
+ `${ environment.api_url }${ path }`,
+ JSON.stringify( body )
+ ).pipe( catchError( this.formatErrors ) );
+ }
+
+ post ( path: string, body: object = {} ): Observable {
+ return this.http.post(
+ `${ environment.api_url }${ path }`,
+ JSON.stringify( body )
+ ).pipe( catchError( this.formatErrors ) );
+ }
+
+ delete ( path ): Observable {
+ return this.http.delete(
+ `${ environment.api_url }${ path }`
+ ).pipe( catchError( this.formatErrors ) );
+ }
+}
diff --git a/src/app/core/services/app-info.service.ts b/src/app/core/services/app-info.service.ts
new file mode 100644
index 00000000..6997975c
--- /dev/null
+++ b/src/app/core/services/app-info.service.ts
@@ -0,0 +1,16 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { ElectronService } from 'ngx-electron';
+
+export class AppInfo {
+
+ static electron: ElectronService;
+
+ static get isElectronApp (): boolean {
+
+ return this.electron.isElectronApp;
+
+ }
+}
\ No newline at end of file
diff --git a/src/app/core/services/app.service.ts b/src/app/core/services/app.service.ts
new file mode 100644
index 00000000..c1195da8
--- /dev/null
+++ b/src/app/core/services/app.service.ts
@@ -0,0 +1,67 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Injectable } from '@angular/core';
+import { ElectronService } from 'ngx-electron';
+import { EventSystem } from '../../events/event-system.service';
+import { ThreeService } from '../../modules/three-js/three.service';
+import { SnackBar } from '../../services/snack-bar.service';
+import { AuthService } from './auth.service';
+import { SceneService } from './scene.service';
+import { AssetLoaderService } from 'app/services/asset-loader.service';
+import { FileService } from 'app/services/file.service';
+import { AppInfo } from './app-info.service';
+import { SceneExporterService } from 'app/services/scene-exporter.service';
+
+@Injectable( {
+ providedIn: 'root'
+} )
+export class AppService {
+
+ static homeUrl = '';
+ static loginUrl = '/sessions/signin';
+
+ static eventSystem: EventSystem;
+ static three: ThreeService;
+ static electron: ElectronService;
+ static assets: AssetLoaderService;
+ static file: FileService;
+ static exporter: SceneExporterService;
+
+ constructor (
+ private eventSystem: EventSystem,
+ private electron: ElectronService,
+ private scene: SceneService,
+ private snackBar: SnackBar,
+ private three: ThreeService,
+ public auth: AuthService,
+ public assets: AssetLoaderService,
+ public files: FileService,
+ sceneExporter: SceneExporterService,
+ ) {
+
+ AppService.eventSystem = eventSystem;
+ AppService.three = three;
+ AppService.electron = electron;
+ AppService.assets = assets;
+ AppService.file = files;
+
+ AppService.exporter = sceneExporter;
+
+ AppInfo.electron = electron;
+ }
+
+ static get isElectronApp (): boolean {
+
+ return this.electron.isElectronApp;
+
+ }
+
+ public exit () {
+
+ this.electron.remote.app.exit( 0 );
+
+ }
+
+}
diff --git a/src/app/core/services/auth.service.ts b/src/app/core/services/auth.service.ts
new file mode 100644
index 00000000..87cd926d
--- /dev/null
+++ b/src/app/core/services/auth.service.ts
@@ -0,0 +1,131 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Injectable } from '@angular/core';
+import { ApiService } from './api.service';
+
+import { map } from 'rxjs/operators';
+import { JwtService } from './jwt.service';
+import { BehaviorSubject, Observable } from 'rxjs';
+import { User } from '../models/user';
+
+@Injectable( {
+ providedIn: 'root'
+} )
+export class AuthService {
+
+ private currentUserSubject: BehaviorSubject;
+ public currentUser: Observable;
+
+ constructor ( private api: ApiService, private jwt: JwtService ) {
+
+ this.currentUserSubject = new BehaviorSubject( JSON.parse( localStorage.getItem( 'currentUser' ) ) );
+ this.currentUser = this.currentUserSubject.asObservable();
+
+ }
+
+ public get email (): string {
+
+ if ( window.localStorage.getItem( 'email' ) ) {
+ return window.localStorage.getItem( 'email' );
+ }
+
+ return null;
+ }
+
+ public refresh () {
+
+ return this.api.post( '/auth/refresh' ).pipe( map( response => {
+
+ if ( response && response.token ) {
+
+ this.jwt.saveToken( response.token );
+
+ window.localStorage.setItem( 'currentUser', JSON.stringify( response ) );
+
+ this.currentUserSubject.next( response );
+
+ }
+
+ return response;
+
+ } ) );
+
+ }
+
+ public logout () {
+
+ window.localStorage.clear();
+
+ this.jwt.destroyToken();
+
+ }
+
+ public login ( email: string, password: string ) {
+
+ return this.api.post( '/auth/login', { email, password } )
+
+ .pipe( map( response => {
+
+ // login successful if there's a jwt token in the response
+ if ( response && response.token ) {
+
+ this.jwt.saveToken( response.token );
+
+ window.localStorage.setItem( 'currentUser', JSON.stringify( response ) );
+ window.localStorage.setItem( 'email', email );
+
+ this.currentUserSubject.next( response );
+
+ }
+
+ return response;
+
+ } ) );
+
+ }
+
+ public register (
+ name: string,
+ email: string,
+ password: string,
+ confirmPassword: string,
+ agreed: boolean
+ ) {
+
+ return this.api.post( '/auth/register', {
+ name,
+ email,
+ password,
+ password_confirmation: confirmPassword,
+ agreed
+ } )
+
+ .pipe( map( response => {
+
+ // login successful if there's a jwt token in the response
+ if ( response && response.token ) {
+
+ this.jwt.saveToken( response.token );
+
+ window.localStorage.setItem( 'currentUser', JSON.stringify( response ) );
+ window.localStorage.setItem( 'email', email );
+
+ this.currentUserSubject.next( response );
+
+ }
+
+ return response;
+
+ } ) );
+
+ }
+
+ public forgotPassword ( email: string ) {
+
+ return this.api.post( '/auth/forgot-password', { email } );
+
+ }
+
+}
diff --git a/src/app/core/services/drawing.service.ts b/src/app/core/services/drawing.service.ts
new file mode 100644
index 00000000..79bba3fc
--- /dev/null
+++ b/src/app/core/services/drawing.service.ts
@@ -0,0 +1,231 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Injectable } from '@angular/core';
+import { ThreeService } from 'app/modules/three-js/three.service';
+import * as THREE from 'three';
+import { Vector2 } from 'three';
+import { COLOR } from 'app/shared/utils/colors.service';
+
+@Injectable( {
+ providedIn: 'root'
+} )
+export class DrawingService {
+
+ ORANGE_COLOR = 0xFF4500;
+
+ DEFAULT_BOX_COLOR = 0xff0000;
+ HIGHTLIGHT_BOX_COLOR = 0x00ff00;
+ CROSSHAIR_COLOR = 0x00ff00;
+ DASHED_LINE_COLOR = 0xff00ff;
+
+ lastBoundingBox: THREE.Object3D;
+ lastSolidBox: THREE.Mesh;
+ boxMaterial = new THREE.MeshBasicMaterial( { color: this.ORANGE_COLOR, opacity: 0.2, transparent: true } );
+ lineDashedMaterial = new THREE.LineDashedMaterial( { color: this.DASHED_LINE_COLOR, linewidth: 10, dashSize: 3, gapSize: 3 } );
+
+ boxes: Map = new Map();
+
+ lastLine: THREE.Line;
+
+ constructor ( private engineService: ThreeService ) {
+
+ // this.editor.undoAddedAnnotation.subscribe( e => this.engineService.remove( this.lastBoundingBox, true ) );
+
+ }
+
+ getLastSolidBox (): THREE.Mesh {
+ return this.lastSolidBox;
+ }
+
+ getLastBoundingBox (): THREE.Object3D {
+ return this.lastBoundingBox;
+ }
+
+ public drawSolidBox ( start: THREE.Vector3, end: THREE.Vector3, color: string = null ): THREE.Object3D {
+
+ this.engineService.remove( this.lastBoundingBox, true );
+
+ var box = this.getDimensions( start, end );
+
+ var boxGeometry = new THREE.PlaneBufferGeometry();
+
+ var boxMaterial = (new THREE.MeshBasicMaterial()).copy( this.boxMaterial );
+
+ boxMaterial.color.set( color ? color : '#000000' );
+
+ this.lastSolidBox = new THREE.Mesh( boxGeometry, boxMaterial );
+
+ this.lastSolidBox.geometry.scale( box.width, box.height, 1 );
+
+ if ( end.y < start.y ) box.height *= -1;
+ if ( end.x < start.x ) box.width *= -1;
+
+ this.lastSolidBox.position.set( start.x + (box.width / 2), start.y + (box.height / 2), 0.1 );
+
+ this.engineService.add( this.lastSolidBox, true );
+
+ this.addBoxInfo( this.lastSolidBox, start, end, box );
+
+ // this.updateControlPoints( start, end, this.lastBox );
+
+ this.drawBoundingBox( start, end );
+
+ this.drawDeletButton( start.x, start.y, this.lastSolidBox );
+
+ return this.lastSolidBox;
+ }
+
+ public drawBoundingBox ( start: THREE.Vector3, end: THREE.Vector3, color: string = null ): THREE.Object3D {
+
+ var box = this.getDimensions( start, end );
+
+ var geometry = new THREE.PlaneBufferGeometry( box.width, box.height );
+ var edges = new THREE.EdgesGeometry( geometry );
+ var material = new THREE.LineBasicMaterial( { color: color ? color : 0xff00ff } );
+
+ this.lastBoundingBox = new THREE.LineSegments( edges, material );
+
+ // this.lastBoundingBox.computeLineDistances();
+
+ geometry.scale( box.width, box.height, 1 );
+
+ if ( end.y < start.y ) box.height *= -1;
+ if ( end.x < start.x ) box.width *= -1;
+
+ // this.lastBoundingBox.position.set( start.x + ( box.width / 2 ), start.y + ( box.height / 2 ), 0.1 );
+
+ this.lastSolidBox.add( this.lastBoundingBox );
+
+ // this.engineService.add( this.lastBoundingBox, true );
+
+ this.addBoxInfo( this.lastBoundingBox, start, end, box );
+
+ return this.lastBoundingBox;
+ }
+
+ private addBoxInfo ( object: THREE.Object3D, start: THREE.Vector3, end: THREE.Vector3, box: any ): any {
+
+ object.userData.is_annotation = true;
+ object.userData.type = 'box';
+
+ object.userData.width = box.width;
+ object.userData.height = box.height;
+
+ object.userData.startX = start.x;
+ object.userData.startY = start.y;
+
+ object.userData.endX = end.x;
+ object.userData.endY = end.y;
+ }
+
+ public drawLine ( vertices: Vector2[] ) {
+
+ this.engineService.remove( this.lastLine, true );
+
+ let material = new THREE.LineBasicMaterial( { color: 0xFF0000 } );
+
+ var geometry = new THREE.Geometry();
+
+ for ( let i = 0; i < vertices.length; i++ ) {
+
+ const item = vertices[i];
+
+ const position = new THREE.Vector3( item.x, item.y, 0.2 );
+
+ geometry.vertices.push( position );
+ }
+
+ this.lastLine = new THREE.Line( geometry, material );
+
+ for ( let i = 0; i < vertices.length; i++ ) {
+
+ const item = vertices[i];
+
+ const position = new THREE.Vector3( item.x, item.y, 0.2 );
+
+ this.drawSpritePoint( position, this.lastLine );
+ }
+
+ this.engineService.add( this.lastLine, true );
+ }
+
+ private lastButtonSprite: THREE.Sprite;
+
+ public drawDeletButton ( x, y, parent ) {
+
+ // this.engineService.remove( this.lastButtonSprite, true );
+
+ // var button = new DeleteButton( x, y, parent );
+
+ // this.engineService.add( button.object, true );
+
+ // this.lastButtonSprite = button.object;
+ }
+
+ drawSpritePoint ( position: any, parent: any ) {
+
+ var dotGeometry = new THREE.Geometry();
+ dotGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ) );
+
+ var dotMaterial = new THREE.PointsMaterial( {
+ size: 15,
+ sizeAttenuation: true,
+ map: this.diskSprite,
+ alphaTest: 0.5,
+ transparent: true,
+ color: COLOR.MAGENTA
+ } );
+
+ // dotMaterial.color.setHSL( 1.0, 0.3, 0.7 );
+
+ var object = new THREE.Points( dotGeometry, dotMaterial );
+
+ object.position.set( position.x, position.y, position.z );
+
+ // this.engineService.add( object );
+ this.lastLine.add( object );
+
+ // extra data
+ object.userData.is_button = true;
+ object.userData.is_control_point = true;
+
+ return object;
+
+ }
+
+ drawPoint ( position: any, parent: any ): any {
+
+ var geometry = new THREE.CircleBufferGeometry( 5, 32 );
+ var object = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { color: 0x00FF00 } ) );
+
+ object.position.set( position.x, position.y, position.z + 0.2 );
+
+ // this.engineService.add( object );
+ this.lastLine.add( object );
+
+ // extra data
+ object.userData.is_button = true;
+ object.userData.is_control_point = true;
+
+ return object;
+ }
+
+ private diskSprite = new THREE.TextureLoader().load( 'assets/disc.png' );
+
+ private getDimensions ( p1, p2 ): any {
+
+ let width = Math.abs( p1.x - p2.x );
+ let height = Math.abs( p1.y - p2.y );
+
+ width = Math.max( width, 10 );
+ height = Math.max( height, 10 );
+
+ return {
+ width: width,
+ height: height
+ };
+ }
+
+}
diff --git a/src/app/core/services/editor-events.ts b/src/app/core/services/editor-events.ts
new file mode 100644
index 00000000..8974168f
--- /dev/null
+++ b/src/app/core/services/editor-events.ts
@@ -0,0 +1,16 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { EventEmitter, Output } from '@angular/core';
+
+export class EditorEvents {
+
+ @Output() static onZoomIn = new EventEmitter();
+ @Output() static onZoomOut = new EventEmitter();
+ @Output() static onZoomReset = new EventEmitter();
+
+ @Output() static sceneRendered = new EventEmitter();
+ @Output() static sceneCreated = new EventEmitter();
+
+}
diff --git a/src/app/core/services/file-api.service.ts b/src/app/core/services/file-api.service.ts
new file mode 100644
index 00000000..307058c9
--- /dev/null
+++ b/src/app/core/services/file-api.service.ts
@@ -0,0 +1,41 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Injectable } from '@angular/core';
+import { ApiService } from './api.service';
+import { IFile } from '../models/file';
+
+@Injectable( {
+ providedIn: 'root'
+} )
+export class FileApiService {
+
+ constructor ( private api: ApiService ) { }
+
+ // get file
+ getFile ( name: string, type: string ) {
+
+ return this.api.get( `/files?type=${ type }&name=${ name }` );
+
+ }
+
+ // get list of files
+ getFileList ( type: string ) {
+
+ return this.api.get( `/files/list?type=${ type }` );
+
+ }
+
+ save ( file: IFile ) {
+
+ return this.api.put( '/files', { file } );
+
+ }
+
+ ping () {
+
+ return this.api.get( `/ping` );
+
+ }
+}
diff --git a/src/app/core/services/input.service.ts b/src/app/core/services/input.service.ts
new file mode 100644
index 00000000..d0ef24d1
--- /dev/null
+++ b/src/app/core/services/input.service.ts
@@ -0,0 +1,21 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Injectable, Output, EventEmitter } from '@angular/core';
+
+@Injectable( {
+ providedIn: 'root'
+} )
+export class InputService {
+
+ public isShiftKeyDown: boolean;
+
+ @Output() shiftKeyDown = new EventEmitter();
+
+ constructor () {
+
+ this.shiftKeyDown.subscribe( value => this.isShiftKeyDown = value );
+
+ }
+}
diff --git a/src/app/core/services/jwt.service.ts b/src/app/core/services/jwt.service.ts
new file mode 100644
index 00000000..d4b32867
--- /dev/null
+++ b/src/app/core/services/jwt.service.ts
@@ -0,0 +1,50 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Injectable } from '@angular/core';
+
+import { JwtHelperService } from '@auth0/angular-jwt';
+
+@Injectable()
+export class JwtService {
+
+ private KEY = 'jwtToken';
+
+ private helper: JwtHelperService;
+
+ constructor () {
+ this.helper = new JwtHelperService();
+ }
+
+ get token () { return this.getToken(); }
+
+ get decodedToken () { return this.helper.decodeToken( this.token ); }
+
+ get expirationDate () { return this.helper.getTokenExpirationDate( this.token ); }
+
+ getToken (): string {
+ return window.localStorage[ this.KEY ];
+ }
+
+ saveToken ( token: string ) {
+ window.localStorage[ this.KEY ] = token;
+ }
+
+ destroyToken () {
+ window.localStorage.removeItem( 'jwtToken' );
+ }
+
+ hasToken () {
+ return (
+ this.token != null &&
+ this.token !== undefined &&
+ this.token !== '' &&
+ this.token !== 'null'
+ );
+ }
+
+ isTokenExpired (): boolean {
+ return this.helper.isTokenExpired( this.token );
+ }
+}
diff --git a/src/app/core/services/open-drive-api.service.ts b/src/app/core/services/open-drive-api.service.ts
new file mode 100644
index 00000000..119cde5f
--- /dev/null
+++ b/src/app/core/services/open-drive-api.service.ts
@@ -0,0 +1,25 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Injectable } from '@angular/core';
+import { IFile } from '../models/file';
+import { Observable } from 'rxjs';
+import { FileApiService } from './file-api.service';
+
+@Injectable( {
+ providedIn: 'root'
+} )
+export class OpenDriveApiService {
+
+ constructor ( private files: FileApiService ) {
+ }
+
+ getOpenDrive ( name: string ): Observable {
+ return this.files.getFile( name, 'tv-map' );
+ }
+
+ getAll (): Observable {
+ return this.files.getFileList( 'tv-map' );
+ }
+}
diff --git a/src/app/core/services/open-scenario-api.service.ts b/src/app/core/services/open-scenario-api.service.ts
new file mode 100644
index 00000000..84aebfdc
--- /dev/null
+++ b/src/app/core/services/open-scenario-api.service.ts
@@ -0,0 +1,29 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Injectable } from '@angular/core';
+import { IFile } from '../models/file';
+import { Observable } from 'rxjs';
+import { FileApiService } from './file-api.service';
+
+@Injectable( {
+ providedIn: 'root'
+} )
+export class OpenScenarioApiService {
+
+ constructor ( private files: FileApiService ) {
+ }
+
+ getOpenScenario ( name: string ): Observable {
+
+ return this.files.getFile( name, 'open-scenario' );
+
+ }
+
+ saveOpenScenario ( file: IFile ): Observable {
+
+ return this.files.save( file );
+
+ }
+}
diff --git a/src/app/core/services/picking-helper.service.ts b/src/app/core/services/picking-helper.service.ts
new file mode 100644
index 00000000..6e84070c
--- /dev/null
+++ b/src/app/core/services/picking-helper.service.ts
@@ -0,0 +1,88 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { MouseButton, PointerEventData } from 'app/events/pointer-event-data';
+import { TvLane } from 'app/modules/tv-map/models/tv-lane';
+import { ObjectTypes } from 'app/modules/tv-map/models/tv-common';
+import { Object3D, Vector3 } from 'three';
+import { BaseControlPoint } from 'app/modules/three-js/objects/control-point';
+
+export class PickingHelper {
+
+ public static checkControlPointInteraction ( event: PointerEventData, tag: string, maxDistance = 0.5 ): BaseControlPoint {
+
+ let hasInteracted = false;
+
+ let currentMin = Number.MAX_VALUE;
+ let controlPoint: BaseControlPoint = null;
+
+ for ( let i = 0; i < event.intersections.length; i++ ) {
+
+ const intersection = event.intersections[ i ];
+
+ if ( event.button === MouseButton.LEFT && intersection.object && intersection.object[ 'tag' ] == tag ) {
+
+ hasInteracted = true;
+
+ if ( intersection.distanceToRay < currentMin && intersection.distanceToRay < maxDistance ) {
+
+ currentMin = intersection.distanceToRay;
+
+ controlPoint = intersection.object as BaseControlPoint;
+
+ }
+ }
+ }
+
+ return controlPoint;
+ }
+
+ public static findNearest ( position: Vector3, objects: T[], maxDistance = 0.5 ): T {
+
+ let nearestDistance = Number.MAX_VALUE;
+ let nearestObject: T = null;
+
+ for ( let i = 0; i < objects.length; i++ ) {
+
+ const object = objects[ i ];
+
+ const distance = position.distanceTo( object.position );
+
+ if ( distance < nearestDistance && distance < maxDistance ) {
+
+ nearestDistance = distance;
+
+ nearestObject = object;
+
+ }
+ }
+
+ return nearestObject;
+ }
+
+ public static checkLaneObjectInteraction ( event: PointerEventData, tag?: string ): TvLane {
+
+ const laneTag = tag || ObjectTypes.LANE;
+
+ let lane = null;
+
+ for ( let i = 0; i < event.intersections.length; i++ ) {
+
+ const intersection = event.intersections[ i ];
+
+ if ( intersection.object && intersection.object[ 'tag' ] === laneTag ) {
+
+ if ( intersection.object.userData.lane ) {
+
+ lane = intersection.object.userData.lane as TvLane;
+
+ break;
+ }
+ }
+ }
+
+ return lane;
+ }
+
+}
\ No newline at end of file
diff --git a/src/app/core/services/scene.service.ts b/src/app/core/services/scene.service.ts
new file mode 100644
index 00000000..43592142
--- /dev/null
+++ b/src/app/core/services/scene.service.ts
@@ -0,0 +1,185 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Injectable } from '@angular/core';
+import * as THREE from 'three';
+import { Mesh, Object3D } from 'three';
+import { GameObject } from '../game-object';
+
+@Injectable( {
+ providedIn: 'root'
+} )
+export class SceneService {
+
+ public static scene: THREE.Scene = new THREE.Scene;
+ public static objects: Object3D[] = [];
+ public static sceneHelpers: Object3D[] = [];
+
+ constructor () {
+
+
+ }
+
+ public static get children () {
+ return this.scene.children;
+ }
+
+ static addHelper ( object: Object3D ): void {
+
+ this.scene.add( object );
+
+ this.sceneHelpers.push( object );
+ }
+
+ static removeHelper ( object: Object3D ): void {
+
+ if ( object == null ) return;
+
+ this.scene.remove( object );
+
+ for ( let i = 0; i < this.sceneHelpers.length; i++ ) {
+
+ const element = this.sceneHelpers[ i ];
+
+ if ( element.id === object.id ) {
+
+ this.sceneHelpers.splice( i, 1 );
+
+ break;
+ }
+ }
+ }
+
+ static add ( object: Object3D, raycasting: boolean = true ): void {
+
+ this.scene.add( object );
+
+ if ( raycasting ) this.objects.push( object );
+
+ }
+
+ static deselect ( object: Object3D ): void {
+
+ throw new Error( 'Not implemented' );
+
+ }
+
+ static focus ( object: Object3D ): void {
+
+ throw new Error( 'Not implemented' );
+
+ }
+
+ static remove ( object: Object3D, raycasting: boolean = true ): void {
+
+ if ( object == null ) return;
+
+ this.scene.remove( object );
+
+ if ( raycasting ) {
+
+ for ( let i = 0; i < this.objects.length; i++ ) {
+
+ const element = this.objects[ i ];
+
+ if ( element.id === object.id ) {
+
+ this.objects.splice( i, 1 );
+
+ break;
+
+ }
+ }
+ }
+ }
+
+ static reset (): void {
+
+ while ( this.scene.children.length > 0 ) {
+
+ let obj = this.scene.children[ 0 ];
+
+ this.scene.remove( obj );
+
+ this.disposeHierarchy( obj, SceneService.disposeNode );
+
+ }
+
+ this.objects = [];
+ }
+
+ static select ( object: Object3D ): void {
+
+ throw new Error( 'Not implemented' );
+
+ }
+
+ public static disposeHierarchy ( node, callback ) {
+
+ for ( let i = node.children.length - 1; i >= 0; i-- ) {
+
+ const child = node.children[ i ];
+
+ this.disposeHierarchy( child, callback );
+
+ callback( child );
+
+ }
+ }
+
+ static removeWithChildren ( object: Object3D, raycasting: boolean = false ) {
+
+ while ( object.children.length > 0 ) {
+
+ this.disposeNode( object.children[ 0 ] );
+
+ }
+
+ this.remove( object, raycasting );
+ }
+
+ private static disposeNode ( node ) {
+
+ if ( node instanceof Mesh ) {
+
+ node.parent.remove( node );
+ node.parent = undefined;
+
+ if ( node.geometry ) {
+
+ node.geometry.dispose();
+
+ }
+
+ let material: any = node.material;
+
+ if ( material ) {
+
+ if ( material.map ) material.map.dispose();
+ if ( material.lightMap ) material.lightMap.dispose();
+ if ( material.bumpMap ) material.bumpMap.dispose();
+ if ( material.normalMap ) material.normalMap.dispose();
+ if ( material.specularMap ) material.specularMap.dispose();
+ if ( material.envMap ) material.envMap.dispose();
+
+ material.dispose();
+ }
+
+ } else if ( node instanceof Object3D ) {
+
+ node.parent.remove( node );
+ node.parent = undefined;
+
+ } else if ( node instanceof GameObject ) {
+
+ node.parent.remove( node );
+ node.parent = undefined;
+
+ } else {
+
+ throw new Error( 'unknown type' );
+
+ }
+ }
+}
diff --git a/src/app/core/shapes/ParametricPolynomial.ts b/src/app/core/shapes/ParametricPolynomial.ts
new file mode 100644
index 00000000..b641e4e8
--- /dev/null
+++ b/src/app/core/shapes/ParametricPolynomial.ts
@@ -0,0 +1,44 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { ARC_SEGMENTS, PARACUBICFACTOR } from "./spline-config";
+import { Vector3, BufferGeometry, BufferAttribute, Line, LineBasicMaterial } from 'three';
+class ParametricPolynomial {
+ private curveType;
+ private mesh;
+ constructor ( private points ) {
+ const geometry = new BufferGeometry();
+ geometry.addAttribute( 'position', new BufferAttribute( new Float32Array( ARC_SEGMENTS * 3 ), 3 ) );
+ this.curveType = 'cubic';
+ this.mesh = new Line( geometry, new LineBasicMaterial( { color: 0x0000ff, opacity: 0.85 } ) );
+ this.mesh.castShadow = true;
+ this.mesh.renderOrder = 1;
+ this.mesh.frustumCulled = false;
+ }
+ update () {
+ const position = this.mesh.geometry.attributes.position;
+ const point = new Vector3();
+ for ( let i = 0; i < ARC_SEGMENTS; i++ ) {
+ const t = i / ( ARC_SEGMENTS - 1 );
+ this.getPoint( t, point );
+ position.setXYZ( i, point.x, point.y, point.z );
+ }
+ position.needsUpdate = true;
+ }
+ getPoint ( t, rettarget ) {
+ const retpoint = rettarget || new Vector3();
+ const p1 = this.points[ 0 ];
+ const p2 = this.points[ 3 ];
+ const t1 = new Vector3().subVectors( this.points[ 1 ], p1 ).multiplyScalar( PARACUBICFACTOR );
+ const t2 = new Vector3().subVectors( p2, this.points[ 2 ] ).multiplyScalar( PARACUBICFACTOR );
+ // tslint:disable-next-line: one-variable-per-declaration
+ const s = t, s2 = s * s, s3 = s2 * s;
+ const h1 = new Vector3().setScalar( 2 * s3 - 3 * s2 + 1 ).multiply( p1 );
+ const h2 = new Vector3().setScalar( -2 * s3 + 3 * s2 ).multiply( p2 );
+ const h3 = new Vector3().setScalar( s3 - 2 * s2 + s ).multiply( t1 );
+ const h4 = new Vector3().setScalar( s3 - s2 ).multiply( t2 );
+ retpoint.copy( h1 ).add( h2 ).add( h3 ).add( h4 );
+ return retpoint;
+ }
+}
diff --git a/src/app/core/shapes/PolyLine.ts b/src/app/core/shapes/PolyLine.ts
new file mode 100644
index 00000000..a48074be
--- /dev/null
+++ b/src/app/core/shapes/PolyLine.ts
@@ -0,0 +1,57 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Vector3, BufferGeometry, BufferAttribute, Line, LineBasicMaterial } from 'three';
+import { COLOR } from 'app/shared/utils/colors.service';
+import { MAX_CTRL_POINTS } from "./spline-config";
+import { BaseControlPoint } from 'app/modules/three-js/objects/control-point';
+
+export class PolyLine {
+
+ curveType;
+
+ mesh;
+
+ constructor ( public points: BaseControlPoint[] ) {
+
+ const geometry = new BufferGeometry();
+
+ geometry.addAttribute( 'position', new BufferAttribute( new Float32Array( MAX_CTRL_POINTS * 3 ), 3 ) );
+
+ this.curveType = 'polyline';
+
+ this.mesh = new Line( geometry, new LineBasicMaterial( { color: COLOR.WHITE, opacity: 0.35, linewidth: 2 } ) );
+
+ this.mesh.castShadow = true;
+
+ this.mesh.renderOrder = 1;
+
+ this.mesh.frustumCulled = false;
+ }
+
+ addPoint ( point: BaseControlPoint ) {
+
+ this.points.push( point );
+
+ }
+
+ // Should be called once after curve control points get updated
+ update () {
+
+ if ( this.points.length <= 1 ) return;
+
+ const position = this.mesh.geometry.attributes.position;
+
+ // fill the whole buffer
+ for ( let i = 0; i < MAX_CTRL_POINTS; i++ ) {
+
+ const point = i >= this.points.length ? this.points[ this.points.length - 1 ] : this.points[ i ];
+
+ position.setXYZ( i, point.position.x, point.position.y, point.position.z );
+
+ }
+
+ position.needsUpdate = true;
+ }
+}
diff --git a/src/app/core/shapes/Spiral.ts b/src/app/core/shapes/Spiral.ts
new file mode 100644
index 00000000..9645baff
--- /dev/null
+++ b/src/app/core/shapes/Spiral.ts
@@ -0,0 +1,33 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { ARC_SEGMENTS } from "./spline-config";
+import { BufferGeometry, BufferAttribute, Line, LineBasicMaterial } from 'three';
+class Spiral {
+ curveType;
+ mesh;
+ constructor ( private points ) {
+ const geometry = new BufferGeometry();
+ geometry.addAttribute( 'position', new BufferAttribute( new Float32Array( ARC_SEGMENTS * 3 ), 3 ) );
+ this.curveType = 'spiral';
+ this.mesh = new Line( geometry, new LineBasicMaterial( { color: 0x00ff00, opacity: 0.35 } ) );
+ this.mesh.castShadow = true;
+ this.mesh.renderOrder = 1;
+ this.mesh.frustumCulled = false;
+ }
+ update (): void {
+ // // determine params
+ // var sd = SPIRAL.vec2Angle( this.points[ 1 ].x - this.points[ 0 ].x, this.points[ 1 ].z - this.points[ 0 ].z );
+ // var ed = SPIRAL.vec2Angle( this.points[ 3 ].x - this.points[ 2 ].x, this.points[ 3 ].z - this.points[ 2 ].z );
+ // var k, dk, L, iter;
+ // [ k, dk, L, iter ] = SPIRAL.buildClothoid( this.points[ 0 ].x * 100, this.points[ 0 ].z * 100, sd, this.points[ 3 ].x * 100, this.points[ 3 ].z * 100, ed );
+ // //console.log(k,dk,L);
+ // var spiralarc = SPIRAL.clothoid_1( this.points[ 0 ].x * 100, this.points[ 0 ].z * 100, this.points[ 0 ].y, sd, k, k + dk * L, L, this.points[ 3 ].y, ARC_SEGMENTS - 1 )
+ // var position = this.mesh.geometry.attributes.position;
+ // for ( var i = 0; i < ARC_SEGMENTS; i++ ) {
+ // position.setXYZ( i, spiralarc[ i ][ 0 ] / 100, spiralarc[ i ][ 2 ], spiralarc[ i ][ 1 ] / 100 );
+ // }
+ // position.needsUpdate = true;
+ }
+}
diff --git a/src/app/core/shapes/SplineData.ts b/src/app/core/shapes/SplineData.ts
new file mode 100644
index 00000000..a41a547f
--- /dev/null
+++ b/src/app/core/shapes/SplineData.ts
@@ -0,0 +1,104 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Vector2, Vector4 } from 'three';
+
+class SplineData {
+
+ public xb: Vector4;
+
+ public yb: Vector4;
+
+ constructor ( p0: Vector2, p1: Vector2, p2: Vector2, p3: Vector2 ) {
+
+ this.xb = new Vector4( p0.x, p1.x, p2.x, p3.x );
+
+ this.yb = new Vector4( p0.y, p1.y, p2.y, p3.y );
+
+ }
+
+}
+
+// Optimised for t=0.5
+function Split ( spline: Vector4 ) {
+
+ const q0 = ( spline.x + spline.y ) * 0.5; // x + y / 2
+
+ const q1 = ( spline.y + spline.z ) * 0.5; // y + z / 2
+
+ const q2 = ( spline.z + spline.w ) * 0.5; // z + w / 2
+
+ const r0 = ( q0 + q1 ) * 0.5; // x + 2y + z / 4
+
+ const r1 = ( q1 + q2 ) * 0.5; // y + 2z + w / 4
+
+ const s0 = ( r0 + r1 ) * 0.5; // q0 + 2q1 + q2 / 4 = x+y + 2(y+z) + z+w / 8 = x + 3y + 3z + w
+
+ const sx = spline.x; // support aliasing
+
+ const sw = spline.w;
+
+ return [ new Vector4( sx, q0, r0, s0 ), new Vector4( s0, r1, q2, sw ) ];
+
+}
+
+export function HermiteSpline ( p0: Vector2, p1: Vector2, v0: Vector2, v1: Vector2 ): SplineData {
+
+ const pb1 = new Vector2().copy( v0 ).multiplyScalar( 1.0 / 3.0 ).add( p0 );
+
+ const pb2 = new Vector2().copy( v1 ).multiplyScalar( -1.0 / 3.0 ).add( p1 );
+
+ return new SplineData( p0, pb1, pb2, p1 );
+
+}
+
+function sqr ( x ) { return x * x; }
+
+function LengthEstimate ( s: SplineData ) {
+
+ // Our convex hull is p0, p1, p2, p3, so p0_p3 is our minimum possible length, and p0_p1 + p1_p2 + p2_p3 our maximum.
+
+ const d03 = sqr( s.xb.x - s.xb.w ) + sqr( s.yb.x - s.yb.w );
+
+ const d01 = sqr( s.xb.x - s.xb.y ) + sqr( s.yb.x - s.yb.y );
+
+ const d12 = sqr( s.xb.y - s.xb.z ) + sqr( s.yb.y - s.yb.z );
+
+ const d23 = sqr( s.xb.z - s.xb.w ) + sqr( s.yb.z - s.yb.w );
+
+ let minLength = Math.sqrt( d03 );
+
+ let maxLength = Math.sqrt( d01 ) + Math.sqrt( d12 ) + Math.sqrt( d23 );
+
+ minLength *= 0.5;
+
+ maxLength *= 0.5;
+
+ return [ minLength + maxLength, maxLength - minLength ];
+
+}
+
+export function Length ( s: SplineData, maxError /*float*/ ) {
+
+ let length, error;
+
+ [ length, error ] = LengthEstimate( s );
+
+ if ( error > maxError ) {
+
+ const s0 = new SplineData( new Vector2(), new Vector2(), new Vector2(), new Vector2() );
+
+ const s1 = new SplineData( new Vector2(), new Vector2(), new Vector2(), new Vector2() );
+
+ [ s0.xb, s1.xb ] = Split( s.xb );
+
+ [ s0.yb, s1.yb ] = Split( s.yb );
+
+ return Length( s0, maxError ) + Length( s1, maxError );
+
+ }
+
+ return length;
+
+}
\ No newline at end of file
diff --git a/src/app/core/shapes/TangentLine.ts b/src/app/core/shapes/TangentLine.ts
new file mode 100644
index 00000000..e693634f
--- /dev/null
+++ b/src/app/core/shapes/TangentLine.ts
@@ -0,0 +1,109 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Vector3, BufferGeometry, BufferAttribute, LineSegments, LineBasicMaterial } from 'three';
+import { COLOR } from 'app/shared/utils/colors.service';
+import { MAX_CTRL_POINTS, CURVE_Y } from "./spline-config";
+
+export class TangentLine {
+
+ public radiuses: any[];
+
+ public mesh: LineSegments;
+
+ private hdgs: any[] = [];
+
+ private geometry: BufferGeometry;
+
+ private curveType: string = 'tangent';
+
+ constructor ( public points: Vector3[] ) {
+
+ this.geometry = new BufferGeometry();
+
+ this.geometry.addAttribute( 'position', new BufferAttribute( new Float32Array( MAX_CTRL_POINTS * 2 * 3 ), 3 ) );
+
+ this.mesh = new LineSegments( this.geometry, new LineBasicMaterial( { color: COLOR.DARKBLUE, opacity: 0.35 } ) );
+
+ this.mesh.castShadow = true;
+
+ this.mesh.renderOrder = 3;
+
+ this.mesh.frustumCulled = false;
+
+ }
+
+ update ( hdgs: any[], points?: any[] ) {
+
+ this.hdgs = hdgs;
+
+ const position = this.geometry.attributes.position as BufferAttribute;
+
+ const pos = new Vector3();
+
+ for ( let i = 0; i < points.length; i++ ) {
+
+ pos.set( Math.cos( this.hdgs[ i ][ 0 ] ), Math.sin( this.hdgs[ i ][ 0 ] ), CURVE_Y )
+
+ .multiplyScalar( this.hdgs[ i ][ 1 ] )
+
+ .add( points[ i ] );
+
+ position.setXYZ( i * 2 + 0, pos.x, pos.y, pos.z );
+
+ pos.set( Math.cos( this.hdgs[ i ][ 0 ] ), Math.sin( this.hdgs[ i ][ 0 ] ), CURVE_Y )
+
+ .multiplyScalar( -this.hdgs[ i ][ 2 ] )
+
+ .add( points[ i ] );
+
+ position.setXYZ( i * 2 + 1, pos.x, pos.y, pos.z );
+
+ }
+
+ for ( let i = points.length; i < MAX_CTRL_POINTS; i++ ) {
+
+ position.setXYZ( i * 2 + 0, pos.x, pos.y, pos.z );
+
+ position.setXYZ( i * 2 + 1, pos.x, pos.y, pos.z );
+
+ }
+
+ position.needsUpdate = true;
+
+ }
+
+ updateOneSegment ( idx, point ) {
+
+ const position = this.geometry.attributes.position as BufferAttribute;
+
+ const pos1 = new Vector3();
+
+ const pos2 = new Vector3();
+
+ pos1.set( Math.cos( this.hdgs[ idx ][ 0 ] ), Math.sin( this.hdgs[ idx ][ 0 ] ), CURVE_Y )
+
+ .multiplyScalar( this.hdgs[ idx ][ 1 ] )
+
+ .add( point );
+
+ position.setXYZ( idx * 2 + 0, pos1.x, pos1.y, pos1.z );
+
+ pos2.set( Math.cos( this.hdgs[ idx ][ 0 ] ), Math.sin( this.hdgs[ idx ][ 0 ] ), CURVE_Y )
+
+ .multiplyScalar( -this.hdgs[ idx ][ 2 ] )
+
+ .add( point );
+
+ position.setXYZ( idx * 2 + 1, pos2.x, pos2.y, pos2.z );
+
+ position.needsUpdate = true;
+
+ return [ pos1, pos2 ];
+
+ }
+
+}
+
+
diff --git a/src/app/core/shapes/abstract-spline.ts b/src/app/core/shapes/abstract-spline.ts
new file mode 100644
index 00000000..80350ac4
--- /dev/null
+++ b/src/app/core/shapes/abstract-spline.ts
@@ -0,0 +1,183 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import * as THREE from 'three';
+import { Vector2, Vector3 } from 'three';
+import { SceneService } from '../services/scene.service';
+
+import { TvAbstractRoadGeometry } from 'app/modules/tv-map/models/geometries/tv-abstract-road-geometry';
+import { EventEmitter } from '@angular/core';
+import { RoadControlPoint } from 'app/modules/three-js/objects/road-control-point';
+import { BaseControlPoint } from 'app/modules/three-js/objects/control-point';
+
+export abstract class AbstractSpline {
+
+ abstract type: string;
+
+ abstract init (): void;
+
+ abstract hide (): void;
+
+ abstract show (): void;
+
+ abstract update (): void;
+
+ abstract exportGeometries (): TvAbstractRoadGeometry[];
+
+ // tcboxgeometry = new THREE.BoxBufferGeometry( 0.7, 0.3, 0.7 );
+ protected controlPointAdded = new EventEmitter();
+ protected controlPointRemoved = new EventEmitter();
+
+ public controlPoints: BaseControlPoint[] = [];
+
+ protected meshAddedInScene: boolean;
+
+ constructor ( public closed = true, public tension = 0.5 ) {
+
+ this.init();
+
+ }
+
+ get scene () { return SceneService.scene; }
+
+ get controlPointPositions (): Vector3[] {
+ return this.controlPoints.map( point => point.position );
+ }
+
+ clear () {
+
+ throw new Error( "Method not implemented." );
+
+ }
+
+ addControlPoint ( cp: BaseControlPoint ) {
+
+ this.controlPoints.push( cp );
+
+ }
+
+ addControlPointAtNew ( position: Vector3 ): RoadControlPoint {
+
+ throw new Error( "method not implemented" );
+
+ }
+
+ getFirstPoint () {
+
+ return this.controlPoints[ 0 ];
+
+ }
+
+ getSecondPoint () {
+
+ try {
+
+ return this.controlPoints[ 1 ];
+
+ } catch ( error ) {
+
+ }
+
+ }
+
+ getLastPoint () {
+
+ return this.controlPoints[ this.controlPoints.length - 1 ];
+
+ }
+
+ getSecondLastPoint () {
+
+ try {
+
+ return this.controlPoints[ this.controlPoints.length - 2 ];
+
+ } catch ( error ) {
+
+ }
+
+ }
+
+ removeControlPoint ( cp: BaseControlPoint ) {
+
+ const index = this.controlPoints.findIndex( p => p.id === cp.id );
+
+ this.controlPoints.splice( index, 1 );
+ }
+
+ getArcParams ( p1: Vector2, p2: Vector2, dir1: Vector2, dir2: Vector2 ): number[] {
+
+ const distance = p1.distanceTo( p2 );
+
+ const normalisedDotProduct = new THREE.Vector2()
+ .copy( dir1 )
+ .normalize()
+ .dot( new THREE.Vector2().copy( dir2 ).normalize() );
+
+ const alpha = Math.acos( normalisedDotProduct );
+
+ const r = distance / 2 / Math.sin( alpha / 2 );
+
+ const length = r * alpha;
+
+ const ma = dir1.x, mb = dir1.y, mc = -mb, md = ma;
+
+ const det = 1 / ( ma * md - mb * mc );
+
+ const mia = det * md, mib = -mb * det, mic = -mc * det, mid = ma * det;
+
+ const p2proj = new THREE.Vector2().subVectors( p2, p1 );
+
+ p2proj.set( p2proj.x * mia + p2proj.y * mic, p2proj.x * mib + p2proj.y * mid );
+
+ return [ r, alpha, length, Math.sign( p2proj.y ) ];
+ }
+
+ updateControlPoint ( cp: BaseControlPoint, id: number, cpobjidx?: any ) {
+
+ cp[ 'tag' ] = "cp"; cp[ 'tagindex' ] = id;
+
+ cp.userData.is_button = true;
+ cp.userData.is_control_point = true;
+ cp.userData.is_selectable = true;
+
+ if ( cpobjidx == undefined )
+ this.controlPoints.push( cp );
+ else
+ this.controlPoints.splice( cpobjidx, 0, cp );
+ }
+
+ /**
+ *
+ * @deprecated dont use this make another internal for any sub class
+ * @param tag
+ * @param id
+ * @param cpobjidx
+ */
+ createControlPoint ( tag: "cp" | "tpf" | "tpb", id: number, cpobjidx?: any ): BaseControlPoint {
+
+ // let cptobj = new THREE.Mesh( this.tcboxgeometry, new THREE.MeshLambertMaterial( { color: Math.random() * 0xffffff } ) );
+ let controlPointObject = new RoadControlPoint( null, new Vector3(), tag, id, cpobjidx );
+
+ controlPointObject[ 'tag' ] = tag; controlPointObject[ 'tagindex' ] = id;
+
+ controlPointObject.userData.is_button = true;
+ controlPointObject.userData.is_control_point = true;
+ controlPointObject.userData.is_selectable = true;
+
+ this.scene.add( controlPointObject );
+
+ if ( cpobjidx == undefined )
+ this.controlPoints.push( controlPointObject );
+ else
+ this.controlPoints.splice( cpobjidx, 0, controlPointObject );
+
+ this.controlPointAdded.emit( controlPointObject );
+
+ return controlPointObject;
+ }
+
+}
+
+
diff --git a/src/app/core/shapes/arc-spline-curve.spec.ts b/src/app/core/shapes/arc-spline-curve.spec.ts
new file mode 100644
index 00000000..423f7301
--- /dev/null
+++ b/src/app/core/shapes/arc-spline-curve.spec.ts
@@ -0,0 +1,37 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TestBed } from '@angular/core/testing';
+import { Vector3 } from 'three';
+import { CustomSplineCurve } from './arc-spline-curve';
+
+
+describe( 'ArcSplineCurve test', () => {
+
+ let curve: CustomSplineCurve;
+
+ beforeEach( () => TestBed.configureTestingModule( {} ) );
+
+ beforeEach( () => {
+ curve = new CustomSplineCurve();
+ } );
+
+ it( 'should give correct angle', () => {
+
+ // tslint:disable-next-line: one-variable-per-declaration
+ let p1, p2, p3: Vector3;
+
+ p1 = new Vector3( 0, 0, 0 );
+ p2 = new Vector3( 10, 0, 0 );
+ p3 = new Vector3( 20, 10, 0 );
+
+ curve.addPoints( [ p1, p2, p3 ] );
+
+ const geomtries = curve.geometries;
+
+
+ } );
+
+
+} );
diff --git a/src/app/core/shapes/arc-spline-curve.ts b/src/app/core/shapes/arc-spline-curve.ts
new file mode 100644
index 00000000..cd11fa87
--- /dev/null
+++ b/src/app/core/shapes/arc-spline-curve.ts
@@ -0,0 +1,167 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Curve, Vector3 } from 'three';
+import { Maths } from 'app/utils/maths';
+import { TvLineGeometry } from 'app/modules/tv-map/models/geometries/tv-line-geometry';
+import { TvArcGeometry } from 'app/modules/tv-map/models/geometries/tv-arc-geometry';
+import { TvSide } from 'app/modules/tv-map/models/tv-common';
+import { TvPosTheta } from '../../modules/tv-map/models/tv-pos-theta';
+import { TvAbstractRoadGeometry } from 'app/modules/tv-map/models/geometries/tv-abstract-road-geometry';
+
+export class CustomSplineCurve {
+
+ private mGeometries: TvAbstractRoadGeometry[] = [];
+
+ constructor ( private points: Vector3[] = [] ) {
+ }
+
+ get geometries () {
+ return this.mGeometries;
+ }
+
+ addPoint ( p: Vector3 ) {
+
+ this.points.push( p );
+
+ this.compute();
+
+ }
+
+ addPoints ( points: Vector3[] ) {
+
+ points.forEach( point => {
+ this.points.push( point );
+ } );
+
+ this.compute();
+ }
+
+ compute () {
+
+ if ( this.points.length < 3 ) return;
+
+ const reversed = [ ...this.points ].reverse();
+
+ for ( let i = 0; i < reversed.length; i++ ) {
+
+ // break if we don't have 3 points remaining
+ if ( ( reversed.length - i ) < 3 ) break;
+
+ const p1 = reversed[ i + 2 ];
+ const p2 = reversed[ i + 1 ];
+ const p3 = reversed[ i ];
+
+ const x = p1.x;
+ const y = p1.y;
+
+ const firstSegment = p1.distanceTo( p2 );
+ const secondSegment = p2.distanceTo( p3 );
+
+ // hdg of first line segment
+ const hdg = Math.atan2( p2.y - p1.y, p2.x - p1.x );
+
+ // hdg of second line segment
+ const hdg2 = Math.atan2( p3.y - p2.y, p3.x - p2.x );
+
+ let arcStartingPosition: Vector3;
+
+ // line + arc
+ if ( firstSegment >= secondSegment ) {
+
+ const t = ( firstSegment - secondSegment ) / firstSegment;
+
+ const lineLength = firstSegment - secondSegment;
+
+ arcStartingPosition = Maths.linearInterpolationVector3( p1, p2, t );
+
+ const line = new TvLineGeometry( 0, x, y, hdg, lineLength );
+
+ const end = line.end;
+
+ const arc = this.createArcGeometry( line.s2, end.x, end.y, arcStartingPosition, hdg, p3, hdg2, p2 );
+
+ this.mGeometries.push( line );
+ this.mGeometries.push( arc );
+
+ // arc + line
+ } else if ( secondSegment > firstSegment ) {
+
+ const t = firstSegment / secondSegment;
+
+ const lineLength = secondSegment - firstSegment;
+
+ const lineStartingPosition = Maths.linearInterpolationVector3( p2, p3, t );
+
+ const arc = this.createArcGeometry( 0, x, y, p1, hdg, lineStartingPosition, hdg2, p2 );
+
+ const pos = new TvPosTheta();
+
+ arc.getCoords( arc.s2, pos );
+
+ const line = new TvLineGeometry( arc.s2, pos.x, pos.y, pos.hdg, lineLength );
+
+ this.mGeometries.push( arc );
+ this.mGeometries.push( line );
+ }
+ }
+ }
+
+ private createArcGeometry ( s: number, x: number, y: number, p1: Vector3, hdg: number, p3: Vector3, hdg2: number, p2: Vector3 ) {
+
+ const res = this.getRadius( p1, hdg, p3, hdg2 );
+
+ const radius = res.radius;
+ const center = res.center;
+
+ let curvature = 1 / radius;
+
+ const doCurvature = Maths.angle( center, p1, p3 );
+
+ const arcLength = doCurvature * radius;
+
+ // make the curvature negative for right side i.e. for clockwise
+ const side = Maths.direction( p2, p3, p1 );
+ if ( side === TvSide.RIGHT ) curvature *= -1;
+
+ return new TvArcGeometry( s, x, y, hdg, arcLength, curvature );
+ }
+
+ private getRadius ( A: Vector3, line1Hdg: number, C: Vector3, line2Hdg: number ) {
+
+ const B = new Vector3(
+ A.x + Math.cos( line1Hdg + Maths.M_PI_2 ) * 1,
+ A.y + Math.sin( line1Hdg + Maths.M_PI_2 ) * 1
+ );
+
+ const D = new Vector3(
+ C.x + Math.cos( line2Hdg + Maths.M_PI_2 ) * 1,
+ C.y + Math.sin( line2Hdg + Maths.M_PI_2 ) * 1
+ );
+
+ const center = Maths.lineLineIntersection( A, B, C, D );
+
+ const radius = A.distanceTo( center );
+
+ return {
+ radius,
+ center
+ };
+
+ }
+
+}
+
+
+export class BSplineCurve3 extends Curve {
+
+ constructor ( private points?: Vector3[], private closed?: boolean, private curveType?: string, private tension?: number ) {
+ super();
+ }
+
+ getPoint ( t: number ): Vector3 {
+
+ return new Vector3();
+ }
+}
\ No newline at end of file
diff --git a/src/app/core/shapes/auto-spline.spec.ts b/src/app/core/shapes/auto-spline.spec.ts
new file mode 100644
index 00000000..ef36a579
--- /dev/null
+++ b/src/app/core/shapes/auto-spline.spec.ts
@@ -0,0 +1,547 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+// describe( 'blah test', () => {
+
+// it( 'should run', () => {
+
+
+// // taking example values for a,b,c,d
+// //
+// const p0 = new Vector3( 0, 0, 0 ); // first point
+// const p1 = new Vector3( 100, 0, 0 ); // first tangent
+// const p2 = new Vector3( 0, 100, 0 ); // second tangent
+// const p3 = new Vector3( 100, 100, 0 ); // second point
+
+// // const t1 = Math.atan2( p1.y, p1.x );
+// // const t2 = Math.atan2( p2.y, p2.x );
+
+// const s1 = 0;
+// const s2 = 300;
+
+// const length = s2 - s1;
+
+// const pp0 = 3.5;
+// const pp1 = 15;
+// const pd0 = 0;
+// const pd1 = 0;
+
+// let a = pp0;
+// let b = pd0;
+// let c = ( -3 * pp0 ) + ( 3 * pp1 ) + ( -2 * pd0 ) + ( -1 * pd1 );
+// let d = ( 2 * pp0 ) + ( -2 * pp1 ) + ( 1 * pd0 ) + ( 1 * pd1 );
+
+// b /= length;
+// c /= length * length;
+// d /= length * length * length;
+
+// console.log( a, b, c, d );
+
+// // console.log( t1, t2 );
+
+// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// // const B = new Matrix4().set(
+// // 1, 0, 0, 0,
+// // 1, 1, 1, 1,
+// // 0, 1, 0, 0,
+// // 0, 1, 2, 3
+// // );
+
+// // const B_inverse = new Matrix4().getInverse( B );
+
+// // console.log( B );
+// // console.log( B_inverse );
+
+// // // const A_coeffs = new Matrix4().set(
+// // // 0, 0, 0, 0,
+// // // 0, 0, 0, 0,
+// // // 0.01, 0, 0, 0,
+// // // 0, 0, 0, 0
+// // // );
+
+// // const G_x = new Matrix4().set(
+// // 100, 0, 0, 0,
+// // 0, 0, 0, 0,
+// // 1, 0, 0, 0,
+// // 0, 0, 0, 0,
+// // );
+
+// // const A = B_inverse.multiply( G_x );
+
+// // console.log( "====================================================" );
+// // console.log( B );
+// // console.log( B_inverse );
+// // console.log( G_x );
+// // console.log( "========= coeffs ===================================" );
+// // console.log( A );
+
+// // return;
+
+// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// // const B = new Matrix4().set(
+// // 0, 0, 0, 1,
+// // 0.125, 0.25, 0.5, 1,
+// // 0.75, 1, 1, 0,
+// // 1, 1, 1, 1
+// // );
+
+// // const B_inverse = new Matrix4().getInverse( B );
+
+// // const G_x = new Matrix4().set(
+// // 0, 0, 0, 0,
+// // 10, 0, 0, 0,
+// // 0, 0, 0, 0,
+// // 0, 0, 0, 0
+// // );
+
+// // const A = B_inverse.multiply( G_x )
+
+// // console.log( A );
+
+// // console.log( B.multiply( A ) );
+
+// // return;
+
+// // const p0 = new Vector3( 0, 0, 0 );
+// // const p1 = new Vector3( 50, 0, 0 );
+// // const p2 = new Vector3( 50, 100, 0 );
+// // const p3 = new Vector3( 100, 100, 0 );
+
+// // // const h1 = new Vector4( 2, -2, 1, 1 );
+// // // const h2 = new Vector4( -3, -3, -2, 1 );
+// // // const h3 = new Vector4( 0, 0, 1, 0 );
+// // // const h4 = new Vector4( 1, 0, 0, 0 );
+
+// // // const hh1 = new Vector4().set(2, -2, 1, 1).applyMatrix4();
+
+// // const m4 = new Matrix4();
+
+// // // hermite curve
+// // // m4.set(
+// // // 2, -2, 1, 1,
+// // // -3, 3, -2, 1,
+// // // 0, 0, 1, 0,
+// // // 1, 0, 0, 0
+// // // );
+
+// // // // hermite curve
+// // // m4.set(
+// // // 2, -2, 1, 1,
+// // // -3, 3, -2, 1,
+// // // 0, 0, 1, 0,
+// // // 1, 0, 0, 0
+// // // );
+
+// // // m4.set(
+// // // 0, 0, 0, 1,
+// // // 1, 1, 1, 1,
+// // // 0, 0, 1, 0,
+// // // 3, 2, 1, 0
+// // // );
+
+// // // curve with midpoint
+// // m4.set(
+// // -4, 0, -4, 4,
+// // 8, -4, 6, -4,
+// // -5, 4, -2, 1,
+// // 1, 0, 0, 0
+// // );
+
+// // const m5 = new Matrix4().set(
+// // 0, 0, 0, 0,
+// // 1, 0, 0, 0,
+// // 0, 0, 0, 0,
+// // 0, 0, 0, 0
+// // );
+
+// // const m6 = new Matrix4().set(
+// // 1, 0, 0, 0,
+// // 0, 0, 0, 0,
+// // 0, 0, 0, 0,
+// // 0, 0, 0, 0
+// // );
+
+
+// // console.log( m4.multiply( m5 ) );
+
+// // const points = [ p0, p1, p2, p3 ];
+
+// // const curve = ( new ParametricPolynomial( points ) );
+
+// // curve.update();
+
+// // console.log( curve.getCoefficents() );
+// // console.log( curve.getCoefficent() );
+
+// } );
+
+// } )
+
+// // describe( 'AutoSpline test', () => {
+
+// // let curve: AutoSpline;
+
+// // beforeEach( () => TestBed.configureTestingModule( {} ) );
+
+// // beforeEach( () => {
+// // curve = new AutoSpline();
+// // } );
+
+// // interface DOT {
+// // id: number,
+// // roadId: number,
+// // laneId: number,
+// // direction: 'forward' | 'backward',
+// // type: 'start' | 'end',
+// // position: Vector3
+// // };
+
+// // interface CONNECTION {
+// // id: number,
+// // incomingRoad: number,
+// // outgoingRoad: number,
+// // point: 'start' | 'end',
+// // links: LINK[]
+// // }
+
+// // interface LINK {
+// // from: number,
+// // to: number,
+// // }
+
+// // // TODO:
+// // // 1. it should give correct lines, arc based points
+
+// // // 2. it should give correct points based on lines, arcs
+// // // it( 'it should give correct points based on lines', () => {
+
+// // // const road = new OdRoad( 'road', 10, 1, 0 );
+
+// // // road.addPlanView();
+
+// // // // road.addGeometryLine( 0, 0, 0, 1, 10 );
+// // // road.addGeometryArc( 0, 0, 0, 0, 10, 0.001 );
+
+// // // // road.getGeometryCoords(0, o)
+
+// // // const odPosTheta = new OdPosTheta();
+
+// // // const points: Vector2[] = [];
+
+// // // for ( let s = 0; s < road.length; s++ ) {
+
+// // // road.getGeometryCoords( s, odPosTheta );
+
+// // // // console.log( odPosTheta );
+
+// // // points.push( odPosTheta.toVector2() );
+// // // }
+
+// // // const planView = new OdPlaneView();
+
+// // // let prevHdg = null;
+// // // let prevCurvature = null;
+
+// // // for ( let i = 2; i < points.length; i++ ) {
+
+// // // const p0 = points[ i - 2 ];
+// // // const p1 = points[ i - 1 ];
+// // // const p2 = points[ i ];
+
+// // // const d = p0.distanceTo( p1 );
+
+// // // const hdg = new THREE.Vector2().subVectors( p1, p0 ).angle();
+// // // const hdg2 = new THREE.Vector2().subVectors( p2, p1 ).angle();
+
+// // // const curvature = Math.fround( hdg - prevHdg );
+// // // const curvature2 = Math.fround( hdg2 - hdg );
+
+// // // // same hdg, same curvature == line
+// // // // diff hdg, same curvature == arc
+
+// // // // line
+// // // if ( Maths.approxEquals( hdg, hdg2 ) ) {
+
+// // // // continue adding to the road
+// // // console.log( 'is line', hdg, hdg2 );
+
+// // // } else {
+
+// // // // create new road
+// // // console.log( 'is curved', curvature, hdg, hdg2 );
+
+// // // }
+
+// // // console.log( d, hdg );
+
+// // // prevHdg = hdg;
+// // // prevCurvature = curvature;
+// // // }
+
+// // // } );
+
+// // // it( 'it should give correct connection count for 2x2 lane road intersection', () => {
+
+// // // const roads = [];
+
+// // // const road1 = new OdRoad( 'road1', 100, 1, -1 );
+// // // const road2 = new OdRoad( 'road1', 100, 2, -1 );
+
+// // // roads.push( road1 );
+// // // roads.push( road2 );
+
+// // // const dots: DOT[] = []
+
+// // // let id = 1;
+
+// // // roads.forEach( road => {
+
+// // // // add 2 lanes on each road
+// // // road.addLaneSection( 0, false );
+
+// // // const laneSection = road.getLastAddedLaneSection();
+
+// // // laneSection.addLane( LaneSide.LEFT, 1, OdLaneType.driving, true, true );
+// // // laneSection.addLane( LaneSide.CENTER, 0, OdLaneType.driving, true, true );
+// // // laneSection.addLane( LaneSide.RIGHT, -1, OdLaneType.driving, true, true );
+
+// // // road1.laneSections[ 0 ].getRightLanes().forEach( lane => {
+// // // dots.push( { id: id++, roadId: road.id, laneId: lane.id, type: 'start', direction: 'backward', position: null } )
+// // // dots.push( { id: id++, roadId: road.id, laneId: lane.id, type: 'end', direction: 'backward', position: null } )
+// // // } )
+
+// // // road1.laneSections[ 0 ].getLeftLanes().forEach( lane => {
+// // // dots.push( { id: id++, roadId: road.id, laneId: lane.id, type: 'start', direction: 'forward', position: null } )
+// // // dots.push( { id: id++, roadId: road.id, laneId: lane.id, type: 'end', direction: 'forward', position: null } )
+// // // } )
+
+// // // } );
+
+// // // const connections = getConnections( dots );
+
+// // // expect( connections.length ).toBe( 12 );
+
+// // // } )
+
+// // it( 'it should give correct connection count for 4x4 highway lane road intersection', () => {
+
+// // const roads = [];
+
+// // const road1 = new OdRoad( 'road1', 100, 1, -1 );
+// // const road2 = new OdRoad( 'road1', 100, 2, -1 );
+
+// // roads.push( road1 );
+// // roads.push( road2 );
+
+// // const dots: DOT[] = []
+
+// // let id = 1;
+
+// // roads.forEach( road => {
+
+// // // add 2 lanes on each road
+// // road.addLaneSection( 0, false );
+
+// // const laneSection = road.getLastAddedLaneSection();
+
+// // laneSection.addLane( LaneSide.LEFT, 3, OdLaneType.driving, true, true );
+// // laneSection.addLane( LaneSide.LEFT, 4, OdLaneType.driving, true, true );
+// // laneSection.addLane( LaneSide.LEFT, 2, OdLaneType.driving, true, true );
+// // laneSection.addLane( LaneSide.LEFT, 1, OdLaneType.driving, true, true );
+
+// // // road1.laneSections[ 0 ].getRightLanes().forEach( lane => {
+// // // dots.push( { id: id++, roadId: road.id, laneId: lane.id, type: 'start', direction: 'backward', position: null } )
+// // // dots.push( { id: id++, roadId: road.id, laneId: lane.id, type: 'end', direction: 'backward', position: null } )
+// // // } )
+
+// // road.laneSections[ 0 ].getLeftLanes().forEach( lane => {
+// // dots.push( { id: id++, roadId: road.id, laneId: lane.id, type: 'start', direction: 'forward', position: null } )
+// // dots.push( { id: id++, roadId: road.id + 2, laneId: lane.id, type: 'end', direction: 'forward', position: null } )
+// // } )
+
+// // } );
+
+// // roads.push( new OdRoad( 'road3', 100, 3, -1 ) );
+// // roads.push( new OdRoad( 'road4', 100, 4, -1 ) );
+
+// // const connections = getConnections( dots, roads );
+
+
+
+// // expect( connections.length ).toBe( 10 );
+
+// // } )
+
+// // function getConnections ( points: DOT[], roads: OdRoad[] ) {
+
+// // const connections: CONNECTION[] = [];
+
+// // const startPoints = points.filter( d => d.type === 'start' );
+// // const endPoints = points.filter( d => d.type === 'end' );
+
+
+// // const blah: [][] = [];
+
+// // // left turn
+// // roads.forEach( road => {
+
+// // const roadStartPoints = points.filter( d => d.type == 'start' && d.roadId == road.id );
+
+// // // start connections
+// // roadStartPoints.forEach( roadStartPoint => {
+
+// // // straight connection
+// // // 1 connection for same lane and road to
+// // const pointOnSameLaneRoad = points.find( other => {
+// // return other.roadId === roadStartPoint.roadId + 2 && // + 2 because continusing road is at + 2
+// // other.laneId == roadStartPoint.laneId &&
+// // other.id != roadStartPoint.id &&
+// // other.type != roadStartPoint.type
+// // } )
+
+// // if ( pointOnSameLaneRoad ) {
+
+// // const links: LINK[] = []
+
+// // connections.push( {
+// // id: 0,
+// // incomingRoad: roadStartPoint.roadId,
+// // outgoingRoad: pointOnSameLaneRoad.roadId,
+// // point: 'start',
+// // links: []
+// // } );
+// // }
+
+// // } );
+
+// // // console.log( road.id );
+
+// // // // find lane with highest id
+// // // const leftMostPoint = startPoints.filter( p => p.roadId == road.id ).reduce( ( x, y ) => {
+// // // return x.laneId > y.laneId ? x : y;
+// // // } )
+
+// // // const leftMostPointOnNextRoad = endPoints.filter( p => p.roadId != road.id ).reduce( ( x, y ) => {
+// // // return x.laneId > y.laneId ? x : y;
+// // // } )
+
+// // // find lane with lowest id
+// // // const rightMostPoint = startPoints.filter( p => p.roadId == road.id ).reduce( ( x, y ) => {
+// // // return x.laneId < y.laneId ? x : y;
+// // // } )
+
+// // // console.log( 'left', leftMostPoint, leftMostPointOnNextRoad );
+// // // console.log( 'right', rightMostPoint );
+
+// // } );
+
+// // // startPoints.forEach( point => {
+
+// // // // left turn
+// // // const pointForLeftTurn = dots.find( other => {
+// // // return other.direction == point.direction &&
+// // // other.roadId != point.roadId &&
+// // // other.laneId == point.laneId &&
+// // // other.type != point.type
+// // // } )
+
+// // // connections.push( {
+// // // incomingRoad: pointForLeftTurn.roadId,
+// // // outgoingRoad: pointForLeftTurn.laneId,
+// // // point: pointForLeftTurn.type,
+// // // } );
+
+// // // } )
+
+// // // for ( let i = 0; i < startPoints.length; i++ ) {
+
+// // // const startingDot = startPoints[ i ];
+
+// // // // console.log( dot, sameDot )
+
+// // // // if direction
+// // // // match start with end; ignore end
+// // // // start from right
+// // // // total dots are lanes * 2
+// // // // not from same road
+
+// // // // dots from other roads
+// // // const otherDots = dots.filter( d => d.roadId != startingDot.roadId );
+
+// // // otherDots.forEach( other => {
+
+// // // const laneConnectExits = connections.find( connection => {
+// // // return connection.road == startingDot.roadId &&
+// // // connection.lane == startingDot.laneId
+// // // } );
+
+// // // if (
+// // // other.type != startingDot.type &&
+// // // ) {
+// // // connections.push( {
+// // // road: other.roadId,
+// // // lane: other.laneId,
+// // // type: other.type,
+// // // } );
+// // // }
+
+// // // } );
+
+// // // }
+
+// // return connections;
+// // }
+
+
+// // } );
+
+// /////////////////////////////////////////
+// // PSE
+
+// // // left road 1
+// // 4F S
+// // 3F S
+// // 2F S
+// // 1F S
+
+// // // right road 3
+// // 4F E
+// // 3F E
+// // 2F E
+// // 1F E
+
+// // // top road 4
+// // 4F E
+// // 3F E
+// // 2F E
+// // 1F E
+
+// // // bottom road 2
+// // 4F S
+// // 3F S
+// // 2F S
+// // 1F S
+
+// // ===========================================================
+
+// // in auto :
+// // max 1 left turn
+// // and
+// // max 1 right turn per road
+// // for outgoing traffic
+
+// // left turns
+// // ---------
+// // left most (highest id) lane of current road
+// // with left most (highest) lane of next road
+// // start point with endpoint
+
+
+// // right turns
+// // -----------
+// // right most lane (lowest id) of current road
+// // with right most (lowest id) lane of next road
+// // start point with endpoint
diff --git a/src/app/core/shapes/auto-spline.ts b/src/app/core/shapes/auto-spline.ts
new file mode 100644
index 00000000..90440497
--- /dev/null
+++ b/src/app/core/shapes/auto-spline.ts
@@ -0,0 +1,288 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Vector2, Vector3 } from 'three';
+import { TvAbstractRoadGeometry } from 'app/modules/tv-map/models/geometries/tv-abstract-road-geometry';
+import { TvLineGeometry } from 'app/modules/tv-map/models/geometries/tv-line-geometry';
+import { TvArcGeometry } from 'app/modules/tv-map/models/geometries/tv-arc-geometry';
+import { PolyLine } from './PolyLine';
+import { RoundLine } from './round-line';
+import { AbstractSpline } from './abstract-spline';
+import { TvRoad } from 'app/modules/tv-map/models/tv-road.model';
+import { RoadControlPoint } from 'app/modules/three-js/objects/road-control-point';
+import { TvPosTheta } from 'app/modules/tv-map/models/tv-pos-theta';
+
+
+export class AutoSpline extends AbstractSpline {
+
+ public type = 'auto';
+
+ public polyline: PolyLine;
+
+ public roundline: RoundLine;
+
+ constructor ( private road?: TvRoad ) {
+
+ super();
+
+ }
+
+ get hdgs () { return this.controlPoints.map( ( cp: RoadControlPoint ) => cp.hdg ) }
+
+ init () {
+
+ this.polyline = new PolyLine( this.controlPoints );
+
+ this.roundline = new RoundLine( this.controlPoints );
+
+ if ( this.meshAddedInScene ) return;
+
+ this.scene.add( this.polyline.mesh );
+
+ this.scene.add( this.roundline.mesh );
+
+ this.meshAddedInScene = true;
+
+ }
+
+ hide (): void {
+
+ this.controlPoints.forEach( i => i.visible = false );
+
+ this.polyline.mesh.visible = false;
+
+ this.roundline.mesh.visible = false;
+
+ }
+
+ show (): void {
+
+ this.controlPoints.forEach( i => i.visible = true );
+
+ this.polyline.mesh.visible = true;
+
+ this.roundline.mesh.visible = true;
+
+ }
+
+ addControlPoint ( cp: RoadControlPoint ) {
+
+ // this.polyline.addPoint( cp );
+
+ // this.roundline.addPoint( cp );
+
+ super.addControlPoint( cp );
+
+ }
+
+ update () {
+
+ this.updateHdgs();
+
+ this.polyline.update();
+
+ this.roundline.update();
+
+
+ }
+
+ updateHdgs () {
+
+ const hdgs = [];
+
+ let hdg, p1, p2, currentPoint, previousPoint;
+
+ for ( let i = 1; i < this.controlPoints.length; i++ ) {
+
+ previousPoint = this.controlPoints[ i - 1 ];
+ currentPoint = this.controlPoints[ i ];
+
+ p1 = new Vector2( currentPoint.position.x, currentPoint.position.y );
+ p2 = new Vector2( previousPoint.position.x, previousPoint.position.y );
+
+ hdg = new Vector2().subVectors( p1, p2 ).angle();
+
+ previousPoint[ 'hdg' ] = hdg;
+
+ hdgs.push( hdg );
+ }
+
+ // setting hdg for the last point
+ if ( hdg != null ) {
+
+ currentPoint[ 'hdg' ] = hdg;
+
+ }
+
+ }
+
+ clear () {
+
+ this.controlPoints.splice( 0, this.controlPoints.length );
+
+ this.scene.remove( this.polyline.mesh );
+
+ this.scene.remove( this.roundline.mesh );
+
+ }
+
+ exportGeometries (): TvAbstractRoadGeometry[] {
+
+ let totalLength = 0;
+
+ const points = this.roundline.points as RoadControlPoint[];
+
+ const radiuses = this.roundline.radiuses;
+
+ const geometries: TvAbstractRoadGeometry[] = [];
+
+ let s = totalLength;
+
+ for ( let i = 1; i < points.length; i++ ) {
+
+ let x, y, hdg, length;
+
+ const previous = points[ i - 1 ].position;
+ const current = points[ i ].position;
+
+ const p1 = new Vector2( previous.x, previous.y );
+
+ const p2 = new Vector2( current.x, current.y );
+
+ const d = p1.distanceTo( p2 );
+
+ // line between p1 and p2
+ if ( d - radiuses[ i - 1 ] - radiuses[ i ] > 0.001 ) {
+
+ [ x, y ] = new Vector2()
+ .subVectors( p2, p1 )
+ .normalize()
+ .multiplyScalar( radiuses[ i - 1 ] )
+ .add( p1 )
+ .toArray();
+
+ // hdg = new Vector2().subVectors( p2, p1 ).angle();
+ hdg = points[ i - 1 ].hdg;
+
+ length = d - radiuses[ i - 1 ] - radiuses[ i ];
+
+ s = totalLength;
+
+ totalLength += length;
+
+ geometries.push( new TvLineGeometry( s, x, y, hdg, length ) );
+
+ }
+
+ // arc for p2
+ if ( radiuses[ i ] > 0 ) { // first and last point can't have zero radiuses
+
+ const next = points[ i + 1 ].position;
+
+ const dir1 = new Vector2( current.x - previous.x, current.y - previous.y ).normalize();
+
+ const dir2 = new Vector2( next.x - current.x, next.y - current.y ).normalize();
+
+ const pp1 = new Vector2()
+ .subVectors( p1, p2 )
+ .normalize()
+ .multiplyScalar( radiuses[ i ] )
+ .add( p2 );
+
+ const pp2 = new Vector2()
+ .subVectors( ( new Vector2( next.x, next.y ) ), p2 )
+ .normalize()
+ .multiplyScalar( radiuses[ i ] )
+ .add( p2 );
+
+ x = pp1.x;
+
+ y = pp1.y;
+
+ hdg = dir1.angle();
+
+ let r, alpha, sign;
+
+ [ r, alpha, length, sign ] = this.getArcParams( pp1, pp2, dir1, dir2 );
+
+ if ( r != Infinity ) {
+
+ s = totalLength;
+
+ totalLength += length;
+
+ const curvature = ( sign > 0 ? 1 : -1 ) * ( 1 / r ); // sign < for mirror image
+
+ geometries.push( new TvArcGeometry( s, x, y, hdg, length, curvature ) );
+
+
+ } else {
+
+ s = totalLength;
+
+ length = pp1.distanceTo( pp2 );
+
+ totalLength += length;
+
+ geometries.push( new TvLineGeometry( s, x, y, hdg, length ) );
+
+ console.warn( "radius is infinity" );
+
+ }
+
+
+ }
+
+ }
+
+ return geometries;
+ }
+
+ addControlPointAt ( position: Vector3 ): RoadControlPoint {
+
+ const index = this.controlPoints.length;
+
+ const point = new RoadControlPoint( this.road, position, "cp", index, index );
+
+ this.controlPoints.push( point );
+
+ this.update();
+
+ return point;
+ }
+
+ getPoint ( t: number, offset = 0 ): Vector3 {
+
+ const geometries = this.exportGeometries();
+
+ const length = geometries.map( g => g.length ).reduce( ( a, b ) => a + b );
+
+ const s = length * t;
+
+ const geometry = geometries.find( g => s >= g.s && s <= g.s2 );
+
+ const posTheta = new TvPosTheta();
+
+ geometry.getCoords( s, posTheta );
+
+ posTheta.addLateralOffset( offset );
+
+ return posTheta.toVector3();
+ }
+
+ getLength () {
+
+ const geometries = this.exportGeometries();
+
+ let length = 0;
+
+ for ( let i = 0; i < geometries.length; i++ ) {
+
+ length += geometries[ i ].length;
+
+ }
+
+ return length;
+ }
+}
diff --git a/src/app/core/shapes/catmull-rom-spline.spec.ts b/src/app/core/shapes/catmull-rom-spline.spec.ts
new file mode 100644
index 00000000..e2273969
--- /dev/null
+++ b/src/app/core/shapes/catmull-rom-spline.spec.ts
@@ -0,0 +1,40 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TestBed } from '@angular/core/testing';
+import { Vector3 } from 'three';
+import { GameObject } from '../game-object';
+import { CatmullRomSpline } from './catmull-rom-spline';
+import { AnyControlPoint } from 'app/modules/three-js/objects/control-point';
+
+
+describe( 'CatmullRomSpline test', () => {
+
+ let spline: CatmullRomSpline;
+
+ beforeEach( () => TestBed.configureTestingModule( {} ) );
+
+ beforeEach( () => {
+ spline = new CatmullRomSpline( false );
+ } );
+
+ it( 'should give correct positions', () => {
+
+ spline.add( AnyControlPoint.create( "", new Vector3( 0, 0, 0 ) ) )
+
+ spline.add( AnyControlPoint.create( "", new Vector3( 50, 0, 0 ) ) )
+
+ spline.add( AnyControlPoint.create( "", new Vector3( 100, 0, 0 ) ) )
+
+ const points = spline.getPoints( 10 );
+
+ // 11 becuase 1 is at the start as well
+ expect( points.length ).toBe( 11 );
+
+ expect( spline.getLength() ).toBe( 100 );
+
+ } );
+
+
+} );
diff --git a/src/app/core/shapes/catmull-rom-spline.ts b/src/app/core/shapes/catmull-rom-spline.ts
new file mode 100644
index 00000000..ff8e5673
--- /dev/null
+++ b/src/app/core/shapes/catmull-rom-spline.ts
@@ -0,0 +1,150 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BufferGeometry, CatmullRomCurve3, Line, LineBasicMaterial, Vector3, LineLoop } from 'three';
+import { TvAbstractRoadGeometry } from 'app/modules/tv-map/models/geometries/tv-abstract-road-geometry';
+import { AnyControlPoint } from 'app/modules/three-js/objects/control-point';
+import { AbstractSpline } from './abstract-spline';
+import { COLOR } from 'app/shared/utils/colors.service';
+
+export class CatmullRomSpline extends AbstractSpline {
+
+ public type: string = 'catmullrom';
+
+ public curveType: string = 'curve';
+
+ public curve: CatmullRomCurve3;
+
+ public mesh: Line;
+
+ constructor ( closed = true, type = 'catmullrom', tension = 0.5 ) {
+
+ super( closed, tension );
+
+ }
+
+ init (): void {
+
+ this.curve = new CatmullRomCurve3( this.controlPointPositions, this.closed, this.type || 'catmullrom', this.tension );
+
+ const geometry = new BufferGeometry();
+
+ // Create the final object to add to the scene
+ if ( this.closed ) {
+
+ this.mesh = new LineLoop( geometry, new LineBasicMaterial( { color: COLOR.RED, opacity: 0.35 } ) );
+
+ } else {
+
+ this.mesh = new Line( geometry, new LineBasicMaterial( { color: COLOR.RED, opacity: 0.35 } ) );
+
+ }
+
+ this.mesh.castShadow = true;
+
+ this.mesh.renderOrder = 1;
+
+ this.mesh.frustumCulled = false;
+
+ this.scene.add( this.mesh );
+ }
+
+
+ hide (): void {
+
+ this.controlPoints.forEach( i => i.visible = false );
+
+ this.mesh.visible = false;
+
+ }
+
+ show (): void {
+
+ this.controlPoints.forEach( i => i.visible = true );
+
+ this.mesh.visible = true;
+
+ }
+
+ hideAllTangents () {
+
+ this.controlPoints.forEach( ( cp: AnyControlPoint ) => {
+
+ this.hideTangenAt();
+
+ } );
+
+ }
+
+ showcontrolPoints () {
+
+ this.controlPoints.forEach( co => co.visible = true );
+
+ }
+
+ hidecontrolPoints () {
+
+ this.controlPoints.forEach( co => co.visible = false );
+
+ }
+
+ showTangentsAt () {
+
+ }
+
+ hideTangenAt () {
+
+ }
+
+ update (): void {
+
+ if ( this.controlPoints.length < 2 ) return;
+
+ // if ( !this.curve ) {
+ // this.curve = new CatmullRomCurve3(
+ // this.controlPointPositions,
+ // this.closed,
+ // this.type,
+ // this.tension
+ // );
+ // }
+
+ this.curve.points = this.controlPointPositions;
+
+ this.curve.updateArcLengths();
+
+ this.mesh.geometry.dispose();
+
+ this.mesh.geometry = new BufferGeometry().setFromPoints( this.curve.getPoints( 100 ) );
+
+ }
+
+ add ( point: AnyControlPoint ) {
+
+ this.controlPoints.push( point );
+
+ this.curve.points.push( point.position );
+
+ this.update();
+ }
+
+ exportGeometries (): TvAbstractRoadGeometry[] {
+
+ throw new Error( 'Method not implemented.' );
+
+ }
+
+ getLength () {
+
+ return this.curve.getLength();
+
+ }
+
+ getPoints ( spacing = 10 ): Vector3[] {
+
+ return this.curve.getPoints( spacing );
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/app/core/shapes/cubic-spline-curve.ts b/src/app/core/shapes/cubic-spline-curve.ts
new file mode 100644
index 00000000..55c91217
--- /dev/null
+++ b/src/app/core/shapes/cubic-spline-curve.ts
@@ -0,0 +1,357 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Curve, CurvePath, Vector3, CatmullRomCurve3 } from 'three';
+import { AnyControlPoint } from 'app/modules/three-js/objects/control-point';
+import { ExplicitSpline } from './explicit-spline';
+import { AutoSpline } from './auto-spline';
+
+
+function CubicBezierP0 ( t, p ) {
+
+ const k = 1 - t;
+ return k * k * k * p;
+
+}
+
+function CubicBezierP1 ( t, p ) {
+
+ const k = 1 - t;
+ return 3 * k * k * t * p;
+
+}
+
+function CubicBezierP2 ( t, p ) {
+
+ return 3 * ( 1 - t ) * t * t * p;
+
+}
+
+function CubicBezierP3 ( t, p ) {
+
+ return t * t * t * p;
+
+}
+
+function CubicBezier ( t, p0, p1, p2, p3 ) {
+
+ return CubicBezierP0( t, p0 ) + CubicBezierP1( t, p1 ) + CubicBezierP2( t, p2 ) +
+ CubicBezierP3( t, p3 );
+
+}
+
+export class CubicSplineCurve extends Curve {
+
+ constructor (
+ public points: Vector3[] = [],
+ public closed?: boolean,
+ public curveType?: string,
+ public tension?: number
+ ) {
+ super();
+ }
+
+ getPoint ( t: number, optionalTarget?: Vector3 ): Vector3 {
+
+ const point = optionalTarget || new Vector3();
+
+ const points = this.points;
+ const p = ( points.length - 1 ) * t;
+
+ const intPoint = Math.floor( p );
+ const weight = p - intPoint;
+
+ const p0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ];
+ const p1 = points[ intPoint ];
+ const p2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ];
+ const p3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ];
+
+ point.set(
+ CubicBezier( weight, p0.x, p1.x, p2.x, p3.x ),
+ CubicBezier( weight, p0.y, p1.y, p2.y, p3.y ),
+ CubicBezier( weight, p0.z, p1.z, p2.z, p3.z ),
+ );
+
+ return point;
+ }
+
+
+}
+
+export class HermiteSplineCurve extends CurvePath {
+
+ constructor (
+ public v0: Vector3, public v1: Vector3, public v2: Vector3, public v3: Vector3
+ ) {
+ super();
+ }
+
+ getPoint ( t: number, optionalTarget?: Vector3 ): Vector3 {
+
+ const point = optionalTarget || new Vector3();
+
+ const p1 = this.v0;
+ const p2 = this.v3;
+ const t1 = new Vector3().subVectors( this.v1, this.v0 ).multiplyScalar( 3.5 );
+ const t2 = new Vector3().subVectors( this.v3, this.v2 ).multiplyScalar( 3.5 );
+
+ const s = t, s2 = s * s, s3 = s2 * s;
+ const h1 = new Vector3().setScalar( 2 * s3 - 3 * s2 + 1 ).multiply( p1 );
+ const h2 = new Vector3().setScalar( -2 * s3 + 3 * s2 ).multiply( p2 );
+ const h3 = new Vector3().setScalar( s3 - 2 * s2 + s ).multiply( t1 );
+ const h4 = new Vector3().setScalar( s3 - s2 ).multiply( t2 );
+
+ point.copy( h1 ).add( h2 ).add( h3 ).add( h4 );
+
+ return point;
+ }
+
+ getCoefficients () {
+
+
+ }
+
+}
+
+export class LineArcSplineCurve extends CurvePath {
+
+ public radiuses: number[];
+
+ /** NOT WORKING CURRENTLY */
+ constructor (
+ public points: AnyControlPoint[]
+ ) {
+ super();
+ }
+
+ getPoint ( t: number, optionalTarget?: Vector3 ): Vector3 {
+
+ this.calcRadius();
+
+ const point = optionalTarget || new Vector3();
+
+ const ARC_TESSEL_HALF = 24 / 2;
+
+ let vertexIndex = 0;
+ let currentPoint = null;
+ let prevPoint = null;
+ let nextPoint = null;
+ let radius = null;
+
+ const i = 1;
+
+ currentPoint = this.points[ i ];
+
+ radius = this.radiuses[ i ];
+
+ if ( radius == 0 ) {
+
+ point.copy( currentPoint );
+
+ } else {
+
+ prevPoint = this.points[ i - 1 ].position;
+ nextPoint = this.points[ i + 1 ].position;
+
+ const p1 = new Vector3()
+ .subVectors( prevPoint, currentPoint )
+ .normalize()
+ .multiplyScalar( radius )
+ .add( currentPoint );
+
+ const p0 = currentPoint;
+
+ const p2 = new Vector3()
+ .subVectors( nextPoint, currentPoint )
+ .normalize()
+ .multiplyScalar( radius )
+ .add( currentPoint );
+
+
+ // for ( let ii = 0; ii < ARC_TESSEL_HALF; ii++ ) {
+
+ // pos.lerpVectors( p1, p0, ii / ARC_TESSEL_HALF );
+
+ // pos.copy( this.arcInterpolation( currentPoint, prevPoint, nextPoint, radius, pos ) );
+
+ // }
+
+ // for ( let ii = 0; ii < ARC_TESSEL_HALF; ii++ ) {
+
+ // pos.lerpVectors( p0, p2, ii / ARC_TESSEL_HALF );
+
+ // pos.copy( this.arcInterpolation( currentPoint, prevPoint, nextPoint, radius, pos ) );
+
+ // }
+
+ point.lerpVectors( p1, p0, t );
+
+ point.copy( this.arcInterpolation( currentPoint, prevPoint, nextPoint, radius, point ) );
+
+ point.lerpVectors( p0, p2, t );
+
+ point.copy( this.arcInterpolation( currentPoint, prevPoint, nextPoint, radius, point ) );
+
+
+ }
+
+ return point;
+ }
+
+ calcRadius (): void {
+
+ if ( this.points.length === 0 ) return;
+
+ // init all radiuses to Infinity
+ this.radiuses = new Array( this.points.length );
+
+ // store lengths from one point to another for the whole spline
+ const lengths = new Array( this.points.length - 1 );
+
+ let currentPoint: AnyControlPoint = null;
+ let nextPoint: AnyControlPoint = null;
+
+ this.points.forEach( ( currentPoint, i ) => {
+
+ // set the radius at each point 0 by default
+ this.radiuses[ i ] = 0;
+
+ // set the lengths until the last point
+ if ( i < this.points.length - 1 ) {
+
+ nextPoint = this.points[ i + 1 ];
+
+ lengths[ i ] = currentPoint.position.distanceTo( nextPoint.position );
+ }
+
+ } )
+
+ // foreach point except the first one
+ for ( let i = 1; i < this.points.length - 1; i++ ) {
+
+ this.radiuses[ i ] = Math.min( lengths[ i - 1 ], lengths[ i ] );
+
+ }
+
+ for ( let updated = true; updated; ) {
+
+ updated = false;
+
+ for ( let i = 1; i < this.points.length - 1; i++ ) {
+
+ const leftR = this.radiuses[ i - 1 ] + this.radiuses[ i ] > lengths[ i - 1 ] ? lengths[ i - 1 ] / 2 : this.radiuses[ i ];
+
+ const rightR = this.radiuses[ i + 1 ] + this.radiuses[ i ] > lengths[ i ] ? lengths[ i ] / 2 : this.radiuses[ i ];
+
+ const minR = Math.min( leftR, rightR );
+
+ if ( minR != this.radiuses[ i ] ) {
+ updated = true;
+ this.radiuses[ i ] = minR;
+ }
+ }
+ }
+ }
+
+ arcInterpolation ( currentPoint: Vector3, prevPoint: Vector3, nextPoint: Vector3, radius: number, v: Vector3 ) {
+
+ const va = new Vector3()
+ .subVectors( prevPoint, currentPoint )
+ .normalize()
+ .multiplyScalar( radius );
+
+ const vb = new Vector3()
+ .subVectors( nextPoint, currentPoint )
+ .normalize()
+ .multiplyScalar( radius );
+
+ // const t = ( va.x * va.x + va.y * va.y + va.z * va.z ) / ( va.x * va.x + va.y * va.y + va.z * va.z + vb.x * va.x + vb.y * va.y + vb.z * va.z );
+ const t = ( va.x * va.x + va.z * va.z + va.y * va.y )
+ / ( va.x * va.x + va.z * va.z + va.y * va.y + vb.x * va.x + vb.z * va.z + vb.y * va.y );
+
+ // center of circle
+ const p = new Vector3().addVectors( va, vb ).multiplyScalar( t ).add( currentPoint );
+
+ // radius of circle
+ const r = new Vector3().addVectors( currentPoint, va ).distanceTo( p );
+
+ // project to circle
+ return new Vector3().subVectors( v, p ).normalize().multiplyScalar( r ).add( p );
+ }
+}
+
+export class CatmullRomPath extends CurvePath {
+
+ private catmull: CatmullRomCurve3;
+
+ constructor (
+ public points: Vector3[] = [],
+ public closed?: boolean,
+ public curveType?: string,
+ public tension?: number
+ ) {
+
+ super();
+
+ this.catmull = new CatmullRomCurve3( points, closed, curveType, tension );
+ }
+
+ getPoint ( t: number, optionalTarget?: Vector3 ): Vector3 {
+
+ return this.catmull.getPoint( t );
+
+ }
+
+ getLength () {
+
+ return this.catmull.getLength();
+
+ }
+}
+
+export class ExplicitSplinePath extends CurvePath {
+
+ constructor (
+ private spline: ExplicitSpline,
+ private offset: number = 0,
+ ) {
+
+ super();
+ }
+
+ getPoint ( t: number, optionalTarget?: Vector3 ): Vector3 {
+
+ return this.spline.getPoint( t, this.offset );
+
+ }
+
+ getLength () {
+
+ return this.spline.getLength();
+
+ }
+}
+
+export class AutoSplinePath extends CurvePath {
+
+ constructor (
+ private spline: AutoSpline,
+ private offset: number = 0,
+ ) {
+
+ super();
+ }
+
+ getPoint ( t: number, optionalTarget?: Vector3 ): Vector3 {
+
+ return this.spline.getPoint( t, this.offset );
+
+ }
+
+ getLength () {
+
+ return this.spline.getLength();
+
+ }
+}
diff --git a/src/app/core/shapes/explicit-spline.spec.ts b/src/app/core/shapes/explicit-spline.spec.ts
new file mode 100644
index 00000000..9357f37c
--- /dev/null
+++ b/src/app/core/shapes/explicit-spline.spec.ts
@@ -0,0 +1,52 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Shape } from 'three';
+import { ExplicitSpline } from './explicit-spline';
+import { ExplicitSplinePath } from './cubic-spline-curve';
+import { TvRoad } from 'app/modules/tv-map/models/tv-road.model';
+import { TvMapSourceFile } from 'app/modules/tv-map/services/tv-map-source-file';
+import { TvGeometryType } from 'app/modules/tv-map/models/tv-common';
+import { Maths } from 'app/utils/maths';
+
+
+describe( 'ExplicitSpline Test', () => {
+
+ let road: TvRoad;
+ let spline: ExplicitSpline;
+
+ beforeEach( () => {
+
+ road = TvMapSourceFile.openDrive.addDefaultRoad();
+
+ spline = road.spline = new ExplicitSpline();
+
+ } );
+
+ it( 'should work as curve path', () => {
+
+ road.addGeometryLine( 0, 0, 0, 0, 100 );
+
+ const shape = new Shape(); shape.moveTo( 0, -0.25 ); shape.lineTo( 0, 0.25 );
+
+ spline.addFromFile( 0, road.startPosition().toVector3(), 0, TvGeometryType.LINE );
+ spline.addFromFile( 1, road.endPosition().toVector3(), 0, TvGeometryType.LINE );
+
+ const path = new ExplicitSplinePath( spline );
+ // const path = new CatmullRomPath( [ road.startPosition().toVector3(), road.endPosition().toVector3() ] );
+
+ expect( Maths.approxEquals( path.getLength(), 100, 0.01 ) ).toBe( true );
+
+ // const extrudeSettings = {
+ // steps: path.getLength() * 2,
+ // bevelEnabled: false,
+ // extrudePath: path
+ // };
+
+ // const geometry = new ExtrudeGeometry( shape, extrudeSettings );
+
+ } );
+
+
+} );
\ No newline at end of file
diff --git a/src/app/core/shapes/explicit-spline.ts b/src/app/core/shapes/explicit-spline.ts
new file mode 100644
index 00000000..4e492f58
--- /dev/null
+++ b/src/app/core/shapes/explicit-spline.ts
@@ -0,0 +1,458 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { AbstractSpline } from './abstract-spline';
+import { TvAbstractRoadGeometry } from 'app/modules/tv-map/models/geometries/tv-abstract-road-geometry';
+import { AnyControlPoint } from 'app/modules/three-js/objects/control-point';
+import { BufferAttribute, BufferGeometry, Line, LineBasicMaterial, Vector2, Vector3 } from 'three';
+
+import * as SPIRAL from './spiral-math.js';
+import { TvGeometryType } from 'app/modules/tv-map/models/tv-common';
+import { TvLineGeometry } from 'app/modules/tv-map/models/geometries/tv-line-geometry';
+import { TvArcGeometry } from 'app/modules/tv-map/models/geometries/tv-arc-geometry';
+import { TvSpiralGeometry } from 'app/modules/tv-map/models/geometries/tv-spiral-geometry';
+import { TvParamPoly3Geometry } from 'app/modules/tv-map/models/geometries/tv-param-poly3-geometry';
+import { HermiteSpline, Length } from './SplineData';
+import { CURVE_TESSEL, CURVE_Y, PARACUBICFACTOR } from './spline-config';
+import { TvRoad } from 'app/modules/tv-map/models/tv-road.model';
+import { RoadControlPoint } from 'app/modules/three-js/objects/road-control-point';
+import { SceneService } from '../services/scene.service';
+import { TvPosTheta } from 'app/modules/tv-map/models/tv-pos-theta';
+
+export class ExplicitSpline extends AbstractSpline {
+
+ type: string = 'explicit';
+
+ private segments: Line[] = []
+
+ // no need for now
+ // private tangentLines: Line[] = [];
+
+ get hdgs () {
+
+ return this.controlPoints.map( ( cp: RoadControlPoint ) => [ cp.hdg, 7, 7 ] );
+
+ }
+
+ get segTypes () {
+
+ return this.controlPoints.map( ( cp: RoadControlPoint ) => cp.segmentType );
+
+ }
+
+ constructor ( private road?: TvRoad ) {
+
+ super();
+
+ }
+
+ init (): void {
+
+ // nothing to init
+
+ }
+
+ hide (): void {
+
+ this.controlPoints.forEach( i => i.hide() );
+
+ this.segments.forEach( segment => segment.visible = false );
+
+ }
+
+ show (): void {
+
+ this.controlPoints.forEach( i => i.show() );
+
+ this.segments.forEach( segment => segment.visible = true );
+
+ }
+
+ update (): void {
+
+ for ( let i = 0; i < this.segments.length; i++ ) {
+
+ this.updateSegment( i );
+
+ }
+
+ }
+
+ clear () {
+
+ this.controlPoints.splice( 0, 1 );
+
+ this.segments.forEach( segment => segment.visible = false );
+
+ }
+
+ exportGeometries (): TvAbstractRoadGeometry[] {
+
+ return this.exportFromSpline( this.segTypes, this.hdgs, this.controlPointPositions );
+
+ }
+
+ exportFromSpline ( segTypes: number[], hdgs: number[][], points: Vector3[] ): TvAbstractRoadGeometry[] {
+
+ let totalLength = 0;
+ const geometries: TvAbstractRoadGeometry[] = [];
+
+ for ( let i = 0; i < this.segments.length; i++ ) {
+
+ const hdg1 = hdgs[ i ];
+ const hdg2 = hdgs[ i + 1 ];
+
+ const dir1 = new Vector2( Math.cos( hdg1[ 0 ] ), Math.sin( hdg1[ 0 ] ) );
+ const dir2 = new Vector2( Math.cos( hdg2[ 0 ] ), Math.sin( hdg2[ 0 ] ) );
+
+ // using y instead of z
+ const p1 = new Vector2( points[ i ].x, points[ i ].y );
+ const p2 = new Vector2( points[ i + 1 ].x, points[ i + 1 ].y );
+
+ const distance = p1.distanceTo( p2 );
+
+ let s = totalLength, x: number, y: number, hdg: number, length: number;
+
+ if ( segTypes[ i ] === TvGeometryType.LINE ) {
+
+ x = p1.x; y = p1.y;
+
+ hdg = hdg1[ 0 ];
+
+ length = distance;
+
+ geometries.push( new TvLineGeometry( s, x, y, hdg, length ) );
+
+ } else if ( segTypes[ i ] === TvGeometryType.ARC ) {
+
+ x = p1.x; y = p1.y;
+
+ hdg = hdg1[ 0 ];
+
+ let radius, alpha, sign;[ radius, alpha, length, sign ] = this.getArcParams( p1, p2, dir1, dir2 );
+
+ // world z is flipped so inverse the sign
+ // const curvature = + ( sign < 0 ? 1 : -1 ) + 1 / r;
+ let curvature = ( sign > 0 ? 1 : -1 ) * ( 1 / radius ); // sign < for mirror image
+
+ // if radius if infinite then curvature should be the least possible value
+ // so its almost close to a line but still an arc
+ if ( radius === Infinity ) curvature = Number.MIN_VALUE;
+
+ // because its alsmot a line we can take the arc length as the simple distance between the points
+ if ( radius === Infinity ) length = distance;
+
+ geometries.push( new TvArcGeometry( s, x, y, hdg, length, curvature ) );
+
+ } else if ( segTypes[ i ] === TvGeometryType.SPIRAL ) {
+
+ let k, dk, _L, iter;[ k, dk, _L, iter ] = SPIRAL.buildClothoid(
+ p1.x,
+ p1.y,
+ SPIRAL.vec2Angle( dir1.x, dir1.y ),
+ p2.x,
+ p2.y,
+ SPIRAL.vec2Angle( dir2.x, dir2.y )
+ );
+
+ x = p1.x; y = p1.y;
+
+ hdg = hdg1[ 0 ];
+
+ length = _L;
+
+ const curvStart = k;
+
+ const curvEnd = ( k + dk * _L );
+
+ geometries.push( new TvSpiralGeometry( s, x, y, hdg, length, curvStart, curvEnd ) );
+
+ } else if ( segTypes[ i ] === TvGeometryType.PARAMPOLY3 ) {
+
+ const ma = dir1.x, mb = dir1.y, mc = -mb, md = ma;
+
+ const det = 1 / ( ma * md - mb * mc );
+
+ const mia = det * md, mib = -mb * det, mic = -mc * det, mid = ma * det;
+
+ const dir2proj = new Vector2(
+ dir2.x * mia + dir2.y * mic,
+ dir2.x * mib + dir2.y * mid
+ );
+
+ /*flip y axis*/
+ dir2proj.y = -dir2proj.y;
+
+ const p2proj = new Vector2().subVectors( p2, p1 );
+
+ p2proj.set( p2proj.x * mia + p2proj.y * mic, p2proj.x * mib + p2proj.y * mid );
+
+ /*flip y axis*/
+ p2proj.y = -p2proj.y;
+
+ x = p1.x; y = p1.y;
+
+ hdg = hdg1[ 0 ];
+
+ length = distance; //TODO fix this
+
+ const t1 = new Vector2( 1, 0 ).multiplyScalar( PARACUBICFACTOR * hdgs[ i ][ 1 ] );
+ const t2 = new Vector2( dir2proj.x, dir2proj.y ).multiplyScalar( PARACUBICFACTOR * hdgs[ i + 1 ][ 2 ] );
+
+ const hs = HermiteSpline( new Vector2( 0, 0 ), p2proj, t1, t2 );
+
+ length = Length( hs, 0.001 );
+
+ const f3 = new Vector2( -2 * p2proj.x + 1 * t1.x + 1 * t2.x, -2 * p2proj.y + 1 * t1.y + 1 * t2.y );
+ const f2 = new Vector2( 3 * p2proj.x - 2 * t1.x - 1 * t2.x, 3 * p2proj.y - 2 * t1.y - 1 * t2.y );
+ const f1 = new Vector2( 1 * t1.x, 1 * t1.y );
+
+ const aU = 0;
+ const bU = f1.x;
+ const cU = f2.x;
+ const dU = f3.x;
+
+ const aV = 0;
+ const bV = f1.y;
+ const cV = f2.y;
+ const dV = f3.y;
+
+ geometries.push( new TvParamPoly3Geometry( s, x, y, hdg, length, aU, bU, cU, dU, aV, bV, cV, dV ) );
+ }
+
+ totalLength += length;
+
+ }
+
+ return geometries;
+ }
+
+ /**
+ *
+ * @param cp
+ * @deprecated
+ */
+ addControlPoint ( cp: AnyControlPoint ) {
+
+ cp.visible = false;
+
+ this.addControlPointAtNew( cp.position );
+
+ }
+
+ addControlPointAtNew ( position: Vector3 ) {
+
+ const index = this.segTypes.length;
+
+ const previousPoint = this.controlPoints[ index - 1 ] as RoadControlPoint;
+
+ let hdg: number = 0;
+
+ if ( previousPoint ) {
+
+ // need to set previous point to spiral to avoid bugs
+ previousPoint.segmentType = TvGeometryType.SPIRAL;
+
+ hdg = SPIRAL.vec2Angle( previousPoint.position.x, previousPoint.position.y );
+ }
+
+ return this.addFromFile( index, position, hdg, TvGeometryType.SPIRAL );
+ }
+
+ addFromFile ( index: number, position: Vector3, hdg: number, segType: TvGeometryType ) {
+
+ // this.segTypes.push( segType );
+
+ const controlPoint = new RoadControlPoint( this.road, position, 'cp', index, index );
+
+ controlPoint.segmentType = segType;
+
+ // TODO: move this in spline mesh or somewhere else
+ SceneService.add( controlPoint );
+
+ this.controlPoints.push( controlPoint );
+
+ controlPoint.hdg = hdg;
+
+ controlPoint.addDefaultTangents( hdg, 1, 1 );
+
+ // update tangent line
+ // this.tangent.update( this.hdgs, this.tangentLines );
+
+ if ( index > 0 ) {
+
+ // add empty curve mesh
+ this.addSegment( index - 1 );
+
+ // calculate curve mesh
+ this.updateSegment( index - 1 );
+
+ }
+
+ return controlPoint;
+ }
+
+ addSegment ( index: number ) {
+
+ const geometry = new BufferGeometry();
+
+ geometry.attributes.position = new BufferAttribute( new Float32Array( CURVE_TESSEL * 3 ), 3 );
+
+ const line = new Line( geometry, new LineBasicMaterial( { color: 0x0000ff, opacity: 0.35, linewidth: 2 } ) );
+
+ line[ 'tag' ] = 'curve';
+
+ line[ 'tagindex' ] = index;
+
+ line.castShadow = true;
+
+ line.renderOrder = 3;
+
+ line.frustumCulled = false;
+
+ this.segments.push( line );
+
+ this.scene.add( line );
+
+ // this.tangentLines.push( line );
+ }
+
+ updateSegment ( idx: number ) {
+
+ const mesh = this.segments[ idx ];
+ const posattr = ( mesh.geometry as BufferGeometry ).attributes.position as BufferAttribute;
+
+ const dir1 = new Vector2( Math.cos( this.hdgs[ idx ][ 0 ] ), Math.sin( this.hdgs[ idx ][ 0 ] ) );
+ const dir2 = new Vector2( Math.cos( this.hdgs[ idx + 1 ][ 0 ] ), Math.sin( this.hdgs[ idx + 1 ][ 0 ] ) );
+
+ const p1 = this.controlPointPositions[ idx ];
+ const p2 = this.controlPointPositions[ idx + 1 ];
+
+ const L = p1.distanceTo( p2 );
+
+ if ( this.segTypes[ idx ] == TvGeometryType.LINE ) {
+
+ posattr.setXYZ( 0, p1.x, p1.y, p1.z );
+ posattr.setXYZ( 1, p2.x, p2.y, p2.z );
+
+ for ( let ii = 2; ii < CURVE_TESSEL; ii++ ) {
+ posattr.setXYZ( ii, p2.x, p2.y, p2.z );
+ }
+
+ posattr.needsUpdate = true;
+
+ } else if ( this.segTypes[ idx ] == TvGeometryType.ARC ) {
+
+ const alpha = Math.acos( dir1.dot( dir2 ) );
+
+ // let advx = new Vector2( p2.x - p1.x, p2.z - p1.z ).normalize();
+ const advx = new Vector2( p2.x - p1.x, p2.y - p1.y ).normalize();
+ const advy = new Vector2( -advx.y, advx.x );
+
+ // get inverse matrix of p1 local space transform
+ const ma = dir1.x, mb = dir1.y, mc = -mb, md = ma;
+ const det = 1 / ( ma * md - mb * mc );
+ const mia = det * md, mib = -mb * det, mic = -mc * det, mid = ma * det;
+
+ // project p2 into p1 local space
+ const arcdir = new Vector2( advx.x * mia + advx.y * mic, advx.x * mib + advx.y * mid );
+
+ // flip y axis according to arcdir (world z is flipped so inverse the sign)
+ advy.multiplyScalar( -Math.sign( arcdir.y ) );
+
+ for ( let ii = 0; ii < CURVE_TESSEL; ii++ ) {
+
+ const theta = ii / ( CURVE_TESSEL - 1 ) * alpha;
+
+ const gamma = ( alpha - theta ) / 2;
+
+ const d = L * Math.sin( theta / 2 ) / Math.sin( alpha / 2 );
+
+ // posattr.setXYZ( ii, p1.x + advx.x * Math.cos( gamma ) * d + advy.x * Math.sin( gamma ) * d, CURVE_Y, p1.z + advx.y * Math.cos( gamma ) * d + advy.y * Math.sin( gamma ) * d );
+ posattr.setXYZ( ii, p1.x + advx.x * Math.cos( gamma ) * d + advy.x * Math.sin( gamma ) * d, p1.y + advx.y * Math.cos( gamma ) * d + advy.y * Math.sin( gamma ) * d, CURVE_Y );
+ }
+
+ posattr.needsUpdate = true;
+
+ } else if ( this.segTypes[ idx ] == TvGeometryType.SPIRAL ) {
+
+ const sd = SPIRAL.vec2Angle( dir1.x, dir1.y );
+ const ed = SPIRAL.vec2Angle( dir2.x, dir2.y );
+
+ // axis issue
+ // let k, dk, _L, iter;[ k, dk, _L, iter ] = SPIRAL.buildClothoid( p1.x * 100, p1.z * 100, sd, p2.x * 100, p2.z * 100, ed );
+ let k, dk, _L, iter;[ k, dk, _L, iter ] = SPIRAL.buildClothoid( p1.x * 100, p1.y * 100, sd, p2.x * 100, p2.y * 100, ed );
+
+ // axis issue
+ // let spiralarc = SPIRAL.clothoid_1( p1.x * 100, p1.z * 100, p1.y, sd, k, k + dk * _L, _L, p2.y, CURVE_TESSEL - 1 )
+ const spiralarc = SPIRAL.clothoid_1( p1.x * 100, p1.y * 100, p1.y, sd, k, k + dk * _L, _L, p2.y, CURVE_TESSEL - 1 )
+
+ for ( let ii = 0; ii < CURVE_TESSEL; ii++ ) {
+ // axis issue
+ // posattr.setXYZ( ii, spiralarc[ ii ][ 0 ] / 100, spiralarc[ ii ][ 2 ], spiralarc[ ii ][ 1 ] / 100 );
+ // posattr.setXYZ( ii, spiralarc[ ii ][ 0 ] / 100, spiralarc[ ii ][ 1 ] / 100, spiralarc[ ii ][ 2 ] );
+ posattr.setXYZ( ii, spiralarc[ ii ][ 0 ] / 100, spiralarc[ ii ][ 1 ] / 100, 0 );
+ }
+
+ posattr.needsUpdate = true;
+
+ } else if ( this.segTypes[ idx ] == TvGeometryType.PARAMPOLY3 ) {
+
+ const t1 = new Vector3( dir1.x, dir1.y, CURVE_Y ).multiplyScalar( PARACUBICFACTOR * this.hdgs[ idx ][ 1 ] );
+ const t2 = new Vector3( dir2.x, dir2.y, CURVE_Y ).multiplyScalar( PARACUBICFACTOR * this.hdgs[ idx + 1 ][ 2 ] );
+
+ const posfoo = new Vector3();
+
+ for ( let ii = 0; ii < CURVE_TESSEL; ii++ ) {
+ const s = ii / ( CURVE_TESSEL - 1 ), s2 = s * s, s3 = s2 * s;
+ const h1 = new Vector3().setScalar( 2 * s3 - 3 * s2 + 1 ).multiply( p1 );
+ const h2 = new Vector3().setScalar( -2 * s3 + 3 * s2 ).multiply( p2 );
+ const h3 = new Vector3().setScalar( s3 - 2 * s2 + s ).multiply( t1 );
+ const h4 = new Vector3().setScalar( s3 - s2 ).multiply( t2 );
+ posfoo.copy( h1 ).add( h2 ).add( h3 ).add( h4 );
+ posattr.setXYZ( ii, posfoo.x, posfoo.y, posfoo.z );
+ }
+
+ posattr.needsUpdate = true;
+ }
+ }
+
+ /**
+ * returns position on the curve
+ * @param t A position on the curve. Must be in the range [ 0, 1 ].
+ */
+ getPoint ( t: number, offset = 0 ): Vector3 {
+
+ const geometries = this.exportGeometries();
+
+ const length = geometries.map( g => g.length ).reduce( ( a, b ) => a + b );
+
+ const s = length * t;
+
+ const geometry = geometries.find( g => s >= g.s && s <= g.s2 );
+
+ const posTheta = new TvPosTheta();
+
+ geometry.getCoords( s, posTheta );
+
+ posTheta.addLateralOffset( offset );
+
+ return posTheta.toVector3();
+ }
+
+ getLength () {
+
+ const geometries = this.exportGeometries();
+
+ let length = 0;
+
+ for ( let i = 0; i < geometries.length; i++ ) {
+
+ length += geometries[ i ].length;
+
+ }
+
+ return length;
+ }
+}
diff --git a/src/app/core/shapes/parametric-spline.ts b/src/app/core/shapes/parametric-spline.ts
new file mode 100644
index 00000000..0dc539d8
--- /dev/null
+++ b/src/app/core/shapes/parametric-spline.ts
@@ -0,0 +1,808 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BufferAttribute, BufferGeometry, Group, Line, LineBasicMaterial, Vector2, Vector3 } from 'three';
+import { TvAbstractRoadGeometry } from 'app/modules/tv-map/models/geometries/tv-abstract-road-geometry';
+import { AnyControlPoint } from 'app/modules/three-js/objects/control-point';
+import { TangentLine } from './TangentLine';
+import { AbstractSpline } from './abstract-spline';
+import { CURVE_TESSEL, CURVE_Y, PARACUBICFACTOR } from './spline-config';
+import { HermiteSpline, Length } from './SplineData';
+
+export class ParametricSpline extends AbstractSpline {
+
+ public type: string = 'parametric';
+
+ public mesh: Group;
+
+ /**
+ * the main control points of the curve, not tangent points
+ */
+ public controlObjects: AnyControlPoint[] = [];
+
+ private tangent: TangentLine;
+
+ // private polyline: PolyLine;
+
+ // private roundline: RoundLine;
+
+ private hdgs: any[] = [];
+
+ /**
+ * Holds reference to the line segments forming the whole curve
+ */
+ private segments: Line[] = [];
+
+ constructor ( private parent: any ) {
+
+ super();
+
+ }
+
+ init (): void {
+
+ this.mesh = new Group();
+
+ this.tangent = new TangentLine( this.controlPointPositions );
+
+ // this.polyline = new PolyLine( this.controlPointPositions );
+
+ // this.roundline = new RoundLine( this.controlPointPositions );
+
+ if ( this.meshAddedInScene ) return;
+
+ this.mesh.add( this.tangent.mesh );
+
+ this.scene.add( this.mesh );
+
+ // this.scene.add( this.polyline.mesh );
+
+ // this.scene.add( this.roundline.mesh );
+
+ this.meshAddedInScene = true;
+
+ }
+
+
+ hide (): void {
+
+ this.controlPoints.forEach( i => i.visible = false );
+
+ this.tangent.mesh.visible = false;
+
+ // this.polyline.mesh.visible = false;
+
+ // this.roundline.mesh.visible = false;
+
+ }
+
+ show (): void {
+
+ this.controlPoints.forEach( i => i.visible = true );
+
+ this.tangent.mesh.visible = true;
+
+ // this.polyline.mesh.visible = true;
+
+ // this.roundline.mesh.visible = true;
+
+ }
+
+ hideAllTangents () {
+
+ this.controlObjects.forEach( ( cp: AnyControlPoint ) => {
+
+ this.hideTangenAt( cp.tagindex );
+
+ } );
+
+ }
+
+ showControlObjects () {
+
+ this.controlObjects.forEach( co => co.visible = true );
+
+ }
+
+ hideControlObjects () {
+
+ this.controlObjects.forEach( co => co.visible = false );
+
+ }
+
+ showTangentsAt ( id: number ) {
+
+ this.controlPoints[ this.controlObjects.length + id * 2 + 0 ].visible = true;
+ this.controlPoints[ this.controlObjects.length + id * 2 + 1 ].visible = true;
+
+ this.tangent.updateOneSegment( id, this.controlObjects[ id ] );
+
+ }
+
+ hideTangenAt ( id: number ) {
+
+ this.controlPoints[ this.controlObjects.length + id * 2 + 0 ].visible = false;
+ this.controlPoints[ this.controlObjects.length + id * 2 + 1 ].visible = false;
+
+ }
+
+ update (): void {
+
+ }
+
+ updateSpine ( cp: AnyControlPoint ) {
+
+ if ( cp.tag == "cp" || cp.tag == "tpf" || cp.tag == "tpb" ) {
+
+ let ptidx = cp.tagindex;
+
+ if ( cp.tag == "cp" ) {
+
+ // do nothing for now
+
+ }
+
+ else if ( cp.tag == "tpf" ) {
+
+ const delta = new Vector3().subVectors(
+ this.controlPoints[ this.controlObjects.length + ptidx * 2 ].position,
+ this.controlPoints[ ptidx ].position
+ );
+
+ this.hdgs[ ptidx ][ 0 ] = Math.atan2( delta.y, delta.x );
+
+ this.hdgs[ ptidx ][ 1 ] = delta.length();
+
+ }
+
+ // tslint:disable-next-line: one-line
+ else if ( cp.tag == "tpb" ) {
+
+ const delta = new Vector3().subVectors(
+ this.controlPoints[ this.controlObjects.length + ptidx * 2 + 1 ].position,
+ this.controlPoints[ ptidx ].position
+ );
+
+ this.hdgs[ ptidx ][ 0 ] = Math.PI + Math.atan2( delta.y, delta.x );
+
+ this.hdgs[ ptidx ][ 2 ] = delta.length();
+
+ }
+
+ let pa: Vector3, pb: Vector3;
+
+ [ pa, pb ] = this.tangent.updateOneSegment( ptidx, this.controlPoints[ ptidx ].position );
+
+ this.controlPoints[ this.controlObjects.length + ptidx * 2 ].position.copy( pa );
+
+ this.controlPoints[ this.controlObjects.length + ptidx * 2 + 1 ].position.copy( pb );
+
+ if ( ptidx < this.segments.length ) {
+
+ this.updateSegment( ptidx );
+
+ }
+
+ if ( ptidx - 1 >= 0 ) {
+
+ this.updateSegment( ptidx - 1 );
+
+ }
+
+ }
+
+ }
+
+ exportGeometries (): TvAbstractRoadGeometry[] {
+
+ throw Error( 'method nor implew' );
+
+ }
+
+ exportFromSpline (): TvAbstractRoadGeometry[] {
+
+ throw Error( 'method nor implew' );
+ }
+
+ add ( position: AnyControlPoint, heading: number, newIndex: number ): AnyControlPoint {
+
+ const controlPointObject = this.createControlPoint( "cp", newIndex, newIndex );
+
+ this.controlObjects.push( controlPointObject );
+
+ controlPointObject.copyPosition( position.position );
+
+ this.hdgs.push( [ heading, 7, 7 ] );
+
+ const tgX = Math.cos( heading );
+
+ const tgY = Math.sin( heading );
+
+ const frontTangentPosition = new Vector3( tgX, tgY, CURVE_Y )
+ .multiplyScalar( this.hdgs[ newIndex ][ 1 ] )
+ .add( this.controlPointPositions[ newIndex ] );
+
+ const backTangentPosition = new Vector3( tgX, tgY, CURVE_Y )
+ .multiplyScalar( -this.hdgs[ newIndex ][ 2 ] )
+ .add( this.controlPointPositions[ newIndex ] );
+
+ const frontTanget = this.createControlPoint( "tpf", newIndex, newIndex + 1 + newIndex * 2 );
+
+ frontTanget.position.copy( frontTangentPosition );
+
+ const backTanget = this.createControlPoint( "tpb", newIndex, newIndex + 1 + newIndex * 2 + 1 );
+
+ backTanget.position.copy( backTangentPosition );
+
+ // update tangent line
+ this.tangent.update( this.hdgs, this.controlObjects );
+
+ if ( newIndex > 0 ) {
+
+ // add empty curve mesh
+ this.addNewSegment( newIndex - 1 );
+
+ // calculate curve mesh
+ this.updateSegment( newIndex - 1 );
+
+ }
+
+ this.tangent.updateOneSegment( newIndex, controlPointObject );
+
+ return controlPointObject;
+ }
+
+ addNewSegment ( idx ) {
+
+ const buffgeo = new BufferGeometry();
+
+ buffgeo.attributes.position = new BufferAttribute( new Float32Array( CURVE_TESSEL * 3 ), 3 );
+
+ const curvemesh = new Line( buffgeo, new LineBasicMaterial( { color: 0x0000ff, opacity: 0.35, linewidth: 2 } ) );
+
+ curvemesh[ 'tag' ] = 'curve';
+
+ curvemesh[ 'tagindex' ] = idx;
+
+ curvemesh.userData.parent = this.parent;
+
+ this.segments.push( curvemesh );
+
+ this.scene.add( curvemesh );
+
+ // this.controlPointLines.push( curvemesh );
+
+ }
+
+ updateSegment ( idx: number ) {
+
+ const curvemesh = this.segments[ idx ];
+
+ if ( !curvemesh ) {
+
+ console.error( "curve not found", idx, this.segments );
+
+ return;
+
+ } else {
+
+ }
+
+
+ const posattr = ( curvemesh.geometry as BufferGeometry ).attributes.position as BufferAttribute;
+
+ const dir1 = new Vector2( Math.cos( this.hdgs[ idx ][ 0 ] ), Math.sin( this.hdgs[ idx ][ 0 ] ) );
+ const dir2 = new Vector2( Math.cos( this.hdgs[ idx + 1 ][ 0 ] ), Math.sin( this.hdgs[ idx + 1 ][ 0 ] ) );
+
+ const p1 = this.controlPointPositions[ idx ];
+ const p2 = this.controlPointPositions[ idx + 1 ];
+
+ const t1 = new Vector3( dir1.x, dir1.y, CURVE_Y ).multiplyScalar( PARACUBICFACTOR * this.hdgs[ idx ][ 1 ] );
+ const t2 = new Vector3( dir2.x, dir2.y, CURVE_Y ).multiplyScalar( PARACUBICFACTOR * this.hdgs[ idx + 1 ][ 2 ] );
+
+ const posfoo = new Vector3();
+
+ for ( let ii = 0; ii < CURVE_TESSEL; ii++ ) {
+
+ const s = ii / ( CURVE_TESSEL - 1 ), s2 = s * s, s3 = s2 * s;
+
+ const h1 = new Vector3().setScalar( 2 * s3 - 3 * s2 + 1 ).multiply( p1 );
+
+ const h2 = new Vector3().setScalar( -2 * s3 + 3 * s2 ).multiply( p2 );
+
+ const h3 = new Vector3().setScalar( s3 - 2 * s2 + s ).multiply( t1 );
+
+ const h4 = new Vector3().setScalar( s3 - s2 ).multiply( t2 );
+
+ posfoo.copy( h1 ).add( h2 ).add( h3 ).add( h4 );
+
+ posattr.setXYZ( ii, posfoo.x, posfoo.y, posfoo.z );
+
+ }
+
+ posattr.needsUpdate = true;
+
+ }
+
+ getPoint ( s: number, target?: Vector3 ) {
+
+ for ( let i = 0; i < this.segments.length; i++ ) {
+
+ const segment = this.segments[ i ];
+ const segmentLength = this.getSegmentLength( i );
+
+ if ( s <= segmentLength ) {
+
+
+ return this.getPointInSegment( ( segmentLength - s ) / segmentLength, i );
+
+ break;
+ }
+
+ }
+ }
+
+ Hermite_Interpolate ( y0, y1, y2, y3, mu, tension, bias ) {
+
+ let m0, m1, mu2, mu3;
+
+ let a0, a1, a2, a3;
+
+ mu2 = mu * mu;
+
+ mu3 = mu2 * mu;
+
+ m0 = ( y1 - y0 ) * ( 1 + bias ) * ( 1 - tension ) / 2;
+
+ m0 += ( y2 - y1 ) * ( 1 - bias ) * ( 1 - tension ) / 2;
+
+ m1 = ( y2 - y1 ) * ( 1 + bias ) * ( 1 - tension ) / 2;
+
+ m1 += ( y3 - y2 ) * ( 1 - bias ) * ( 1 - tension ) / 2;
+
+ a0 = 2 * mu3 - 3 * mu2 + 1;
+
+ a1 = mu3 - 2 * mu2 + mu;
+
+ a2 = mu3 - mu2;
+
+ a3 = -2 * mu3 + 3 * mu2;
+
+ return ( a0 * y1 + a1 * m0 + a2 * m1 + a3 * y2 );
+ }
+
+
+ // getPoints ( spacing = 5 ) {
+
+ // const positions = [];
+
+ // for ( let i = 0; i < this.segments.length; i++ ) {
+
+ // const segment = this.segments[ i ];
+ // const segmentLength = this.getSegmentLength( i );
+
+ // const p1 = this.controlPointPositions[ i ];
+ // const p2 = this.controlPointPositions[ i + 1 ];
+ // const t1 = this.controlPoints[ this.controlObjects.length + i * 2 + 0 ].position;
+ // const t2 = this.controlPoints[ this.controlObjects.length + ( i + 1 ) * 2 + 1 ].position;
+
+ // for ( let t = 0; t < segmentLength; t++ ) {
+
+ // // const x = this.Hermite_Interpolate( t2.x, p2.x, p1.x, t1.x, t / segmentLength, 0, 0 );
+ // // const y = this.Hermite_Interpolate( t2.y, p2.y, p1.y, t1.y, t / segmentLength, 0, 0 );
+
+ // const x = this.Hermite_Interpolate( t1.x, p2.x, p1.x, t2.x, t / segmentLength, 0, 0 );
+ // const y = this.Hermite_Interpolate( t1.y, p2.y, p1.y, t2.y, t / segmentLength, 0, 0 );
+
+ // positions.push( new Vector3( x, y, 0 ) );
+
+ // }
+ // }
+
+ // return positions;
+ // }
+
+ getPoints ( spacing = 10 ) {
+
+ const positions = [];
+
+ for ( let i = 0; i < this.segments.length; i++ ) {
+
+ const segment = this.segments[ i ];
+ const segmentLength = this.getSegmentLength( i );
+
+ const dir1 = new Vector2( Math.cos( this.hdgs[ i ][ 0 ] ), Math.sin( this.hdgs[ i ][ 0 ] ) );
+ const dir2 = new Vector2( Math.cos( this.hdgs[ i + 1 ][ 0 ] ), Math.sin( this.hdgs[ i + 1 ][ 0 ] ) );
+
+ const p1 = this.controlPointPositions[ i ];
+ const p2 = this.controlPointPositions[ i + 1 ];
+
+ const t1 = new Vector3( dir1.x, dir1.y, CURVE_Y ).multiplyScalar( PARACUBICFACTOR * this.hdgs[ i ][ 1 ] );
+ const t2 = new Vector3( dir2.x, dir2.y, CURVE_Y ).multiplyScalar( PARACUBICFACTOR * this.hdgs[ i + 1 ][ 2 ] );
+
+ for ( let u = 0; u < segmentLength; u += spacing ) {
+
+ const t = this.getUtoTmapping( u / segmentLength, 10 );
+
+ const s = t, s2 = s * s, s3 = s2 * s;
+
+ const h1 = new Vector3().setScalar( 2 * s3 - 3 * s2 + 1 ).multiply( p1 );
+ const h2 = new Vector3().setScalar( -2 * s3 + 3 * s2 ).multiply( p2 );
+ const h3 = new Vector3().setScalar( s3 - 2 * s2 + s ).multiply( t1 );
+ const h4 = new Vector3().setScalar( s3 - s2 ).multiply( t2 );
+
+ const position = new Vector3().copy( h1 ).add( h2 ).add( h3 ).add( h4 );
+
+ positions.push( position );
+
+ }
+ }
+
+ return positions;
+ }
+
+
+ getUtoTmapping ( u, distance ) {
+
+ const arcLengths = this.getLengths( 10 );
+
+ let i = 0;
+ const il = arcLengths.length;
+
+ let targetArcLength; // The targeted u distance value to get
+
+ if ( distance ) {
+
+ targetArcLength = distance;
+
+ } else {
+
+ targetArcLength = u * arcLengths[ il - 1 ];
+
+ }
+
+ // binary search for the index with largest value smaller than target u distance
+
+ let low = 0, high = il - 1, comparison;
+
+ while ( low <= high ) {
+
+ // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats
+ i = Math.floor( low + ( high - low ) / 2 );
+
+ comparison = arcLengths[ i ] - targetArcLength;
+
+ if ( comparison < 0 ) {
+
+ low = i + 1;
+
+ } else if ( comparison > 0 ) {
+
+ high = i - 1;
+
+ } else {
+
+ high = i;
+ break;
+
+ // DONE
+
+ }
+
+ }
+
+ i = high;
+
+ if ( arcLengths[ i ] === targetArcLength ) {
+
+ return i / ( il - 1 );
+
+ }
+
+ // we could get finer grain at lengths, or use simple interpolation between two points
+
+ const lengthBefore = arcLengths[ i ];
+ const lengthAfter = arcLengths[ i + 1 ];
+
+ const segmentLength = lengthAfter - lengthBefore;
+
+ // determine where we are between the 'before' and 'after' points
+
+ const segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength;
+
+ // add that fractional amount to t
+
+ const t = ( i + segmentFraction ) / ( il - 1 );
+
+ return t;
+
+ }
+
+
+ // getPoints ( spacing = 5 ) {
+
+ // const positions = [];
+
+ // for ( let i = 0; i < this.segments.length; i++ ) {
+
+ // const position = new Vector3();
+
+ // const segment = this.segments[ i ];
+ // const segmentLength = this.getSegmentLength( i );
+
+ // const hdg1 = this.hdgs[ i ];
+ // const hdg2 = this.hdgs[ i + 1 ];
+
+ // const dir1 = new Vector2( Math.cos( hdg1[ 0 ] ), Math.sin( hdg1[ 0 ] ) );
+ // const dir2 = new Vector2( Math.cos( hdg2[ 0 ] ), Math.sin( hdg2[ 0 ] ) );
+
+ // const p1 = new Vector2( this.controlObjects[ i ].position.x, this.controlObjects[ i ].position.y );
+ // const p2 = new Vector2( this.controlObjects[ i + 1 ].position.x, this.controlObjects[ i + 1 ].position.y );
+
+ // const ma = dir1.x, mb = dir1.y, mc = -mb, md = ma;
+
+ // const det = 1 / ( ma * md - mb * mc );
+
+ // const mia = det * md, mib = -mb * det, mic = -mc * det, mid = ma * det;
+
+ // const dir2proj = new Vector2(
+ // dir2.x * mia + dir2.y * mic,
+ // dir2.x * mib + dir2.y * mid
+ // );
+
+ // /*flip y axis*/
+ // dir2proj.y = -dir2proj.y;
+
+ // const p2proj = new Vector2().subVectors( p2, p1 );
+
+ // p2proj.set( p2proj.x * mia + p2proj.y * mic, p2proj.x * mib + p2proj.y * mid );
+
+ // /*flip y axis*/
+ // p2proj.y = -p2proj.y;
+
+ // const hdg = hdg1[ 0 ];
+
+ // const t1 = new Vector2( 1, 0 ).multiplyScalar( PARACUBICFACTOR * this.hdgs[ i ][ 1 ] );
+ // const t2 = new Vector2( dir2proj.x, dir2proj.y ).multiplyScalar( PARACUBICFACTOR * this.hdgs[ i + 1 ][ 2 ] );
+
+ // const hs = HermiteSpline( new Vector2( 0, 0 ), p2proj, t1, t2 );
+ // const length = Length( hs, 0.001 );
+
+ // const f3 = new Vector2( -2 * p2proj.x + 1 * t1.x + 1 * t2.x, -2 * p2proj.y + 1 * t1.y + 1 * t2.y );
+ // const f2 = new Vector2( 3 * p2proj.x - 2 * t1.x - 1 * t2.x, 3 * p2proj.y - 2 * t1.y - 1 * t2.y );
+ // const f1 = new Vector2( 1 * t1.x, 1 * t1.y );
+
+ // const aU = 0;
+ // const bU = f1.x;
+ // const cU = f2.x;
+ // const dU = f3.x;
+
+ // const aV = 0;
+ // const bV = f1.y;
+ // const cV = f2.y;
+ // const dV = f3.y;
+
+ // const steps = segmentLength / spacing;
+
+ // for ( let ii = 0; ii < segmentLength; ii += steps ) {
+
+ // // normalised p between 0 to 1
+ // const p = ii / length;
+
+ // const uLocal =
+ // ( aU ) +
+ // ( bU * p ) +
+ // ( cU * p * p ) +
+ // ( dU * p * p * p );
+
+ // const vLocal =
+ // ( aV ) +
+ // ( bV * p ) +
+ // ( cV * p * p ) +
+ // ( dV * p * p * p );
+
+ // // Derivate to get heading change
+ // // const dCoeffsU = ( new Vector3( bU, cU, dU ) ).multiply( new Vector3( 1, 2, 3 ) );
+ // // const dCoeffsV = ( new Vector3( bV, cV, dV ) ).multiply( new Vector3( 1, 2, 3 ) );
+
+ // // const dx = this.polyeval( p, dCoeffsU );
+ // // const dy = this.polyeval( p, dCoeffsV );
+
+ // // const tangent = Math.atan2( dy, dx );
+
+ // // apply rotation with respect to start
+ // const xnew = p1.x + uLocal;
+ // const ynew = p1.y + vLocal;
+
+ // position.set( xnew, ynew, 0 );
+
+ // positions.push( position );
+ // }
+ // }
+
+ // return positions;
+ // }
+
+ polyeval ( t: number, v: Vector3 ): number {
+
+ return ( v.x ) + ( v.y * t ) + ( v.z * t * t );
+ }
+
+ getPointInSegment ( t: number, id: number, rettarget?: Vector3 ): Vector3 {
+
+ const p1 = this.controlPointPositions[ id ];
+ const p2 = this.controlPointPositions[ id + 1 ];
+ const p3 = this.controlPoints[ this.controlObjects.length + id * 2 + 0 ].position;
+ const p4 = this.controlPoints[ this.controlObjects.length + ( id + 1 ) * 2 + 1 ].position;
+
+ const s = t, s2 = s * s, s3 = s2 * s;
+
+ const h1 = new Vector3().setScalar( 2 * s3 - 3 * s2 + 1 ).multiply( p1 );
+ const h2 = new Vector3().setScalar( -2 * s3 + 3 * s2 ).multiply( p2 );
+ const h3 = new Vector3().setScalar( s3 - 2 * s2 + s ).multiply( p3 );
+ const h4 = new Vector3().setScalar( s3 - s2 ).multiply( p4 );
+
+ const posfoo = new Vector3();
+
+ posfoo.copy( h1 ).add( h2 ).add( h3 ).add( h4 )
+
+ return posfoo;
+
+ // const dir1 = new Vector2( Math.cos( this.hdgs[ id ][ 0 ] ), Math.sin( this.hdgs[ id ][ 0 ] ) );
+ // const dir2 = new Vector2( Math.cos( this.hdgs[ id + 1 ][ 0 ] ), Math.sin( this.hdgs[ id + 1 ][ 0 ] ) );
+
+ // const p1 = this.controlPointPositions[ id ];
+ // const p2 = this.controlPointPositions[ id + 1 ];
+
+ // const t1 = new Vector3( dir1.x, dir1.y, CURVE_Y ).multiplyScalar( PARACUBICFACTOR * this.hdgs[ id ][ 1 ] );
+ // const t2 = new Vector3( dir2.x, dir2.y, CURVE_Y ).multiplyScalar( PARACUBICFACTOR * this.hdgs[ id + 1 ][ 2 ] );
+
+ // // const t1Pos = this.controlPoints[ this.controlObjects.length + id * 2 + 0 ].position;
+ // // const t2Pos = this.controlPoints[ this.controlObjects.length + ( id + 1 ) * 2 + 1 ].position;
+
+ // // const t1 = new Vector3().subVectors( t1Pos, p1 ).multiplyScalar( PARACUBICFACTOR );
+ // // const t2 = new Vector3().subVectors( p2, t2Pos ).multiplyScalar( PARACUBICFACTOR );
+
+ // const posfoo = new Vector3();
+
+ // // for ( let ii = 0; ii < CURVE_TESSEL; ii++ ) {
+
+ // const s = t, s2 = s * s, s3 = s2 * s;
+
+ // const h1 = new Vector3().setScalar( 2 * s3 - 3 * s2 + 1 ).multiply( p1 );
+
+ // const h2 = new Vector3().setScalar( -2 * s3 + 3 * s2 ).multiply( p2 );
+
+ // const h3 = new Vector3().setScalar( s3 - 2 * s2 + s ).multiply( t1 );
+
+ // const h4 = new Vector3().setScalar( s3 - s2 ).multiply( t2 );
+
+ // posfoo.copy( h1 ).add( h2 ).add( h3 ).add( h4 );
+
+ // return posfoo;
+
+ // // posattr.setXYZ( ii, posfoo.x, posfoo.y, posfoo.z );
+
+ // }
+
+
+ // const retpoint = rettarget || new Vector3();
+
+ // const p1 = this.controlObjects[ i ].position;
+ // const p2 = this.controlObjects[ i + 1 ].position;
+
+ // const t1Pos = this.controlPointPositions[ this.controlObjects.length + i * 2 + 0 ];
+ // const t2Pos = this.controlPointPositions[ this.controlObjects.length + ( i + 1 ) * 2 + 1 ];
+
+ // const t1 = new Vector3().subVectors( t1Pos, p1 ).multiplyScalar( PARACUBICFACTOR );
+ // const t2 = new Vector3().subVectors( p2, t2Pos ).multiplyScalar( PARACUBICFACTOR );
+
+ // const s = t, s2 = s * s, s3 = s2 * s;
+
+ // const h1 = new Vector3().setScalar( 2 * s3 - 3 * s2 + 1 ).multiply( p1 );
+
+ // const h2 = new Vector3().setScalar( -2 * s3 + 3 * s2 ).multiply( p2 );
+
+ // const h3 = new Vector3().setScalar( s3 - 2 * s2 + s ).multiply( t1 );
+
+ // const h4 = new Vector3().setScalar( s3 - s2 ).multiply( t2 );
+
+ // retpoint.copy( h1 ).add( h2 ).add( h3 ).add( h4 );
+
+ // return retpoint;
+
+ }
+
+ getSegmentLength ( i: number ): number {
+
+ const p1 = new Vector2( this.controlObjects[ i ].position.x, this.controlObjects[ i ].position.y );
+ const p2 = new Vector2( this.controlObjects[ i + 1 ].position.x, this.controlObjects[ i + 1 ].position.y );
+
+ const hdg1 = this.hdgs[ i ];
+ const hdg2 = this.hdgs[ i + 1 ];
+
+ const dir1 = new Vector2( Math.cos( hdg1[ 0 ] ), Math.sin( hdg1[ 0 ] ) );
+ const dir2 = new Vector2( Math.cos( hdg2[ 0 ] ), Math.sin( hdg2[ 0 ] ) );
+
+ const ma = dir1.x, mb = dir1.y, mc = -mb, md = ma;
+
+ const det = 1 / ( ma * md - mb * mc );
+
+ const mia = det * md, mib = -mb * det, mic = -mc * det, mid = ma * det;
+
+ const dir2proj = new Vector2(
+ dir2.x * mia + dir2.y * mic,
+ dir2.x * mib + dir2.y * mid
+ );
+
+ /*flip y axis*/
+ dir2proj.y = -dir2proj.y;
+
+ const p2proj = new Vector2().subVectors( p2, p1 );
+
+ p2proj.set( p2proj.x * mia + p2proj.y * mic, p2proj.x * mib + p2proj.y * mid );
+
+ /*flip y axis*/
+ p2proj.y = -p2proj.y;
+
+ const x = p1.x; const y = p1.y;
+
+ const hdg = hdg1[ 0 ];
+
+ const t1 = new Vector2( 1, 0 ).multiplyScalar( PARACUBICFACTOR * this.hdgs[ i ][ 1 ] );
+ const t2 = new Vector2( dir2proj.x, dir2proj.y ).multiplyScalar( PARACUBICFACTOR * this.hdgs[ i + 1 ][ 2 ] );
+
+ const hs = HermiteSpline( new Vector2( 0, 0 ), p2proj, t1, t2 );
+ const length = Length( hs, 0.001 );
+
+ return length;
+ }
+
+ getLength (): number {
+
+ let totalLength = 0;
+
+ for ( let i = 0; i < this.segments.length; i++ ) {
+
+ const length = this.getSegmentLength( i );
+
+ totalLength += length;
+
+ }
+
+ return totalLength;
+ }
+
+ getLengths ( divisions ) {
+
+ const cache = [];
+ let current, last = this.getPoint( 0 );
+ let sum = 0;
+
+ cache.push( 0 );
+
+ for ( let p = 1; p <= divisions; p++ ) {
+
+ current = this.getPoint( p / divisions );
+ sum += current.distanceTo( last );
+ cache.push( sum );
+ last = current;
+
+ }
+
+ return cache;
+
+ // const lenghts = [];
+
+ // lenghts.push( 0 );
+
+ // for ( let i = 0; i < this.segments.length; i++ ) {
+
+ // lenghts.push( this.getSegmentLength( i ) );
+
+ // }
+
+ // return lenghts;
+ }
+}
\ No newline at end of file
diff --git a/src/app/core/shapes/round-line.ts b/src/app/core/shapes/round-line.ts
new file mode 100644
index 00000000..a6acade1
--- /dev/null
+++ b/src/app/core/shapes/round-line.ts
@@ -0,0 +1,227 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Vector3, BufferAttribute, BufferGeometry, Line, LineBasicMaterial } from 'three';
+import { COLOR } from 'app/shared/utils/colors.service';
+import { MAX_CTRL_POINTS, ARC_TESSEL } from "./spline-config";
+import { BaseControlPoint } from 'app/modules/three-js/objects/control-point';
+
+export class RoundLine {
+
+ public radiuses: number[];
+
+ curveType;
+
+ mesh;
+
+ constructor ( public points: BaseControlPoint[] ) {
+
+ const geometry = new BufferGeometry();
+
+ geometry.addAttribute( 'position', new BufferAttribute( new Float32Array( MAX_CTRL_POINTS * ARC_TESSEL * 3 ), 3 ) );
+
+ this.curveType = 'roundline';
+
+ this.mesh = new Line( geometry, new LineBasicMaterial( { color: COLOR.RED, opacity: 0.35 } ) );
+
+ this.mesh.castShadow = true;
+
+ this.mesh.renderOrder = 3;
+
+ this.mesh.frustumCulled = false;
+
+ }
+
+ addPoint ( point: BaseControlPoint ) {
+
+ this.points.push( point );
+
+ }
+
+ calcRadius (): void {
+
+ if ( this.points.length === 0 ) return;
+
+ // init all radiuses to Infinity
+ this.radiuses = new Array( this.points.length );
+
+ // store lengths from one point to another for the whole spline
+ const lengths = new Array( this.points.length - 1 );
+
+ let currentPoint: BaseControlPoint = null;
+
+ let nextPoint: BaseControlPoint = null;
+
+ this.points.forEach( ( currentPoint, i ) => {
+
+ // set the radius at each point 0 by default
+ this.radiuses[ i ] = 0;
+
+ // set the lengths until the last point
+ if ( i < this.points.length - 1 ) {
+
+ nextPoint = this.points[ i + 1 ];
+
+ lengths[ i ] = currentPoint.position.distanceTo( nextPoint.position );
+
+ }
+
+ } );
+
+ // // foreach point
+ // for ( let i = 0; i < this.points.length; i++ ) {
+ // // set the radius at each point 0 by default
+ // this.radiuses[ i ] = 0;
+ // // set the lengths until the last point
+ // if ( i < this.points.length - 1 ) {
+ // currentPoint = this.points[ i ];
+ // nextPoint = this.points[ i + 1 ];
+ // lengths[ i ] = currentPoint.distanceTo( nextPoint );
+ // }
+ // }
+
+ // foreach point except the first one
+ for ( let i = 1; i < this.points.length - 1; i++ ) {
+
+ this.radiuses[ i ] = Math.min( lengths[ i - 1 ], lengths[ i ] );
+
+ }
+
+ for ( let updated = true; updated; ) {
+
+ updated = false;
+
+ for ( let i = 1; i < this.points.length - 1; i++ ) {
+
+ const leftR = this.radiuses[ i - 1 ] + this.radiuses[ i ] > lengths[ i - 1 ] ? lengths[ i - 1 ] / 2 : this.radiuses[ i ];
+
+ const rightR = this.radiuses[ i + 1 ] + this.radiuses[ i ] > lengths[ i ] ? lengths[ i ] / 2 : this.radiuses[ i ];
+
+ const minR = Math.min( leftR, rightR );
+
+ if ( minR != this.radiuses[ i ] ) {
+
+ updated = true;
+
+ this.radiuses[ i ] = minR;
+
+ }
+ }
+ }
+ }
+
+ update (): void {
+
+ if ( this.points.length <= 1 ) return;
+
+ this.calcRadius();
+
+ const position: BufferAttribute = this.mesh.geometry.attributes.position;
+
+ const pos = new Vector3();
+
+ const ARC_TESSEL_HALF = ARC_TESSEL / 2;
+
+ let vertexIndex = 0;
+
+ let currentPoint: Vector3 = null;
+
+ let prevPoint: Vector3 = null;
+
+ let nextPoint: Vector3 = null;
+
+ let radius = null;
+
+ // foreach point
+
+ for ( let i = 0; i < this.points.length; i++ ) {
+
+ currentPoint = this.points[ i ].position;
+
+ radius = this.radiuses[ i ];
+
+ if ( radius == 0 ) {
+
+ pos.copy( currentPoint );
+
+ position.setXYZ( vertexIndex++, pos.x, pos.y, pos.z );
+
+ } else {
+
+ prevPoint = this.points[ i - 1 ].position;
+
+ nextPoint = this.points[ i + 1 ].position;
+
+ const p1 = new Vector3()
+ .subVectors( prevPoint, currentPoint )
+ .normalize()
+ .multiplyScalar( radius )
+ .add( currentPoint );
+
+ const p0 = currentPoint;
+
+ const p2 = new Vector3()
+ .subVectors( nextPoint, currentPoint )
+ .normalize()
+ .multiplyScalar( radius )
+ .add( currentPoint );
+
+ for ( let ii = 0; ii < ARC_TESSEL_HALF; ii++ ) {
+
+ pos.lerpVectors( p1, p0, ii / ARC_TESSEL_HALF );
+
+ pos.copy( this.arcInterpolation( currentPoint, prevPoint, nextPoint, radius, pos ) );
+
+ position.setXYZ( vertexIndex++, pos.x, pos.y, pos.z );
+
+ }
+
+ for ( let ii = 0; ii < ARC_TESSEL_HALF; ii++ ) {
+
+ pos.lerpVectors( p0, p2, ii / ARC_TESSEL_HALF );
+
+ pos.copy( this.arcInterpolation( currentPoint, prevPoint, nextPoint, radius, pos ) );
+
+ position.setXYZ( vertexIndex++, pos.x, pos.y, pos.z );
+
+ }
+ }
+ }
+
+ // repeat last point
+ for ( let i = vertexIndex; i < MAX_CTRL_POINTS * ARC_TESSEL; i++ ) {
+
+ position.setXYZ( i, pos.x, pos.y, pos.z );
+
+ }
+
+ position.needsUpdate = true;
+ }
+
+ arcInterpolation ( currentPoint: Vector3, prevPoint: Vector3, nextPoint: Vector3, radius: number, v: Vector3 ) {
+
+ const va = new Vector3()
+ .subVectors( prevPoint, currentPoint )
+ .normalize()
+ .multiplyScalar( radius );
+
+ const vb = new Vector3()
+ .subVectors( nextPoint, currentPoint )
+ .normalize()
+ .multiplyScalar( radius );
+
+ // const t = ( va.x * va.x + va.y * va.y + va.z * va.z ) / ( va.x * va.x + va.y * va.y + va.z * va.z + vb.x * va.x + vb.y * va.y + vb.z * va.z );
+ const t = ( va.x * va.x + va.z * va.z + va.y * va.y )
+ / ( va.x * va.x + va.z * va.z + va.y * va.y + vb.x * va.x + vb.z * va.z + vb.y * va.y );
+
+ // center of circle
+ const p = new Vector3().addVectors( va, vb ).multiplyScalar( t ).add( currentPoint );
+
+ // radius of circle
+ const r = new Vector3().addVectors( currentPoint, va ).distanceTo( p );
+
+ // project to circle
+ return new Vector3().subVectors( v, p ).normalize().multiplyScalar( r ).add( p );
+ }
+}
diff --git a/src/app/core/shapes/spiral-math.js b/src/app/core/shapes/spiral-math.js
new file mode 100644
index 00000000..650b19b7
--- /dev/null
+++ b/src/app/core/shapes/spiral-math.js
@@ -0,0 +1,731 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+var SPIRAL = {
+ /** S(x) for small x numerator. */
+ SN: [
+ -2.99181919401019853726E3,
+ 7.08840045257738576863E5,
+ -6.29741486205862506537E7,
+ 2.54890880573376359104E9,
+ -4.42979518059697779103E10,
+ 3.18016297876567817986E11
+ ],
+
+ /** S(x) for small x denominator. */
+ SD: [
+ 2.81376268889994315696E2,
+ 4.55847810806532581675E4,
+ 5.17343888770096400730E6,
+ 4.19320245898111231129E8,
+ 2.24411795645340920940E10,
+ 6.07366389490084639049E11
+ ],
+
+ /** C(x) for small x numerator. */
+ CN: [
+ -4.98843114573573548651E-8,
+ 9.50428062829859605134E-6,
+ -6.45191435683965050962E-4,
+ 1.88843319396703850064E-2,
+ -2.05525900955013891793E-1,
+ 9.99999999999999998822E-1
+ ],
+
+ /** C(x) for small x denominator. */
+ CD: [
+ 3.99982968972495980367E-12,
+ 9.15439215774657478799E-10,
+ 1.25001862479598821474E-7,
+ 1.22262789024179030997E-5,
+ 8.68029542941784300606E-4,
+ 4.12142090722199792936E-2,
+ 1.00000000000000000118E0
+ ],
+
+ /** Auxiliary function f(x) numerator. */
+ FN: [
+ 4.21543555043677546506E-1,
+ 1.43407919780758885261E-1,
+ 1.15220955073585758835E-2,
+ 3.45017939782574027900E-4,
+ 4.63613749287867322088E-6,
+ 3.05568983790257605827E-8,
+ 1.02304514164907233465E-10,
+ 1.72010743268161828879E-13,
+ 1.34283276233062758925E-16,
+ 3.76329711269987889006E-20
+ ],
+
+ /** Auxiliary function f(x) denominator. */
+ FD: [
+ 7.51586398353378947175E-1,
+ 1.16888925859191382142E-1,
+ 6.44051526508858611005E-3,
+ 1.55934409164153020873E-4,
+ 1.84627567348930545870E-6,
+ 1.12699224763999035261E-8,
+ 3.60140029589371370404E-11,
+ 5.88754533621578410010E-14,
+ 4.52001434074129701496E-17,
+ 1.25443237090011264384E-20
+ ],
+
+ /** Auxiliary function g(x) numerator. */
+ GN: [
+ 5.04442073643383265887E-1,
+ 1.97102833525523411709E-1,
+ 1.87648584092575249293E-2,
+ 6.84079380915393090172E-4,
+ 1.15138826111884280931E-5,
+ 9.82852443688422223854E-8,
+ 4.45344415861750144738E-10,
+ 1.08268041139020870318E-12,
+ 1.37555460633261799868E-15,
+ 8.36354435630677421531E-19,
+ 1.86958710162783235106E-22
+ ],
+
+ /** Auxiliary function g(x) denominator. */
+ GD: [
+ 1.47495759925128324529E0,
+ 3.37748989120019970451E-1,
+ 2.53603741420338795122E-2,
+ 8.14679107184306179049E-4,
+ 1.27545075667729118702E-5,
+ 1.04314589657571990585E-7,
+ 4.60680728146520428211E-10,
+ 1.10273215066240270757E-12,
+ 1.38796531259578871258E-15,
+ 8.39158816283118707363E-19,
+ 1.86958710162783236342E-22
+ ],
+
+ /**
+ * Compute a polynomial in x.
+ * @param x double; x
+ * @param coef double[]; coefficients
+ * @return polynomial in x
+ */
+ polevl: function(x, coef) {
+ var result = coef[0];
+ for (var i = 0; i < coef.length; i++) {
+ result = result * x + coef[i];
+ }
+ return result;
+ },
+
+ /**
+ * Compute a polynomial in x.
+ * @param x double; x
+ * @param coef double[]; coefficients
+ * @return polynomial in x
+ */
+ p1evl: function(x, coef) {
+ var result = x + coef[0];
+ for (var i = 0; i < coef.length; i++) {
+ result = result * x + coef[i];
+ }
+ return result;
+ },
+
+ /**
+ * Approximate the Fresnel function.
+ * @param xxa double; the xxa parameter
+ * @return double[]; array with two double values c and s
+ */
+ fresnel: function(xxa) {
+ var x = Math.abs(xxa);
+ var x2 = x * x;
+ var cc, ss;
+
+ if (x2 < 2.5625) {
+ var t = x2 * x2;
+ ss = x * x2 * this.polevl(t, this.SN) / this.p1evl(t, this.SD);
+ cc = x * this.polevl(t, this.CN) / this.polevl(t, this.CD);
+ }
+ else if (x > 36974.0) {
+ cc = 0.5;
+ ss = 0.5;
+ }
+ else {
+ var t = Math.PI * x2;
+ var u = 1.0 / (t * t);
+ t = 1.0 / t;
+ var f = 1.0 - u * this.polevl(u, this.FN) / this.p1evl(u, this.FD);
+ var g = t * this.polevl(u, this.GN) / this.p1evl(u, this.GD);
+
+ t = Math.PI * 0.5 * x2;
+ var c = Math.cos(t);
+ var s = Math.sin(t);
+ t = Math.PI * x;
+ cc = 0.5 + (f * s - g * c) / t;
+ ss = 0.5 - (f * c + g * s) / t;
+ }
+
+ if (xxa < 0.0) {
+ cc = -cc;
+ ss = -ss;
+ }
+ return [cc, ss];
+ },
+
+ /**
+ * Approximate one point of the "standard" spiral (curvature at start is 0).
+ * @param s run-length along spiral
+ * @param cDot first derivative of curvature [1/m2]
+ * @param initialCurvature double; curvature at start
+ * @return double[x,y,t]; array of three double values containing x, y, and tangent direction
+ */
+ odrSpiral: function(s, cDot, initialCurvature) {
+ var a = Math.sqrt(Math.PI / Math.abs(cDot));
+
+ var xy = this.fresnel(initialCurvature + s / a);
+ return [xy[0] * a, xy[1] * a * Math.sign(cDot), s * s * cDot * 0.5];
+ },
+
+ /**
+ * Approximate a clothoid that starts in the x-direction.
+ * @param initialCurvature double; curvature at start
+ * @param curvatureDerivative double; rate of curvature change along the clothoid
+ * @param length double; total length of the clothoid
+ * @param numSegments int; number of segments used to approximate (the number of points is one higher than this)
+ * @return double[[x,y,z]]; the line points array; the z-component of each point is set to 0
+ */
+ clothoid_0: function(initialCurvature, curvatureDerivative, length, numSegments) {
+ var points = new Array(numSegments + 1);
+ // get start tangent
+ var offset = this.odrSpiral(initialCurvature / curvatureDerivative, curvatureDerivative, initialCurvature);
+ // make it to align with x-axis
+ var sinRot = Math.sin(-offset[2]);
+ var cosRot = Math.cos(-offset[2]);
+ for (var i = 0; i <= numSegments; i++) {
+ var xyd = this.odrSpiral(i * length / numSegments + initialCurvature / curvatureDerivative, curvatureDerivative, initialCurvature);
+ var dx = xyd[0] - offset[0];
+ var dy = xyd[1] - offset[1];
+ points[i] = [dx * cosRot - dy * sinRot, dx * sinRot + dy * cosRot , 0];
+ }
+ return points;
+ },
+
+ /**
+ * Approximate a clothoid that starts at a given point in the given direction and curvature. Elevation is linearly
+ * interpolated over the length of the clothoid.
+ * @param x1 double; x-coordinate of the start point
+ * @param y1 double; y-coordinate of the start point
+ * @param z1 double; z-component at start of the curve
+ * @param startDirection double; rotation in radians at the start of the curve
+ * @param startCurvature double; curvature at the start of the clothoid
+ * @param endCurvature double; curvature at the end of the clothoid
+ * @param length double; length of the clothoid
+ * @param z2 double; z-component at end of the curve
+ * @param numSegments int; number of segments used to approximate (the number of points is one higher than this)
+ * @return double[[x,y,z]]; the line points array; the clothoid
+ */
+ clothoid_1: function(x1, y1, z1, startDirection, startCurvature, endCurvature, length, z2, numSegments) {
+ var result = this.clothoid_0(startCurvature, (endCurvature - startCurvature) / length, length, numSegments);
+ var sinRot = Math.sin(startDirection);
+ var cosRot = Math.cos(startDirection);
+ var list = new Array(result.length);
+ var elevationPerStep = (z2 - z1) / (result.length - 1);
+ for (var i = 0; i < result.length; i++) {
+ var p = result[i];
+ list[i] = [x1 + cosRot * p[0] - sinRot * p[1], y1 + sinRot * p[0]+cosRot * p[1] , z1 + i * elevationPerStep];
+ }
+ return list;
+ },
+
+ /**
+ * Approximate a clothoid with curvature 0 at start.
+ * @param start double[x,y,z]; starting point of the clothoid
+ * @param startDirection double; start direction of the clothoid
+ * @param endCurvature double; curvature at the end of the clothoid [1/m]
+ * @param length double; length of the clothoid
+ * @param z2 double; elevation at end of the clothoid
+ * @param numSegments int; number of segments used to approximate (the number of points is one higher than this)
+ * @return double[[x,y,z]]; the line points array; the clothoid
+ */
+ clothoid_2: function(start, startDirection, endCurvature, length, z2, numSegments) {
+ return clothoid_1(start[0], start[1], start[2], startDirection, 0, endCurvature, length, z2, numSegments);
+ },
+
+ /**
+ * Approximate a clothoid.
+ * @param start double[x,y,z]; starting point of the clothoid
+ * @param startDirection double; start direction of the clothoid
+ * @param startCurvature double; curvature at the start of the clothoid [1/m]
+ * @param endCurvature double; curvature at the end of the clothoid [1/m]
+ * @param length double; length of the clothoid
+ * @param z2 double; elevation at end of the clothoid
+ * @param numSegments int; number of segments used to approximate (the number of points is one higher than this)
+ * @return double[[x,y,z]]; the line points array; the clothoid
+ */
+ clothoid_3: function(start, startDirection, startCurvature, endCurvature, length, z2, numSegments) {
+ return clothoid_1(start[0], start[1], start[2], startDirection, startCurvature, endCurvature, length, z2, numSegments);
+ },
+
+ /**
+ * Approximate a clothoid with curvature 0 at start.
+ * @param start double[x,y,z]; starting point of the clothoid
+ * @param startDirection double; start direction of the clothoid
+ * @param endCurvature double; curvature at the end of the clothoid
+ * @param length double; length of the clothoid
+ * @param z2 double; elevation at end of the clothoid
+ * @param numSegments int; number of segments used to approximate (the number of points is one higher than this)
+ * @return double[[x,y,z]]; the line points array; the clothoid
+ */
+ clothoid_4: function(start, startDirection, endCurvature, length, z2, numSegments) {
+ return clothoid_3(start, startDirection, 0, endCurvature, length, z2, numSegments);
+ },
+
+ /**
+ * vec2Angle: get direction of vector2d in radians, which is angle between x axis and the vector
+ * @param xy double; vector coordinate, not need to be normalized
+ * @return double; [0,2*PI]
+ */
+ vec2Angle: function(x,y) {
+ var d=Math.sqrt(x*x+y*y);
+ x/=d; y/=d
+ if(y>=0) return Math.acos(x);
+ else return 2*Math.PI-Math.acos(x);
+ },
+
+ /**
+ * guessA: Find guess for zeros of function g(A)
+ * @param {*} double; phi0
+ * @param {*} double; phi1
+ * @return double;
+ */
+ guessA: function (phi0, phi1) {
+ var CF = [2.989696028701907, 0.716228953608281, -0.458969738821509, -0.502821153340377, 0.261062141752652, -0.045854475238709];
+ var X = phi0 / Math.PI;
+ var Y = phi1 / Math.PI;
+ var xy = X * Y;
+ return (phi0 + phi1) * (CF[0] + xy * (CF[1] + xy * CF[2]) + (CF[3] + xy * CF[4]) * (X * X + Y * Y) + CF[5] * (X * X * X * X + Y * Y * Y * Y));
+ },
+
+ /**
+ * findA: Find a zero of function g(A) defined as
+ * g(A) = \int_0^1 \sin( A*t^2+(delta-A)*t+phi0 ) dt
+ * @param {*} double; Aguess
+ * @param {*} double; delta
+ * @param {*} double; phi0
+ * @param {*} double; tol
+ * @return [double,double]
+ */
+ findA: function (Aguess, delta, phi0, tol) {
+ var A = Aguess;
+ for (var iter = 1; iter <= 500; iter++) {
+ [intC, intS] = this.GeneralizedFresnelCS(3, 2 * A, delta - A, phi0);
+ var f = intS[0];
+ var df = intC[2] - intC[1];
+ A = A - f / df;
+ if (Math.abs(f) < tol)
+ break;
+ }
+ if (Math.abs(f) > tol * 10) {
+ console.log('Newton iteration fails, f = ', f);
+ console.log('Aguess = ', Aguess, ', A = ', A, ', delta = ', delta, ', phi0 = ', phi0);
+ }
+ return [A, iter]
+ },
+ /**
+ * normalizeAngle: normalize angle in the range [-pi,pi]
+ * @param {*}double; phi_in
+ * @return double; normalized angle
+ */
+ normalizeAngle: function (phi_in) {
+ var phi = phi_in;
+ while (phi > Math.PI)
+ phi = phi - 2 * Math.PI;
+ while (phi < -Math.PI)
+ phi = phi + 2 * Math.PI;
+ return phi
+ },
+ /**
+ * buildClothoid: Compute parameters of the G1 Hermite clothoid fitting
+ *
+ * x0, y0 = coodinate of initial point
+ * theta0 = orientation (angle) of the clothoid at initial point
+ * x1, y1 = coodinate of final point
+ * theta1 = orientation (angle) of the clothoid at final point
+ *
+ * On output:
+ * L = the lenght of the clothoid curve from initial to final point
+ * k = curvature at initial point
+ * dk = derivative of curvature respect to arclength, notice that curvature at final point is k+dk*L
+ * iter = Newton Iterations used to solve the interpolation problem
+ */
+ buildClothoid: function( x0, y0, theta0, x1, y1, theta1 ) {
+ var dx = x1 - x0;
+ var dy = y1 - y0;
+ var r = Math.sqrt(dx * dx + dy * dy);
+ var phi = Math.atan2(dy, dx);
+
+ var phi0 = this.normalizeAngle(theta0 - phi);
+ var phi1 = this.normalizeAngle(theta1 - phi);
+ var delta = phi1 - phi0;
+
+ // initial point
+ var Aguess = this.guessA(phi0, phi1);
+
+ // Newton iteration
+ [A, iter] = this.findA(Aguess, delta, phi0, 1e-12);
+
+ // final operation
+ [h, g] = this.GeneralizedFresnelCS(1, 2 * A, delta - A, phi0);
+ var L = r / h;
+
+ if (L > 0) {
+ var k = (delta - A) / L;
+ var dk = 2 * A / (L * L);
+ }
+ else {
+ console.error('negative length');
+ }
+
+
+ return [ k, dk, L, iter ]
+ },
+
+ /**
+ *
+ * @param {*} int; nk
+ * @param {*} double; a
+ * @param {*} double; b
+ * @param {*} double; c
+ */
+ GeneralizedFresnelCS: function (nk, a, b, c) {
+ var epsi = 1e-3; // best thresold
+ if (Math.abs(a) < epsi) // case `a` small
+ [X, Y] = this.evalXYaSmall(nk, a, b, 3);
+ else
+ [X, Y] = this.evalXYaLarge(nk, a, b);
+ var cc = Math.cos(c);
+ var ss = Math.sin(c);
+ for (var k = 0; k < nk; k++) {
+ var xx = X[k];
+ var yy = Y[k];
+ X[k] = xx * cc - yy * ss;
+ Y[k] = xx * ss + yy * cc;
+ }
+ return [X,Y]
+ },
+
+ FresnelCSk: function (nk, t) {
+ var C = new Array(nk);
+ var S = new Array(nk);
+ [C[0], S[0]] = this.FresnelCS(t);
+ if (nk > 1) {
+ var tt = Math.PI / 2 * t * t;
+ var ss = Math.sin(tt);
+ var cc = Math.cos(tt);
+ C[1] = ss / Math.PI;
+ S[1] = (1 - cc) / Math.PI;
+ if (nk > 2) {
+ C[2] = (t * ss - S[0]) / Math.PI;
+ S[2] = (C[0] - t * cc) / Math.PI;
+ }
+ }
+ return [C,S]
+ },
+ evalXYaLarge: function (nk, a, b) {
+ var X = new Array(nk);
+ var Y = new Array(nk);
+ var s = Math.sign(a);
+ var z = Math.sqrt(Math.abs(a) / Math.PI);
+ var ell = s * b / Math.sqrt(Math.abs(a) * Math.PI);
+ var g = -0.5 * s * b *b / Math.abs(a);
+ [Cl, Sl] = this.FresnelCSk(nk, ell);
+ [Cz, Sz] = this.FresnelCSk(nk, ell + z);
+ var dC =new Array(Cz.length); /*= Cz - Cl;*/for(var i=0;i 1) {
+ cg = cg / z;
+ sg = sg / z;
+ DC = dC[1] - ell * dC[0];
+ DS = dS[1] - ell * dS[0];
+ X[1] = cg * DC - s * sg * DS;
+ Y[1] = sg * DC + s * cg * DS;
+ if (nk > 2) {
+ cg = cg / z;
+ sg = sg / z;
+ DC = dC[2] + ell * (ell * dC[0] - 2 * dC[1]);
+ DS = dS[2] + ell * (ell * dS[0] - 2 * dS[1]);
+ X[2] = cg * DC - s * sg * DS;
+ Y[2] = sg * DC + s * cg * DS;
+ }
+ }
+ return [X, Y];
+ },
+
+ rLommel: function (mu, nu, b) {
+ var tmp = 1 / ((mu + nu + 1) * (mu - nu + 1));
+ var res = tmp;
+ for (var n = 0; n < 100; n++) {
+ tmp = tmp * (-b / (2 * n + mu - nu + 1)) * (b / (2 * n + mu + nu + 1));
+ res = res + tmp;
+ if (Math.abs(tmp) < Math.abs(res) * 1e-50)
+ break;
+ }
+ return res;
+ },
+ evalXYazero: function (nk, b) {
+ var X = new Array(nk);
+ var Y = new Array(nk);
+ var sb = Math.sin(b);
+ var cb = Math.cos(b);
+ var b2 = b * b;
+ if (Math.abs(b) < 1e-3) {
+ X[0] = 1 - (b2 / 6) * (1 - (b2 / 20) * (1 - (b2 / 42)));
+ Y[0] = (b / 2) * (1 - (b2 / 12) * (1 - (b2 / 30)));
+ }
+ else {
+ X[0] = sb / b;
+ Y[0] = (1 - cb) / b;
+ }
+ // use recurrence in the stable part
+ var m = Math.min(Math.max(1, Math.floor(2 * b)), nk);
+ for (var k = 1; k <= m - 1; k++) {
+ X[k] = (sb - k * Y[k-1]) / b;
+ Y[k] = (k * X[k-1] - cb) / b;
+ }
+ // use Lommel for the unstable part
+ if (m < nk) {
+ var A = b * sb;
+ var D = sb - b * cb;
+ var B = b * D;
+ var C = -b2 * sb;
+ var rLa = this.rLommel(m + 1 / 2, 3 / 2, b);
+ var rLd = this.rLommel(m + 1 / 2, 1 / 2, b);
+ for (var k = m; k <= nk - 1; k++) {
+ var rLb = this.rLommel(k + 3 / 2, 1 / 2, b);
+ var rLc = this.rLommel(k + 3 / 2, 3 / 2, b);
+ X[k] = (k * A * rLa + B * rLb + cb) / (1 + k);
+ Y[k] = (C * rLc + sb) / (2 + k) + D * rLd;
+ rLa = rLc;
+ rLd = rLb;
+ }
+ }
+ return [X, Y];
+ },
+ evalXYaSmall: function (nk, a, b, p) {
+ [X0, Y0] = this.evalXYazero(nk + 4 * p + 2, b);
+
+ var X = new Array(nk);
+ var Y = new Array(nk);
+ var tmpX = new Array(p + 1);
+ var tmpY = new Array(p + 1);
+ var sum=function(arr) {
+ var sumation=0.0;
+ for(var i=0;i eps) {
+ twofn = twofn + 2.0;
+ fact = fact * twofn * (twofn - 1.0);
+ denterm = denterm + 4.0;
+ numterm = numterm * t;
+ term = numterm / (fact * denterm);
+ sum = sum + term;
+ ratio = Math.abs(term / sum);
+ }
+
+ FresnelC = x * sum;
+
+ // Sine integral series
+ twofn = 1.0;
+ fact = 1.0;
+ denterm = 3.0;
+ numterm = 1.0;
+ sum = 1.0 / 3.0;
+ ratio = 10.0;
+
+ while (ratio > eps) {
+ twofn = twofn + 2.0;
+ fact = fact * twofn * (twofn - 1.0);
+ denterm = denterm + 4.0;
+ numterm = numterm * t;
+ term = numterm / (fact * denterm);
+ sum = sum + term;
+ ratio = Math.abs(term / sum);
+ }
+
+ FresnelS = (Math.PI / 2) * sum * x * x * x;
+ }
+ else if (x < 6.0) {
+ // Rational approximation for f
+ var sumn = 0.0;
+ var sumd = fd[11];
+ for (var k = 10; k >= 0; k--) {
+ sumn = fn[k] + x * sumn;
+ sumd = fd[k] + x * sumd;
+ }
+ var f = sumn / sumd;
+ // Rational approximation for g
+ sumn = 0.0;
+ sumd = gd[11];
+ for (var k = 10; k >= 0; k--) {
+ sumn = gn[k] + x * sumn;
+ sumd = gd[k] + x * sumd;
+ }
+ var g = sumn / sumd;
+ var U = (Math.PI / 2) * x * x;
+ var SinU = Math.sin(U);
+ var CosU = Math.cos(U);
+ FresnelC = 0.5 + f * SinU - g * CosU;
+ FresnelS = 0.5 - f * CosU - g * SinU;
+ }
+ else {
+ // x >= 6; asymptotic expansions for f and g
+ t = -Math.pow(Math.PI * x * x, -2.0);
+ // Expansion for f
+ var numterm = -1.0;
+ var term = 1.0;
+ var sum = 1.0;
+ var oldterm = 1.0;
+ var ratio = 10.0;
+ var eps10 = 0.1 * eps;
+
+ while (ratio > eps10) {
+ numterm = numterm + 4.0;
+ term = term * numterm * (numterm - 2.0) * t;
+ sum = sum + term;
+ absterm = Math.abs(term);
+ ratio = Math.abs(term / sum);
+ if (oldterm < absterm) {
+ console.log('\n\n !!In FresnelCS f not converged to eps');
+ ratio = eps10;
+ }
+ oldterm = absterm;
+ }
+
+ var f = sum / (Math.PI * x);
+ // Expansion for g
+ numterm = -1.0;
+ term = 1.0;
+ sum = 1.0;
+ oldterm = 1.0;
+ ratio = 10.0;
+ eps10 = 0.1 * eps;
+
+ while (ratio > eps10) {
+ numterm = numterm + 4.0;
+ term = term * numterm * (numterm + 2.0) * t;
+ sum = sum + term;
+ absterm = Math.abs(term);
+ ratio = Math.abs(term / sum);
+ if (oldterm < absterm) {
+ console.log('\n\n!!In FresnelCS g not converged to eps');
+ ratio = eps10;
+ }
+ oldterm = absterm;
+ }
+ var g = sum / ((Math.PI * x) * (Math.PI * x) * x);
+ var U = (Math.PI / 2) * x * x;
+ var SinU = Math.sin(U);
+ var CosU = Math.cos(U);
+ FresnelC = 0.5 + f * SinU - g * CosU;
+ FresnelS = 0.5 - f * CosU - g * SinU;
+ }
+ if (y < 0) {
+ FresnelC = -FresnelC;
+ FresnelS = -FresnelS;
+ }
+ return [FresnelC, FresnelS];
+ }
+}
+
+module.exports = SPIRAL
\ No newline at end of file
diff --git a/src/app/core/shapes/spline-config.ts b/src/app/core/shapes/spline-config.ts
new file mode 100644
index 00000000..7c31f056
--- /dev/null
+++ b/src/app/core/shapes/spline-config.ts
@@ -0,0 +1,11 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+export const MAX_CTRL_POINTS = 100;
+export const ARC_TESSEL = 24;
+export const CURVE_TESSEL = 48;
+export const CURVE_Y = 0;
+export const ARC_SEGMENTS = 2;
+export const PARACUBICFACTOR = 3.5;
+export const TANGENT_HANDLE_LENGTH = 7;
diff --git a/src/app/core/time.ts b/src/app/core/time.ts
new file mode 100644
index 00000000..0205a698
--- /dev/null
+++ b/src/app/core/time.ts
@@ -0,0 +1,63 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+export class Time {
+
+ /**
+ * Time passed in simulation in milliseconds or ms
+ */
+ static time: number = 0;
+
+ /**
+ * Fixed time-step for physics/simulation update
+ * in millisecond
+ * 20ms = 50 updates per second
+ */
+ static fixedDeltaTime: number = 20;
+
+ /**
+ * Time since last graphic update
+ */
+ static deltaTime: number = 0;
+
+ /**
+ * Time since last graphic update in ms
+ */
+ static deltaTimeInMs: number = 0;
+
+ /**
+ * Graphics frames count
+ */
+ static frameCount: number = 0;
+
+ static realTimeSinceStart: number = 0;
+
+ /**
+ * Returns rounded second
+ */
+ static get seconds () {
+
+ return Math.round( Time.time * 0.001 );
+
+ }
+
+ /**
+ * Returns time in seconds without rounding
+ */
+ static get inSeconds () {
+
+ return Time.time * 0.001;
+
+ }
+
+ static reset () {
+
+ this.deltaTime = 0;
+ this.deltaTimeInMs = 0;
+ this.time = 0;
+ this.frameCount = 0;
+ this.realTimeSinceStart = 0;
+
+ }
+}
diff --git a/src/app/core/tools/_road-plan-arc-tool.ts b/src/app/core/tools/_road-plan-arc-tool.ts
new file mode 100644
index 00000000..970ebcb0
--- /dev/null
+++ b/src/app/core/tools/_road-plan-arc-tool.ts
@@ -0,0 +1,83 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+// import { RoadPlanTool } from './road-plan-tool';
+// import { Points } from 'three';
+// import { OdAbstractRoadGeometry } from '../../modules/tv-map/models/geometries/od-abstract-road-geometry';
+// import { ControlPoint } from 'app/modules/three-js/objects/control-point';
+// import { OdArcGeometry } from 'app/modules/tv-map/models/geometries/od-arc-geometry';
+// import { Maths } from 'app/utils/maths';
+
+// export class RoadPlanArcTool extends RoadPlanTool {
+
+// private cps: ControlPoint[] = [];
+
+// protected onControlPointSelected ( e: ControlPoint ) {
+
+// try {
+
+// const road = this.openDrive.getRoadById( e.userData.roadId );
+
+// if ( road == null ) return;
+
+// this.selectRoad( road );
+
+// // this.cps.push( e );
+
+// } catch ( e ) {
+
+// // console.error( e );
+
+// }
+
+// }
+
+// protected onControlPointAdded ( e: ControlPoint ) {
+
+// this.cps.push( e );
+
+// if ( this.cps.length % 3 === 0 ) {
+
+// const count = this.cps.length;
+// const points = this.cps;
+// const cp1 = points[ count - 3 ];
+// const cp2 = points[ count - 2 ];
+// const cp3 = points[ count - 1 ];
+
+// const road = this.createRoad();
+
+// this.addDefaultLanes( road );
+
+// this.helper.createArcGeometry( cp1, cp2, cp3, road );
+
+// this.selectRoad( road );
+
+// this.cps.splice( 0, this.cps.length );
+// }
+// }
+
+// protected onControlPointMoved ( e: ControlPoint ) {
+
+// try {
+
+// const road = this.openDrive.getRoadById( e.userData.roadId );
+
+// if ( road == null ) return;
+
+// const geometry = e.userData.geometry as OdAbstractRoadGeometry;
+
+// const index = e.userData.index;
+
+// this.helper.updateRoadGeometry( e, road, geometry, index );
+
+// const arc = geometry as OdArcGeometry;
+
+// } catch ( e ) {
+
+// // console.error( e );
+
+// }
+
+// }
+// }
\ No newline at end of file
diff --git a/src/app/core/tools/_road-plan-line-tool.ts b/src/app/core/tools/_road-plan-line-tool.ts
new file mode 100644
index 00000000..76cfad3e
--- /dev/null
+++ b/src/app/core/tools/_road-plan-line-tool.ts
@@ -0,0 +1,106 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+// import { RoadPlanTool } from './road-plan-tool';
+// import { Points } from 'three';
+// import { OdAbstractRoadGeometry } from '../../modules/tv-map/models/geometries/od-abstract-road-geometry';
+// import { MultiCmdsCommand } from 'app/modules/tv-map/commands/multi-cmds-command';
+// import { AddRoadCommand } from 'app/modules/tv-map/commands/add-road-command';
+// import { AddRoadGeometryCommand } from 'app/modules/tv-map/commands/add-road-geometry-command';
+// import { Geometry } from 'ngx-perfect-scrollbar';
+// import { CommandHistory } from 'app/services/command-history';
+// import { OdBuilder } from 'app/modules/tv-map/builders/od-builder.service';
+
+// export class RoadPlanLineTool extends RoadPlanTool {
+
+// private cps: Points[] = [];
+
+// protected onControlPointSelected ( e: any ) {
+
+// try {
+
+// const road = this.openDrive.getRoadById( e.userData.roadId );
+
+// if ( road == null ) return;
+
+// this.selectRoad( road );
+
+// } catch ( e ) {
+
+// // console.error( e );
+
+// }
+
+// }
+
+// protected onControlPointAdded ( e: Points ) {
+
+// this.cps.push( e );
+
+// // if ( this.cps.length % 2 === 0 ) {
+
+// // const count = this.cps.length;
+// // const points = this.cps;
+// // const cp1 = points[ count - 2 ];
+// // const cp2 = points[ count - 1 ];
+
+// // const road = this.createRoad();
+
+// // this.addDefaultLanes( road );
+
+// // const geometry = this.helper.createLineGeometry( cp1, cp2, road );
+
+// // const addRoad = new AddRoadCommand( road );
+// // const addRoadGeom = new AddRoadGeometryCommand( road, geometry );
+
+// // const multiCmd = new MultiCmdsCommand( [ addRoad, addRoadGeom ] );
+
+// // CommandHistory.execute( multiCmd );
+
+// // this.selectRoad( road );
+
+// // this.cps.splice( 0, count );
+// // }
+
+// // if ( this.shapeEditor.controlPointCount % 3 === 0 ) {
+// //
+// // const count = this.shapeEditor.controlPointCount;
+// // const points = this.shapeEditor.controlPoints;
+// // const cp1 = points[ count - 3 ];
+// // const cp2 = points[ count - 2 ];
+// // const cp3 = points[ count - 1 ];
+// //
+// // const road = this.createRoad();
+// //
+// // this.addDefaultLanes( road );
+// //
+// // this.helper.createArcGeometry( cp1, cp2, cp3, road );
+// //
+// // this.selectRoad( road );
+// //
+// // }
+// }
+
+// protected onControlPointMoved ( e: Points ) {
+
+// try {
+
+// const road = this.openDrive.getRoadById( e.userData.roadId );
+
+// if ( road == null ) return;
+
+// const geometry = e.userData.geometry as OdAbstractRoadGeometry;
+
+// const index = e.userData.index;
+
+// this.helper.updateRoadGeometry( e, road, geometry, index );
+
+// } catch ( e ) {
+
+// // console.error( e );
+
+// }
+
+// }
+// }
\ No newline at end of file
diff --git a/src/app/core/tools/_road-plan-spline-tool.ts b/src/app/core/tools/_road-plan-spline-tool.ts
new file mode 100644
index 00000000..454668f0
--- /dev/null
+++ b/src/app/core/tools/_road-plan-spline-tool.ts
@@ -0,0 +1,290 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+// import { RoadPlanTool } from './road-plan-tool';
+// import { Points, Vector3, Vector2, Line, Line3, ArrowHelper } from 'three';
+// import { OdAbstractRoadGeometry } from '../../modules/tv-map/models/geometries/od-abstract-road-geometry';
+// import { ControlPoint } from 'app/modules/three-js/objects/control-point';
+// import { OdArcGeometry } from 'app/modules/tv-map/models/geometries/od-arc-geometry';
+// import { Maths } from 'app/utils/maths';
+// import { SplineCurveEditor } from '../editors/spline-curve-editor';
+// import { OdRoadReferenceLineBuilder } from 'app/modules/tv-map/builders/od-road-reference-line-builder';
+// import { OdSides } from 'app/modules/tv-map/models/od-common.model';
+// import { GameObject } from '../game-object';
+// import { OdLaneReferenceLineBuilder } from 'app/modules/tv-map/builders/od-lane-reference-line-builder';
+// import { SceneService } from '../services/scene.service';
+// import { OdLineGeometry } from 'app/modules/tv-map/models/geometries/od-line-geometry';
+// import { OdRoad } from 'app/modules/tv-map/models/od-road.model';
+// import { OdPosTheta } from 'app/modules/tv-map/models/od-pos-theta';
+
+// export class RoadPlanSplineTool extends RoadPlanTool {
+
+// private cps: ControlPoint[] = [];
+
+// private builder: OdRoadReferenceLineBuilder;
+// private laneBuilder: OdLaneReferenceLineBuilder;
+
+// init () {
+
+// super.init();
+
+// // this.shapeEditor = new LineEditor(10);
+// this.shapeEditor = new SplineCurveEditor();
+// // this.shapeEditor = new CubicBezierCurveEditor();
+// // this.shapeEditor = new PolygonEditor();
+
+// this.helper.createRoadControlPoints( this.openDrive );
+
+// this.builder = new OdRoadReferenceLineBuilder( null );
+// this.laneBuilder = new OdLaneReferenceLineBuilder( null );
+
+// }
+
+// protected onControlPointSelected ( e: ControlPoint ) {
+
+// // try {
+
+// // const road = this.openDrive.getRoadById( e.userData.roadId );
+
+// // if ( road == null ) return;
+
+// // this.selectRoad( road );
+
+// // // this.cps.push( e );
+
+// // } catch ( e ) {
+
+// // // console.error( e );
+
+// // }
+
+// }
+
+// protected onControlPointAdded ( e: ControlPoint ) {
+
+// this.cps.push( e );
+
+// if ( this.cps.length % 3 === 0 ) {
+
+// const count = this.cps.length;
+// const points = this.cps;
+// const cp1 = points[ count - 3 ];
+// const cp2 = points[ count - 2 ];
+// const cp3 = points[ count - 1 ];
+
+// const road = this.createRoad();
+
+// road.gameObject = new GameObject();
+
+// SceneService.add( road.gameObject );
+
+// this.addDefaultLanes( road );
+
+// this.helper.createArcGeometry( cp1, cp2, cp3, road );
+
+// // this.selectRoad( road );
+
+// // this.cps.splice( 0, this.cps.length );
+// }
+// }
+
+// protected onControlPointMoved ( e: ControlPoint ) {
+
+// this.printAngles();
+
+// // try {
+
+// // const road = this.openDrive.getRoadById( e.userData.roadId );
+
+// // if ( road == null ) return;
+
+// // const geometry = e.userData.geometry as OdAbstractRoadGeometry;
+
+// // const index = e.userData.index;
+
+// // this.helper.updateRoadGeometry( e, road, geometry, index );
+
+// // this.printAngles();
+
+// // } catch ( e ) {
+
+// // console.error( e );
+
+// // }
+
+// }
+
+// private printAngles () {
+
+// if ( this.cps.length != 3 ) return;
+
+// const road = this.openDrive.getRoadById( 1 );
+
+// // remove previous geometries
+// road.geometries.splice( 0, road.geometries.length );
+
+// const p1 = this.cps[ 0 ].position;
+// const p2 = this.cps[ 1 ].position;
+// const p3 = this.cps[ 2 ].position;
+
+// const x = p1.x;
+// const y = p1.y;
+
+// // const a = Maths.angle( p1, p2, p3 ) * Maths.Rad2Deg;
+// // const b = Maths.angle( p2, p3, p1 ) * Maths.Rad2Deg; /// actual angle of interest
+// // const c = Maths.angle( p3, p1, p2 ) * Maths.Rad2Deg;
+
+// const firstLineLength = p1.distanceTo( p2 );
+// const secondLineLength = p2.distanceTo( p3 );
+
+// // hdg of first line segment
+// const hdg = Math.atan2( p2.y - p1.y, p2.x - p1.x );
+
+// // hdg of second line segment
+// const hdg2 = Math.atan2( p3.y - p2.y, p3.x - p2.x );
+
+
+// let arcStartingPosition: Vector3;
+
+// // line + arc
+// if ( firstLineLength >= secondLineLength ) {
+
+// const t = ( firstLineLength - secondLineLength ) / firstLineLength;
+
+// const lineLength = firstLineLength - secondLineLength;
+
+// arcStartingPosition = Maths.linearInterpolationVector3( p1, p2, t );
+
+// const lineGeometry = road.planView.addGeometryLine( 0, x, y, hdg, lineLength ) as OdLineGeometry;
+
+// const end = lineGeometry.end;
+
+// this.createArcGeometryRight( lineGeometry.s2, end.x, end.y, arcStartingPosition, hdg, p3, hdg2, p2, road );
+
+// }
+// // only arc is required
+// else if ( secondLineLength > firstLineLength ) {
+
+// const t = firstLineLength / secondLineLength;
+
+// const lineLength = secondLineLength - firstLineLength;
+
+// const lineStartingPosition = Maths.linearInterpolationVector3( p2, p3, t );
+
+// const arcGeometry = this.createArcGeometryRight( 0, x, y, p1, hdg, lineStartingPosition, hdg2, p2, road );
+
+// const lineS = arcGeometry.s2;
+// const pos = new OdPosTheta();
+
+// arcGeometry.getCoords( lineS, pos );
+
+// const lineGeometry = road.planView.addGeometryLine( lineS, pos.x, pos.y, pos.hdg, lineLength );
+
+// }
+
+// // const geometry = road.geometries[ 0 ] as OdArcGeometry;
+
+
+// // const height = Maths.heightOfTriangle( p2, p3, p1 );
+// // const base = p2.distanceTo( p1 );
+// // const radius = ( height / 2 ) + ( ( base * base ) / ( 8 * height ) );
+// // let curvature = 1 / radius;
+// // make the curvature negative for right side i.e. for clockwise
+// // if ( Maths.direction( p2, p3, p1 ) === OdSides.RIGHT ) curvature *= -1;
+
+// // console.log( 180 - b, radius, curvature, firstHalfDistance, secondHalfDistance, d );
+
+// // geometry.curvature = curvature;
+
+// this.builder.clear( this.openDrive.getRoadById( 1 ) );
+// this.builder.buildRoad( this.openDrive.getRoadById( 1 ) );
+// }
+
+// private createArcGeometryRight (
+// s: number, x: number, y: number, p1: Vector3, hdg: number, p3: Vector3, hdg2: number, p2: Vector3, road: OdRoad ) {
+
+// const res = this.getRadius( p1, hdg, p3, hdg2 );
+// const radius = res.radius;
+// const center = res.center;
+
+// let curvature = 1 / radius;
+
+// const doCurvature = Maths.angle( center, p1, p3 );
+
+// const arcLength = doCurvature * radius;
+
+// // make the curvature negative for right side i.e. for clockwise
+// if ( Maths.direction( p2, p3, p1 ) === OdSides.RIGHT )
+// curvature *= -1;
+
+// const geometry = road.planView.addGeometryArc( s, x, y, hdg, arcLength, curvature );
+
+// road.length = s + arcLength;
+
+// return geometry;
+// }
+
+// private createArcGeometryLeft ( s: number, x: number, y: number, end: Vector3, hdg: number, p3: Vector3, hdg2: number, p2: Vector3, road: OdRoad ) {
+
+// const res = this.getRadius( end, hdg, p3, hdg2 );
+// const radius = res.radius;
+// const center = res.center;
+
+// let curvature = 1 / radius;
+
+// const doCurvature = Maths.angle( center, end, p3 );
+
+// const arcLength = doCurvature * radius;
+
+// // make the curvature negative for right side i.e. for clockwise
+// if ( Maths.direction( p2, p3, end ) === OdSides.RIGHT )
+// curvature *= -1;
+
+// const geometry = road.planView.addGeometryArc( s, x, y, hdg, arcLength, curvature );
+
+// road.length = s + arcLength;
+
+// return geometry;
+// }
+// private getRadius ( A: Vector3, line1Hdg: number, C: Vector3, line2Hdg: number ) {
+
+// // refPos.hdg - Maths.M_PI_2
+// // obj.gameObject.rotation.set( 0, 0, refPos.hdg - Maths.M_PI_2 );
+
+// const B = new Vector3(
+// A.x + Math.cos( line1Hdg + Maths.M_PI_2 ) * 1,
+// A.y + Math.sin( line1Hdg + Maths.M_PI_2 ) * 1
+// );
+
+// // const dir1 = new Vector3();
+// // this.createArrow( dir1.subVectors( line1P2, line1P1 ).normalize(), line1P1 );
+
+
+// const D = new Vector3(
+// C.x + Math.cos( line2Hdg + Maths.M_PI_2 ) * 1,
+// C.y + Math.sin( line2Hdg + Maths.M_PI_2 ) * 1
+// );
+
+// // const dir2 = new Vector3();
+// // this.createArrow( dir2.subVectors( line2P2, line2P1 ).normalize(), line2P2 );
+// // console.log( line1P1, line1P2, line1Hdg, line2P1, line2P2, line2Hdg );
+
+
+// const center = Maths.lineLineIntersection( A, B, C, D );
+
+// const radius = A.distanceTo( center );
+
+// // console.log( center, radius );
+
+// return {
+// radius,
+// center
+// };
+
+// }
+
+// private createArrow ( dir, origin ) {
+// // SceneService.add( new ArrowHelper( dir, origin, 10, 0xffff00 ) );
+// }
+// }
diff --git a/src/app/core/tools/add-road-object-tool.ts b/src/app/core/tools/add-road-object-tool.ts
new file mode 100644
index 00000000..41b51d1e
--- /dev/null
+++ b/src/app/core/tools/add-road-object-tool.ts
@@ -0,0 +1,137 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseTool } from './base-tool';
+import { BoxBufferGeometry, Mesh, MeshBasicMaterial, Object3D, TextureLoader } from 'three';
+import { AbstractShapeEditor } from '../editors/abstract-shape-editor';
+import { Subscription } from 'rxjs';
+import { PointEditor } from '../editors/point-editor';
+import { AddRoadObjectCommand } from '../commands/add-road-object-command';
+import { TvOrientation } from '../../modules/tv-map/models/tv-common';
+import { TvRoadObject } from '../../modules/tv-map/models/tv-road-object';
+import { CommandHistory } from '../../services/command-history';
+import { AnyControlPoint } from '../../modules/three-js/objects/control-point';
+import { TvMapQueries } from '../../modules/tv-map/queries/tv-map-queries';
+
+export class AddRoadObjectTool extends BaseTool {
+
+ name: string = 'RoadObject';
+
+ private roadObjects: Object3D[] = [];
+ private shapeEditor: AbstractShapeEditor;
+ private treeTexture = new TextureLoader().load( 'assets/top-down-tree.png' );
+ private subsribers: Subscription[] = [];
+
+ constructor ( private propSprite: string = 'assets/top-down-tree.png' ) {
+
+ super();
+
+ this.shapeEditor = new PointEditor();
+
+ }
+
+ init () {
+
+ super.init();
+
+ this.populate();
+
+ const s1 = this.shapeEditor.controlPointAdded.subscribe( e => this.onPointAdded( e ) );
+ const s2 = this.shapeEditor.controlPointMoved.subscribe( e => this.onPointMoved( e ) );
+
+ this.subsribers.push( s1 );
+ this.subsribers.push( s2 );
+ }
+
+ populate () {
+
+ this.openDrive.roads.forEach( road => {
+
+ const objects = road.getRoadObjects();
+
+ for ( let i = 0; i < objects.length; i++ ) {
+
+ this.roadObjects.push( objects[ i ].mesh );
+
+ const point = this.shapeEditor.addControlPoint( objects[ i ].mesh.position );
+
+ objects[ i ].mesh.userData.controlPointId = point.id;
+ }
+
+ } );
+
+ }
+
+ disable (): void {
+
+ super.disable();
+
+ this.shapeEditor.destroy();
+
+ this.subsribers.forEach( item => {
+ item.unsubscribe();
+ } );
+ }
+
+ private onPointAdded ( e: AnyControlPoint ) {
+
+ this.addProp( e );
+
+ }
+
+ private onPointMoved ( e: AnyControlPoint ) {
+
+ for ( const prop of this.roadObjects ) {
+
+ if ( prop.userData.controlPointId === e.id ) {
+
+ prop.position.set( e.position.x, e.position.y, prop.position.z );
+
+ break;
+
+ }
+
+ }
+
+ }
+
+ private getMaterial () {
+
+ return new MeshBasicMaterial( {
+ map: this.treeTexture,
+ transparent: true,
+ opacity: 0.9
+ } );
+
+ }
+
+ private addProp ( point: Object3D ) {
+
+ const road = TvMapQueries.getRoadByCoords( point.position.x, point.position.y );
+
+ if ( !road ) throw new Error( 'Need road to place objects' );
+
+ const material = this.getMaterial();
+
+ const geometry = new BoxBufferGeometry( 1, 1, 1 );
+
+ const obj = new Mesh( geometry, material );
+
+ obj.position.copy( point.position );
+
+ obj.userData.controlPointId = point.id;
+
+ this.roadObjects.push( obj );
+
+ // tslint:disable-next-line:max-line-length
+ // const roadObject = this.openDrive.getRoad( 0 ).addRoadObject( 'tree', 'tree', point.id, 0, 0, 0, 0, OdOrientations.NONE, null, null, null, null, null, null, null );
+
+ const roadObject = new TvRoadObject( 'tree', 'tree', point.id, 0, 0, 0, 0, TvOrientation.NONE );
+
+ roadObject.mesh = obj;
+
+ CommandHistory.execute( new AddRoadObjectCommand( road.id, roadObject, [ point ] ) );
+ }
+
+}
diff --git a/src/app/core/tools/auto-maneuver-tool.old-spec.ts b/src/app/core/tools/auto-maneuver-tool.old-spec.ts
new file mode 100644
index 00000000..ae22b140
--- /dev/null
+++ b/src/app/core/tools/auto-maneuver-tool.old-spec.ts
@@ -0,0 +1,847 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+// import { TestBed } from '@angular/core/testing';
+// import { OdRoad } from 'app/modules/tv-map/models/od-road.model';
+// import { LaneSide, OdLaneType, OdSides, OdContactPoints } from 'app/modules/tv-map/models/od-common.model';
+// import { Vector3, Vector2 } from 'three';
+// import { OpenDriveQueries } from 'app/modules/tv-map/queries/tv-map-queries';
+// import { OdSourceFile } from 'app/modules/tv-map/services/od-source-file';
+// import { OpenDrive } from 'app/modules/tv-map/models/tv-map.models';
+// import { Maths } from 'app/utils/maths';
+// import { OdPosTheta } from 'app/modules/tv-map/models/od-pos-theta';
+// import { ManeuverTool, IJunctionConnection } from './maneuver-tool';
+// import { OdLineGeometry } from 'app/modules/tv-map/models/geometries/od-line-geometry';
+// import { OdArcGeometry } from 'app/modules/tv-map/models/geometries/od-arc-geometry';
+// import { OpenDriveService } from 'app/modules/tv-map/services/tv-map.service';
+// import { OdWriter } from 'app/modules/tv-map/services/od-writer.service';
+
+// interface Geom {
+// road: number;
+// positions: Vector3[]; // x,y,z
+// }
+
+// describe( 'ManeuverTool Test', () => {
+
+// let tool: ManeuverTool;
+
+// beforeEach( () => {
+
+// tool = new ManeuverTool();
+
+// tool.init();
+
+// ManeuverTool.DOTCOUNT = 0;
+
+// } );
+
+// function findIntersectionsSlow ( roads: OdRoad[] ): Vector3[] {
+
+// const newRoads: Geom[] = [];
+
+// const t1 = performance.now();
+
+// for ( let i = 0; i < roads.length; i++ ) {
+
+// const road = roads[ i ];
+
+// const positions: Vector3[] = [];
+
+// const geom = {
+// road: road.id,
+// positions: positions,
+// }
+
+// const geometries = road.geometries;
+
+// for ( let g = 0; g < geometries.length; g++ ) {
+
+// const geometry = geometries[ g ];
+
+// const s = geometry.s;
+// const s2 = geometry.s2;
+
+// let posTheta = new OdPosTheta();
+
+// for ( let s = 0; s <= s2; s += 10 ) {
+
+// geometry.getCoords( s, posTheta );
+
+// positions.push( posTheta.toVector3() );
+
+// }
+// }
+
+// newRoads.push( geom );
+// }
+
+// const t2 = performance.now();
+
+// const timeToMakeGeometries = t2 - t1;
+
+// const intersections: Vector3[] = [];
+
+// for ( let i = 0; i < newRoads.length; i++ ) {
+
+// const positions = newRoads[ i ].positions;
+
+// for ( let j = i + 1; j < newRoads.length; j++ ) {
+
+// const otherPositions = newRoads[ j ].positions;
+
+// for ( let k = 0; k < otherPositions.length; k++ ) {
+
+// const otherPosition = otherPositions[ k ];
+
+// for ( let l = 0; l < positions.length; l++ ) {
+
+// const position = positions[ l ];
+// const distance = position.distanceTo( otherPosition );
+
+// if ( distance < 9.9 ) {
+
+// intersections.push( position );
+// }
+// }
+// }
+// }
+// }
+
+// const t3 = performance.now();
+
+// const timeToFindIntersections = t3 - t2;
+
+// return intersections;
+// }
+
+// function findRoadIntersections ( roads: OdRoad[] ) {
+
+// const positions: [][] = [];
+
+// roads.forEach( road => {
+
+// const otherRoads = roads.filter( r => r.id != road.id && !road.isJunction );
+
+// const roadGeometries = road.geometries;
+
+// otherRoads.forEach( otherRoad => {
+
+// const otherRoadGeometries = otherRoad.geometries;
+
+// roadGeometries.forEach( g => {
+
+// otherRoadGeometries.forEach( og => {
+
+// // g.getIntersections( og );
+
+// const s = og.s;
+// const s2 = og.s2;
+
+// let position = null;
+// let posTheta = new OdPosTheta();
+
+// for ( let s = 0; s <= s2; s++ ) {
+
+// position = og.getCoords( s, posTheta );
+
+// positions[ road.id ]
+
+// }
+// } );
+
+// } )
+
+// } );
+// } );
+// }
+
+// function getLineArcIntersections ( A: Vector3, B: Vector3, C: Vector3, R: number ): Vector3[] {
+
+// // https://revisionmaths.com/advanced-level-maths-revision/pure-maths/geometry/equation-circle
+// // https://stackoverflow.com/a/1088058
+// // equation of circle with center (a,b) with radius r is
+// //
+// // (x-a)^2 + (y-b)^2 = r^2
+// //
+
+// // line segment
+// // const A = new Vector3( 0, 0, 0 );
+// // const B = new Vector3( 0, 150, 0 );
+// // const slope = Maths.slope( A, B );
+
+// // center of circle
+// // const C = new Vector3( 0, 0, 0 );
+// // const R = 100;
+
+// const LAB = A.distanceTo( B );
+
+// const D = new Vector3(
+// ( B.x - A.x ) / LAB,
+// ( B.y - A.y ) / LAB,
+// 0
+// );
+
+// // compute the distance between the points A and E, where
+// // E is the point of AB closest the circle center (Cx, Cy)
+// const t = D.x * ( C.x - A.x ) + D.y * ( C.y - A.y )
+
+// const E = new Vector3(
+// t * D.x + A.y,
+// t * D.y + A.y,
+// 0
+// );
+
+// const LEC = E.distanceTo( C );
+
+// // line intersects
+// if ( LEC < R ) {
+
+// // compute distance from t to circle intersection point
+// const dt = Math.sqrt( R * R - LEC * LEC );
+
+// // compute first intersection point
+// const F = new Vector3(
+// ( t - dt ) * D.x + A.x,
+// ( t - dt ) * D.y + A.y,
+// 0
+// );
+
+// // compute second intersection point
+// const G = new Vector3(
+// ( t + dt ) * D.x + A.x,
+// ( t + dt ) * D.y + A.y,
+// 0
+// );
+
+// const intersections = [];
+
+// const F_online = Maths.isPointOnLine( A, B, F );
+
+// if ( F_online ) intersections.push( F );
+
+// const G_online = Maths.isPointOnLine( A, B, G );
+
+// if ( G_online ) intersections.push( G );
+
+// return intersections;
+
+// // const isXOnLine = Maths.isPointOnLine( A, B, new Vector3( 0, 10, 0 ) );
+
+// } else if ( Maths.approxEquals( LEC, R ) ) {
+
+// // TODO: Return tanget also in future
+// // tangent point to circle is E
+
+// return [];
+
+// } else {
+
+// // line does not intersect
+// return [];
+
+// }
+// }
+
+// it( 'should check line and line intersection', () => {
+
+// create2x2LaneRoadIntersection( 10000 );
+
+// const intersections = findIntersectionsSlow( [ ...OdSourceFile.openDrive.roads.values() ] );
+
+// expect( intersections.length ).toBe( 1 );
+
+// } );
+
+// it( 'should give left side for point direction checks', () => {
+
+// // TODO: Move this to maths.spec.ts
+
+// let side: OdSides;
+
+// side = Maths.findSide( new Vector3( 0, 10, 0 ), new Vector3( 0, 0, 0 ), 0 );
+
+// expect( side ).toBe( OdSides.LEFT );
+
+// side = Maths.findSide( new Vector3( 0, 10, 0 ), new Vector3( 0, 0, 0 ), -0.78 ); // -45 degree
+
+// expect( side ).toBe( OdSides.LEFT );
+
+// side = Maths.findSide( new Vector3( 0, 10, 0 ), new Vector3( 0, 0, 0 ), 0.78 ); // +45 degree
+
+// expect( side ).toBe( OdSides.LEFT );
+
+// side = Maths.findSide( new Vector3( 10, 10, 0 ), new Vector3( 0, 0, 0 ), -1.57 ); // -90 degree
+
+// expect( side ).toBe( OdSides.LEFT );
+
+// side = Maths.findSide( new Vector3( 10, 0, 0 ), new Vector3( 0, 0, 0 ), -1.57 ); // -90 degree
+
+// expect( side ).toBe( OdSides.LEFT );
+
+// side = Maths.findSide( new Vector3( 10, 20, 0 ), new Vector3( 0, 0, 0 ), -1.57 ); // -90 degree
+
+// expect( side ).toBe( OdSides.LEFT );
+
+// } );
+
+// it( 'should give right side for point direction checks', () => {
+
+// // TODO: Move this to maths.spec.ts
+
+// let side: OdSides;
+
+// // +90 degree
+// expect( Maths.findSide( new Vector3( 10, 0, 0 ), new Vector3( 0, 0, 0 ), 1.57 ) )
+// .toBe( OdSides.RIGHT );
+
+// expect( Maths.findSide( new Vector3( 10, 10, 0 ), new Vector3( 0, 0, 0 ), 1.57 ) )
+// .toBe( OdSides.RIGHT );
+
+// expect( Maths.findSide( new Vector3( 100, 100, 0 ), new Vector3( 0, 0, 0 ), 1.57 ) )
+// .toBe( OdSides.RIGHT );
+
+// expect( Maths.findSide( new Vector3( 1000, 1000, 0 ), new Vector3( 0, 0, 0 ), 1.57 ) )
+// .toBe( OdSides.RIGHT );
+
+// // +45 degree
+// expect( Maths.findSide( new Vector3( 10, 0, 0 ), new Vector3( 0, 0, 0 ), 0.78 ) )
+// .toBe( OdSides.RIGHT );
+
+// expect( Maths.findSide( new Vector3( 100, 0, 0 ), new Vector3( 0, 0, 0 ), 0.78 ) )
+// .toBe( OdSides.RIGHT );
+
+// } )
+
+// it( 'should create 8 dots for 2x2 lane intersections with same direction', () => {
+
+// const roads = create2x2LaneRoadIntersection();
+
+// const intesections = tool.findIntersectionsSlow( roads );
+
+// const junctions = tool.createJunctionAreas( intesections );
+
+// expect( intesections.length ).toBe( 1 );
+// expect( junctions.length ).toBe( 1 );
+
+// // TODO: assert junction[0] values also for correct s, start, end values
+
+// const dots = tool.createDotsForJunction( junctions[ 0 ] );
+
+// expect( dots.length ).toBe( 8 );
+// expect( dots[ 0 ].id ).toBe( 0 );
+// expect( dots[ 1 ].id ).toBe( 1 );
+// expect( dots[ 2 ].id ).toBe( 2 );
+// expect( dots[ 3 ].id ).toBe( 3 );
+// expect( dots[ 4 ].id ).toBe( 4 );
+// expect( dots[ 5 ].id ).toBe( 5 );
+// expect( dots[ 6 ].id ).toBe( 6 );
+// expect( dots[ 7 ].id ).toBe( 7 );
+
+// // A Left End > A Left Start
+// expect( dots[ 1 ].s ).toBeGreaterThan( dots[ 0 ].s );
+// // A Right Start > A Right End
+// expect( dots[ 2 ].s ).toBeGreaterThan( dots[ 3 ].s );
+
+// // B Left End > B Left Start
+// expect( dots[ 5 ].s ).toBeGreaterThan( dots[ 4 ].s );
+// // B Right Start > B Right End
+// expect( dots[ 6 ].s ).toBeGreaterThan( dots[ 7 ].s );
+
+// // TODO: test position
+// // TODO: test connections
+
+// const connections = ManeuverTool.createConnections( roads, dots );
+
+// const straightConnections = connections.filter( c => c.type == 'straight' );
+// const leftTurnConnections = connections.filter( c => c.type == "left-turn" );
+// const rightTurnConnections = connections.filter( c => c.type == "right-turn" );
+
+// expect( connections.length ).toBe( 12 );
+// expect( straightConnections.length ).toBe( 4 );
+// expect( leftTurnConnections.length ).toBe( 4 );
+// expect( rightTurnConnections.length ).toBe( 4 );
+
+// expect( straightConnections[ 0 ].incomingRoad ).toBe( 1 );
+// expect( straightConnections[ 0 ].outgoingRoad ).toBe( 1 );
+// expect( straightConnections[ 0 ].fromLane ).toBe( 1 );
+// expect( straightConnections[ 0 ].toLane ).toBe( 1 );
+// expect( straightConnections[ 0 ].point ).toBe( 'start' );
+
+// expect( straightConnections[ 1 ].incomingRoad ).toBe( 1 );
+// expect( straightConnections[ 1 ].outgoingRoad ).toBe( 1 );
+// expect( straightConnections[ 1 ].fromLane ).toBe( -1 );
+// expect( straightConnections[ 1 ].toLane ).toBe( -1 );
+// expect( straightConnections[ 1 ].point ).toBe( 'start' );
+
+// expect( straightConnections[ 2 ].incomingRoad ).toBe( 2 );
+// expect( straightConnections[ 2 ].outgoingRoad ).toBe( 2 );
+// expect( straightConnections[ 2 ].fromLane ).toBe( 1 );
+// expect( straightConnections[ 2 ].toLane ).toBe( 1 );
+// expect( straightConnections[ 2 ].point ).toBe( 'start' );
+
+// expect( straightConnections[ 3 ].incomingRoad ).toBe( 2 );
+// expect( straightConnections[ 3 ].outgoingRoad ).toBe( 2 );
+// expect( straightConnections[ 3 ].fromLane ).toBe( -1 );
+// expect( straightConnections[ 3 ].toLane ).toBe( -1 );
+// expect( straightConnections[ 3 ].point ).toBe( 'start' );
+
+// expect( leftTurnConnections[ 0 ].entry.id ).toBe( 0 );
+// expect( leftTurnConnections[ 0 ].exit.id ).toBe( 5 );
+// expect( leftTurnConnections[ 0 ].incomingRoad ).toBe( 1 );
+// expect( leftTurnConnections[ 0 ].outgoingRoad ).toBe( 2 );
+// expect( leftTurnConnections[ 0 ].fromLane ).toBe( 1 ); // forward
+// expect( leftTurnConnections[ 0 ].toLane ).toBe( 1 ); // forward
+// expect( leftTurnConnections[ 0 ].point ).toBe( 'start' );
+
+// expect( leftTurnConnections[ 1 ].entry.id ).toBe( 2 );
+// expect( leftTurnConnections[ 1 ].exit.id ).toBe( 7 );
+// expect( leftTurnConnections[ 1 ].incomingRoad ).toBe( 1 );
+// expect( leftTurnConnections[ 1 ].outgoingRoad ).toBe( 2 );
+// expect( leftTurnConnections[ 1 ].fromLane ).toBe( -1 ); // backward
+// expect( leftTurnConnections[ 1 ].toLane ).toBe( -1 ); // backward
+// expect( leftTurnConnections[ 1 ].point ).toBe( 'start' );
+
+// expect( leftTurnConnections[ 2 ].entry.id ).toBe( 4 );
+// expect( leftTurnConnections[ 2 ].exit.id ).toBe( 3 );
+// expect( leftTurnConnections[ 2 ].incomingRoad ).toBe( 2 );
+// expect( leftTurnConnections[ 2 ].outgoingRoad ).toBe( 1 );
+// expect( leftTurnConnections[ 2 ].fromLane ).toBe( 1 ); // forward
+// expect( leftTurnConnections[ 2 ].toLane ).toBe( -1 ); // backward
+// expect( leftTurnConnections[ 2 ].point ).toBe( 'start' );
+
+// expect( leftTurnConnections[ 3 ].entry.id ).toBe( 6 );
+// expect( leftTurnConnections[ 3 ].exit.id ).toBe( 1 );
+// expect( leftTurnConnections[ 3 ].incomingRoad ).toBe( 2 );
+// expect( leftTurnConnections[ 3 ].outgoingRoad ).toBe( 1 );
+// expect( leftTurnConnections[ 3 ].fromLane ).toBe( -1 ); // backward
+// expect( leftTurnConnections[ 3 ].toLane ).toBe( 1 ); // forward
+// expect( leftTurnConnections[ 3 ].point ).toBe( 'start' );
+
+// } );
+
+// it( 'should create 8 dots for 2x2 lane intersections with opposite direction', () => {
+
+// const roads = create2x2LaneRoadIntersection();
+
+// // make road go from top to down
+// roads[ 1 ].planView.geometries[ 0 ].x = 0;
+// roads[ 1 ].planView.geometries[ 0 ].y = 50;
+// roads[ 1 ].planView.geometries[ 0 ].hdg = -1.57;
+
+// const intesections = tool.findIntersectionsSlow( roads );
+
+// const junctions = tool.createJunctionAreas( intesections );
+
+// expect( intesections.length ).toBe( 1 );
+// expect( junctions.length ).toBe( 1 );
+
+// // TODO: assert junction[0] values also for correct s, start, end values
+
+// const dots = tool.createDotsForJunction( junctions[ 0 ] );
+
+// expect( dots.length ).toBe( 8 );
+// expect( dots[ 0 ].id ).toBe( 0 );
+// expect( dots[ 1 ].id ).toBe( 1 );
+// expect( dots[ 2 ].id ).toBe( 2 );
+// expect( dots[ 3 ].id ).toBe( 3 );
+// expect( dots[ 4 ].id ).toBe( 4 );
+// expect( dots[ 5 ].id ).toBe( 5 );
+// expect( dots[ 6 ].id ).toBe( 6 );
+// expect( dots[ 7 ].id ).toBe( 7 );
+
+// // A Left End > A Left Start
+// expect( dots[ 1 ].s ).toBeGreaterThan( dots[ 0 ].s );
+// // A Right Start > A Right End
+// expect( dots[ 2 ].s ).toBeGreaterThan( dots[ 3 ].s );
+
+// // B Left End > B Left Start
+// expect( dots[ 5 ].s ).toBeGreaterThan( dots[ 4 ].s );
+// // B Right Start > B Right End
+// expect( dots[ 6 ].s ).toBeGreaterThan( dots[ 7 ].s );
+
+// // TODO: test position
+// // TODO: test connections
+
+// const connections = ManeuverTool.createConnections( roads, dots );
+
+// const straightConnections = connections.filter( c => c.type == 'straight' );
+// const leftTurnConnections = connections.filter( c => c.type == "left-turn" );
+// const rightTurnConnections = connections.filter( c => c.type == "right-turn" );
+
+// expect( connections.length ).toBe( 12 );
+// expect( straightConnections.length ).toBe( 4 );
+// expect( leftTurnConnections.length ).toBe( 4 );
+// expect( rightTurnConnections.length ).toBe( 4 );
+
+// expect( straightConnections[ 0 ].incomingRoad ).toBe( 1 );
+// expect( straightConnections[ 0 ].outgoingRoad ).toBe( 1 );
+// expect( straightConnections[ 0 ].fromLane ).toBe( 1 );
+// expect( straightConnections[ 0 ].toLane ).toBe( 1 );
+// expect( straightConnections[ 0 ].point ).toBe( 'start' );
+
+// expect( straightConnections[ 1 ].incomingRoad ).toBe( 1 );
+// expect( straightConnections[ 1 ].outgoingRoad ).toBe( 1 );
+// expect( straightConnections[ 1 ].fromLane ).toBe( -1 );
+// expect( straightConnections[ 1 ].toLane ).toBe( -1 );
+// expect( straightConnections[ 1 ].point ).toBe( 'start' );
+
+// expect( straightConnections[ 2 ].incomingRoad ).toBe( 2 );
+// expect( straightConnections[ 2 ].outgoingRoad ).toBe( 2 );
+// expect( straightConnections[ 2 ].fromLane ).toBe( 1 );
+// expect( straightConnections[ 2 ].toLane ).toBe( 1 );
+// expect( straightConnections[ 2 ].point ).toBe( 'start' );
+
+// expect( straightConnections[ 3 ].incomingRoad ).toBe( 2 );
+// expect( straightConnections[ 3 ].outgoingRoad ).toBe( 2 );
+// expect( straightConnections[ 3 ].fromLane ).toBe( -1 );
+// expect( straightConnections[ 3 ].toLane ).toBe( -1 );
+// expect( straightConnections[ 3 ].point ).toBe( 'start' );
+
+// // below are not working
+
+// // expect( leftTurnConnections[ 0 ].entry.id ).toBe( 0 );
+// // expect( leftTurnConnections[ 0 ].exit.id ).toBe( 7 );
+// // expect( leftTurnConnections[ 0 ].incomingRoad ).toBe( 1 );
+// // expect( leftTurnConnections[ 0 ].outgoingRoad ).toBe( 2 );
+// // expect( leftTurnConnections[ 0 ].fromLane ).toBe( 1 ); // forward
+// // expect( leftTurnConnections[ 0 ].toLane ).toBe( 1 ); // forward
+// // expect( leftTurnConnections[ 0 ].point ).toBe( 'start' );
+
+// // expect( leftTurnConnections[ 1 ].entry.id ).toBe( 2 );
+// // expect( leftTurnConnections[ 1 ].exit.id ).toBe( 7 );
+// // expect( leftTurnConnections[ 1 ].incomingRoad ).toBe( 1 );
+// // expect( leftTurnConnections[ 1 ].outgoingRoad ).toBe( 2 );
+// // expect( leftTurnConnections[ 1 ].fromLane ).toBe( -1 ); // backward
+// // expect( leftTurnConnections[ 1 ].toLane ).toBe( -1 ); // backward
+// // expect( leftTurnConnections[ 1 ].point ).toBe( 'start' );
+
+// // expect( leftTurnConnections[ 2 ].entry.id ).toBe( 4 );
+// // expect( leftTurnConnections[ 2 ].exit.id ).toBe( 3 );
+// // expect( leftTurnConnections[ 2 ].incomingRoad ).toBe( 2 );
+// // expect( leftTurnConnections[ 2 ].outgoingRoad ).toBe( 1 );
+// // expect( leftTurnConnections[ 2 ].fromLane ).toBe( 1 ); // forward
+// // expect( leftTurnConnections[ 2 ].toLane ).toBe( -1 ); // backward
+// // expect( leftTurnConnections[ 2 ].point ).toBe( 'start' );
+
+// // expect( leftTurnConnections[ 3 ].entry.id ).toBe( 6 );
+// // expect( leftTurnConnections[ 3 ].exit.id ).toBe( 1 );
+// // expect( leftTurnConnections[ 3 ].incomingRoad ).toBe( 2 );
+// // expect( leftTurnConnections[ 3 ].outgoingRoad ).toBe( 1 );
+// // expect( leftTurnConnections[ 3 ].fromLane ).toBe( -1 ); // backward
+// // expect( leftTurnConnections[ 3 ].toLane ).toBe( 1 ); // forward
+// // expect( leftTurnConnections[ 3 ].point ).toBe( 'start' );
+
+// } );
+
+// it( 'should create 14 connections for 3x3 lane intersection', () => {
+
+// const roads = create3x3LaneRoadIntersection();
+
+// const intesections = tool.findIntersectionsSlow( roads );
+
+// const junctions = tool.createJunctionAreas( intesections );
+
+// expect( intesections.length ).toBe( 1 );
+// expect( junctions.length ).toBe( 1 );
+
+// const dots = tool.createDotsForJunction( junctions[ 0 ] );
+
+// const connections = tool.prepareJunctionConnections( junctions );
+
+// expect( dots.length ).toBe( 12 );
+
+// expect( connections.length ).toBe( 14 );
+
+// } );
+
+// it( 'should create 10 connections for 4 lane freeway intersection', () => {
+
+// // const roads = create4LaneFreewayIntersection();
+
+// // const intesections = tool.findIntersectionsSlow( roads );
+
+// // const junctions = ManeuverTool.createJunctionAreas( intesections );
+
+// // expect( intesections.length ).toBe( 1 );
+// // expect( junctions.length ).toBe( 1 );
+
+// // const dots = tool.createDotsForJunction( junctions[ 0 ] );
+
+// // const connections = tool.prepareJunctionConnections( junctions );
+
+// // expect( dots.length ).toBe( 16 );
+
+// // throwing error: 9 instead of 10
+// // expect( connections.length ).toBe( 10 );
+
+// } );
+
+// it( 'should not create junctions for same intersection again', () => {
+
+// const roads = create2x2LaneRoadIntersection();
+
+// const intesections = tool.findIntersectionsSlow( roads );
+
+// const junctions = tool.createJunctionAreas( intesections );
+
+// const connections = tool.prepareJunctionConnections( junctions );
+
+// // const straightConnections = connections.filter( c => c.type == 'straight' );
+// // const leftTurnConnections = connections.filter( c => c.type == "left-turn" );
+// // const rightTurnConnections = connections.filter( c => c.type == "right-turn" );
+
+// } );
+
+// it( 'should divide straight road correctly on intersection', () => {
+
+// // // TODO: this test it not working
+
+// // const sStart = 45;
+// // const sEnd = 55;
+
+// // const roads = create2x2LaneRoadIntersection( 100 );
+
+// // const spline = new AutoSpline();
+
+// // const connection: IJunctionConnection = {
+// // entry: {
+// // id: 0,
+// // s: sStart,
+// // roadId: 1,
+// // laneId: 1,
+// // type: 'start',
+// // color: 345,
+// // direction: 'forward',
+// // position: OpenDriveQueries.getLanePosition( 1, 1, sStart, 0 ),
+// // hdg: 0,
+// // },
+// // exit: {
+// // id: 0,
+// // s: 55,
+// // roadId: 1,
+// // laneId: 1,
+// // type: 'end',
+// // color: 345,
+// // direction: 'forward',
+// // position: OpenDriveQueries.getLanePosition( 1, 1, sEnd, 0 ),
+// // hdg: 0,
+// // },
+// // spline: spline
+// // };
+
+// // spline.addControlPointAt( connection.entry.position );
+// // spline.addControlPointAt( connection.exit.position );
+// // spline.update();
+
+// // const result = tool.createStraightJunctionRoad( 1, connection );
+
+// // expect( result.incoming.length ).toBe( 45 );
+// // expect( result.incoming.geometries[ 0 ].x ).toBe( -50 );
+// // expect( result.incoming.geometries[ 0 ].y ).toBe( 0 );
+// // expect( result.incoming.geometries[ 0 ].hdg ).toBe( 0 );
+
+// // expect( result.connecting.length ).toBe( 10 );
+// // expect( result.connecting.geometries[ 0 ].x ).toBe( -5 );
+// // expect( result.connecting.geometries[ 0 ].y ).toBe( 0 );
+// // expect( result.connecting.geometries[ 0 ].hdg ).toBe( 0 );
+
+// // expect( result.outgoing.length ).toBe( 45 );
+// // expect( result.connecting.geometries[ 0 ].x ).toBe( 5 );
+// // expect( result.connecting.geometries[ 0 ].y ).toBe( 0 );
+// // expect( result.connecting.geometries[ 0 ].hdg ).toBe( 0 );
+
+// } );
+
+// it( 'should find the correct geometry at s', () => {
+
+// const road = new OdRoad( "1", 500, 6, -1 );
+
+// road.addGeometryLine( 0, 0, 0, 0, 100 ); // 100
+// road.addGeometryLine( 100, 100, 0, 0, 100 ); // 200
+// road.addGeometryLine( 200, 200, 0, 0, 100 ); // 300
+// road.addGeometryLine( 300, 300, 0, 0, 100 );
+// road.addGeometryLine( 400, 400, 0, 0, 100 );
+
+// const s = 250;
+// const result = road.getGeometryAt( s );
+
+// expect( result.s ).toBe( 200 );
+// expect( result.s2 ).toBe( 300 );
+
+// } )
+
+// it( 'should find the correct geometry at s', () => {
+
+// const road = new OdRoad( "1", 500, 6, -1 );
+
+// road.addLaneSection( 0, false );
+// road.addLaneSection( 100, false );
+// road.addLaneSection( 200, false );
+// road.addLaneSection( 300, false );
+// road.addLaneSection( 400, false );
+
+// const s = 250;
+// const result = road.getLaneSectionAt( s );
+
+// expect( result.s ).toBe( 200 );
+
+// } )
+
+// it( 'should show imported junctions correctly', () => {
+
+// // create simple two road intersection for testing
+// // ============|||==================
+// const openDrive = OdSourceFile.openDrive = new OpenDrive();
+
+// const roadA = openDrive.addRoad( "A", 90, 1, -1 );
+// const roadB = openDrive.addRoad( "B", 90, 2, -1 );
+
+// roadA.addGeometryLine( 0, -100, 0, 0, 90 );
+// roadB.addGeometryLine( 0, 10, 0, 0, 90 );
+
+// [ roadA, roadB ].forEach( road => {
+
+// road.addLaneSection( 0, false ); const laneSection = road.getLastAddedLaneSection();
+
+// let leftLane = laneSection.addLane( LaneSide.LEFT, 1, OdLaneType.driving, true, true );
+// let centerLane = laneSection.addLane( LaneSide.CENTER, 0, OdLaneType.driving, true, true );
+// let rightLane = laneSection.addLane( LaneSide.RIGHT, -1, OdLaneType.driving, true, true );
+
+// [ leftLane, centerLane, rightLane ].forEach( lane => lane.addWidthRecord( 0, 2, 0, 0, 0 ) );
+
+// } )
+
+// roadA.setSuccessor( "junction", 1, null );
+// roadB.setPredecessor( "junction", 1, null );
+
+
+// // connecting roads for junction
+// const roadC = openDrive.addRoad( "C", 20, 3, 1 );
+// const roadD = openDrive.addRoad( "D", 20, 4, 1 );
+
+// roadC.setPredecessor( "road", 1, OdContactPoints.END );
+// roadC.setSuccessor( "road", 2, OdContactPoints.START );
+
+// roadD.setPredecessor( "road", 1, OdContactPoints.END );
+// roadD.setSuccessor( "road", 2, OdContactPoints.START );
+
+// roadC.addGeometryLine( 0, -10, 0, 0, 20 );
+// roadD.addGeometryLine( 0, -10, 0, 0, 20 );
+
+
+// roadC.addLaneSection( 0, false );
+// roadC.getLaneSectionAt( 0 )
+// .addLane( LaneSide.LEFT, 1, OdLaneType.driving, true, true )
+// .addWidthRecord( 0, 2, 0, 0, 0 );
+// roadC.getLaneSectionAt( 0 )
+// .addLane( LaneSide.CENTER, 0, OdLaneType.driving, true, true );
+
+// roadD.addLaneSection( 0, false );
+// roadD.getLaneSectionAt( 0 )
+// .addLane( LaneSide.RIGHT, -1, OdLaneType.driving, true, true )
+// .addWidthRecord( 0, 2, 0, 0, 0 );
+// roadD.getLaneSectionAt( 0 )
+// .addLane( LaneSide.CENTER, 0, OdLaneType.driving, true, true );
+
+// const junctionIndex = openDrive.addJunction( "J", 1 );
+// const junction = openDrive.getJunctionById( 1 );
+
+// // connections
+// junction.addJunctionConnection( 0, roadA.id, roadC.id, "start" ).addJunctionLaneLink( 1, 1 );
+// junction.addJunctionConnection( 1, roadB.id, roadD.id, "end" ).addJunctionLaneLink( -1, -1 );
+
+// //
+// tool.init();
+// tool.enable();
+
+// const output = new OdWriter().getOutput( openDrive );
+
+// } );
+
+// function create2x2LaneRoadIntersection ( length = 100 ) {
+
+// OdSourceFile.openDrive = new OpenDrive();
+
+// const road1 = OdSourceFile.openDrive.addRoad( '1', length, 1, -1 ); road1.addPlanView()
+// const road2 = OdSourceFile.openDrive.addRoad( '2', length, 2, -1 ); road2.addPlanView();
+
+// road1.addGeometryLine( 0, -50, 0, 0, length );
+// road2.addGeometryLine( 0, 0, -50, 1.57, length );
+
+// const roads = [ road1, road2 ];
+
+// roads.forEach( road => {
+
+// road.addLaneSection( 0, false ); const laneSection = road.getLastAddedLaneSection();
+
+// let leftLane = laneSection.addLane( LaneSide.LEFT, 1, OdLaneType.driving, true, true );
+// let centerLane = laneSection.addLane( LaneSide.CENTER, 0, OdLaneType.driving, true, true );
+// let rightLane = laneSection.addLane( LaneSide.RIGHT, -1, OdLaneType.driving, true, true );
+
+// [ leftLane, centerLane, rightLane ].forEach( lane => lane.addWidthRecord( 0, 2, 0, 0, 0 ) );
+
+// } );
+
+
+// return roads;
+// }
+
+// function create3x3LaneRoadIntersection () {
+
+// OdSourceFile.openDrive = new OpenDrive();
+
+// const road1 = OdSourceFile.openDrive.addRoad( '1', 100, 1, -1 ); road1.addPlanView()
+// const road2 = OdSourceFile.openDrive.addRoad( '2', 100, 2, -1 ); road2.addPlanView();
+
+// road1.addGeometryLine( 0, -50, 0, 0, 100 );
+// road2.addGeometryLine( 0, 0, -50, 1.57, 100 );
+
+// const roads = [ road1, road2 ];
+
+// roads.forEach( road => {
+
+// road.addLaneSection( 0, false ); const laneSection = road.getLastAddedLaneSection();
+
+// let left2 = laneSection.addLane( LaneSide.LEFT, 2, OdLaneType.driving, true, true );
+// let left1 = laneSection.addLane( LaneSide.LEFT, 1, OdLaneType.driving, true, true );
+// let center = laneSection.addLane( LaneSide.CENTER, 0, OdLaneType.driving, true, true );
+// let right1 = laneSection.addLane( LaneSide.RIGHT, -1, OdLaneType.driving, true, true );
+
+// [ left2, left1, center, right1 ].forEach( lane => lane.addWidthRecord( 0, 2, 0, 0, 0 ) );
+
+// } );
+
+// return roads;
+// }
+
+// function create4LaneFreewayIntersection () {
+
+// OdSourceFile.openDrive = new OpenDrive();
+
+// const road1 = OdSourceFile.openDrive.addRoad( '1', 100, 1, -1 ); road1.addPlanView()
+// const road2 = OdSourceFile.openDrive.addRoad( '2', 100, 2, -1 ); road2.addPlanView();
+
+// road1.addGeometryLine( 0, -50, 0, 0, 100 );
+// road2.addGeometryLine( 0, 0, -50, 1.57, 100 );
+
+// const roads = [ road1, road2 ];
+
+// roads.forEach( road => {
+
+// road.addLaneSection( 0, false ); const laneSection = road.getLastAddedLaneSection();
+
+// let left4 = laneSection.addLane( LaneSide.LEFT, 4, OdLaneType.driving, true, true );
+// let left3 = laneSection.addLane( LaneSide.LEFT, 3, OdLaneType.driving, true, true );
+// let left2 = laneSection.addLane( LaneSide.LEFT, 2, OdLaneType.driving, true, true );
+// let left1 = laneSection.addLane( LaneSide.LEFT, 1, OdLaneType.driving, true, true );
+// let center = laneSection.addLane( LaneSide.CENTER, 0, OdLaneType.driving, true, true );
+
+// [ left4, left3, left2, left1, center ].forEach( lane => lane.addWidthRecord( 0, 2, 0, 0, 0 ) );
+
+// } );
+
+// return roads;
+// }
+
+// } );
diff --git a/src/app/core/tools/auto-maneuver-tool.spec.ts b/src/app/core/tools/auto-maneuver-tool.spec.ts
new file mode 100644
index 00000000..f9029084
--- /dev/null
+++ b/src/app/core/tools/auto-maneuver-tool.spec.ts
@@ -0,0 +1,207 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { AutoManeuverTool } from './auto-maneuver-tool';
+import { TvMap } from 'app/modules/tv-map/models/tv-map.model';
+import { TvMapSourceFile } from 'app/modules/tv-map/services/tv-map-source-file';
+import { Maths } from 'app/utils/maths';
+import { TvDirection } from 'app/modules/tv-map/models/tv-common';
+
+
+describe( 'AutoManeuverTool Test', () => {
+
+ let tool: AutoManeuverTool;
+ let openDrive: TvMap;
+
+ beforeEach( () => {
+
+ tool = new AutoManeuverTool();
+
+ tool.init();
+
+ openDrive = TvMapSourceFile.openDrive = new TvMap();
+
+ AutoManeuverTool.DOTCOUNT = 0;
+
+ } );
+
+ it( 'should find straight road intersection in same direction', () => {
+
+ const roads = createStraightRoadsSameDirection();
+
+ const intersections = tool.findIntersectionsSlow( roads );
+
+ expect( intersections.length ).toBe( 1 );
+ expect( intersections[ 0 ].road1 ).toBe( roads[ 0 ].id );
+ expect( intersections[ 0 ].road2 ).toBe( roads[ 1 ].id );
+ expect( intersections[ 0 ].x ).toBe( 0 );
+ expect( intersections[ 0 ].y ).toBe( 0 );
+ expect( intersections[ 0 ].s1 ).toBe( 50 );
+ expect( intersections[ 0 ].s2 ).toBe( 50 );
+ } );
+
+ it( 'should find straight road intersection in opposite direction', () => {
+
+ const roads = createStraightRoadsOppositeDirection();
+
+ const intersections = tool.findIntersectionsSlow( roads );
+
+ expect( intersections.length ).toBe( 1 );
+ expect( intersections[ 0 ].road1 ).toBe( roads[ 0 ].id );
+ expect( intersections[ 0 ].road2 ).toBe( roads[ 1 ].id );
+ expect( intersections[ 0 ].x ).toBe( 0 );
+ expect( intersections[ 0 ].y ).toBe( 0 );
+ expect( intersections[ 0 ].s1 ).toBe( 50 );
+ expect( intersections[ 0 ].s2 ).toBe( 50 );
+ } );
+
+ it( 'should find angled road (45 degree) intersection in same direction', () => {
+
+ const road1 = openDrive.addDefaultRoad();
+ const road2 = openDrive.addDefaultRoad();
+
+ // left to right with angle
+ road1.addGeometryLine( 0, -35.35, -35.35, Maths.Deg2Rad * 45, 100 );
+
+ // down to up
+ road2.addGeometryLine( 0, 0, -50, 1.5708, 100 );
+
+ const intersections = tool.findIntersectionsSlow( [ road1, road2 ] );
+
+ expect( intersections.length ).toBe( 1 );
+ expect( intersections[ 0 ].road1 ).toBe( road1.id );
+ expect( intersections[ 0 ].road2 ).toBe( road2.id );
+ expect( Maths.approxEquals( intersections[ 0 ].x, -0.71, 0.01 ) ).toBe( true );
+ expect( Maths.approxEquals( intersections[ 0 ].y, -0.71, 0.01 ) ).toBe( true );
+ expect( intersections[ 0 ].s1 ).toBe( 49 );
+ expect( intersections[ 0 ].s2 ).toBe( 49 );
+
+
+ } );
+
+ it( 'should find angled road (45 degree) intersection in opposite direction', () => {
+
+ const road1 = openDrive.addDefaultRoad();
+ const road2 = openDrive.addDefaultRoad();
+
+ // left to right with angle
+ road1.addGeometryLine( 0, -35.35, -35.35, Maths.Deg2Rad * 45, 100 );
+
+ // up to down
+ road2.addGeometryLine( 0, 0, 50, -1.5708, 100 );
+
+ const intersections = tool.findIntersectionsSlow( [ road1, road2 ] );
+
+ expect( intersections.length ).toBe( 1 );
+ expect( intersections[ 0 ].road1 ).toBe( road1.id );
+ expect( intersections[ 0 ].road2 ).toBe( road2.id );
+ expect( Maths.approxEquals( intersections[ 0 ].x, 0.71, 0.01 ) ).toBe( true );
+ expect( Maths.approxEquals( intersections[ 0 ].y, 0.71, 0.01 ) ).toBe( true );
+ expect( intersections[ 0 ].s1 ).toBe( 51 );
+ expect( intersections[ 0 ].s2 ).toBe( 49 );
+
+
+ } );
+
+ it( 'should find correct distance from intersection', () => {
+
+ let distance = tool.calculateDistance( 0, 90 * Maths.Deg2Rad );
+
+ // let isEqual = Maths.approxEquals( distance, 2.35619450625 );
+
+ expect( distance ).toBe( 2.35619450625 );
+
+
+ distance = tool.calculateDistance( 45 * Maths.Deg2Rad, 90 * Maths.Deg2Rad );
+
+ // isEqual = Maths.approxEquals( distance, 3.53429174325 );
+
+ expect( distance ).toBe( 3.53429174325 );
+
+ } );
+
+ it( 'should calculate hdg difference', () => {
+
+ let hdgA = 0 * Maths.Deg2Rad;
+ let hdgB = 90 * Maths.Deg2Rad;
+
+ expect( tool.calculateSDirection( hdgA, hdgB ) ).toBe( TvDirection.SAME );
+ expect( tool.calculateSDirection( hdgB, hdgA ) ).toBe( TvDirection.SAME );
+
+ hdgA = -45 * Maths.Deg2Rad;
+ hdgB = 45 * Maths.Deg2Rad;
+
+ expect( tool.calculateSDirection( hdgA, hdgB ) ).toBe( TvDirection.SAME );
+ expect( tool.calculateSDirection( hdgB, hdgA ) ).toBe( TvDirection.SAME );
+
+ hdgA = -45 * Maths.Deg2Rad;
+ hdgB = 0 * Maths.Deg2Rad;
+
+ expect( tool.calculateSDirection( hdgA, hdgB ) ).toBe( TvDirection.SAME );
+ expect( tool.calculateSDirection( hdgB, hdgA ) ).toBe( TvDirection.SAME );
+
+ hdgA = 90 * Maths.Deg2Rad;
+ hdgB = 0 * Maths.Deg2Rad;
+
+ expect( tool.calculateSDirection( hdgA, hdgB ) ).toBe( TvDirection.SAME );
+ expect( tool.calculateSDirection( hdgB, hdgA ) ).toBe( TvDirection.SAME );
+
+ hdgA = 100 * Maths.Deg2Rad;
+ hdgB = 0 * Maths.Deg2Rad;
+
+ expect( tool.calculateSDirection( hdgA, hdgB ) ).toBe( TvDirection.OPPOSITE );
+ expect( tool.calculateSDirection( hdgB, hdgA ) ).toBe( TvDirection.OPPOSITE );
+
+ hdgA = 135 * Maths.Deg2Rad;
+ hdgB = 0 * Maths.Deg2Rad;
+
+ expect( tool.calculateSDirection( hdgA, hdgB ) ).toBe( TvDirection.OPPOSITE );
+ expect( tool.calculateSDirection( hdgB, hdgA ) ).toBe( TvDirection.OPPOSITE );
+
+ hdgA = 135 * Maths.Deg2Rad;
+ hdgB = 90 * Maths.Deg2Rad;
+
+ expect( tool.calculateSDirection( hdgA, hdgB ) ).toBe( TvDirection.SAME );
+ expect( tool.calculateSDirection( hdgB, hdgA ) ).toBe( TvDirection.SAME );
+
+ } );
+
+ it( 'should display links' );
+
+ it( 'should delete link' );
+
+ it( 'should create link' );
+
+ it( 'should show link control points' );
+
+ function createStraightRoadsSameDirection () {
+
+ const road1 = openDrive.addDefaultRoad();
+ const road2 = openDrive.addDefaultRoad();
+
+ // left to right
+ road1.addGeometryLine( 0, -50, 0, 0, 100 );
+
+ // down to up
+ road2.addGeometryLine( 0, 0, -50, 1.5708, 100 );
+
+ return [ road1, road2 ];
+ }
+
+ function createStraightRoadsOppositeDirection () {
+
+ const road1 = openDrive.addDefaultRoad();
+ const road2 = openDrive.addDefaultRoad();
+
+ // left to right
+ road1.addGeometryLine( 0, -50, 0, 0, 100 );
+
+ // up to down
+ road2.addGeometryLine( 0, 0, 50, -1.5708, 100 );
+
+ return [ road1, road2 ];
+ }
+
+} );
+
diff --git a/src/app/core/tools/auto-maneuver-tool.ts b/src/app/core/tools/auto-maneuver-tool.ts
new file mode 100644
index 00000000..2915e4e3
--- /dev/null
+++ b/src/app/core/tools/auto-maneuver-tool.ts
@@ -0,0 +1,1423 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseTool } from './base-tool';
+import { TvRoad } from 'app/modules/tv-map/models/tv-road.model';
+import * as THREE from 'three';
+import { Mesh, PointsMaterial, Vector3 } from 'three';
+import { TvPosTheta } from 'app/modules/tv-map/models/tv-pos-theta';
+import { TvMapQueries } from 'app/modules/tv-map/queries/tv-map-queries';
+import { PointEditor } from '../editors/point-editor';
+import { AbstractShapeEditor } from '../editors/abstract-shape-editor';
+import { TvMapSourceFile } from 'app/modules/tv-map/services/tv-map-source-file';
+import { Maths } from 'app/utils/maths';
+import { TvDirection, TvLaneType, TvSide } from 'app/modules/tv-map/models/tv-common';
+import { SceneService } from '../services/scene.service';
+import { CatmullRomPath, HermiteSplineCurve } from '../shapes/cubic-spline-curve';
+import { OdTextures } from 'app/modules/tv-map/builders/od.textures';
+import { TvJunctionConnection } from 'app/modules/tv-map/models/tv-junction-connection';
+import { LanePathObject, TvJunctionLaneLink } from 'app/modules/tv-map/models/tv-junction-lane-link';
+// import { JunctionDot } from "app/modules/three-js/objects/junction-dot";
+import { AbstractSpline } from '../shapes/abstract-spline';
+import { AutoSpline } from '../shapes/auto-spline';
+import { LanePathFactory } from '../factories/lane-path-factory.service';
+import { MouseButton, PointerEventData } from 'app/events/pointer-event-data';
+import { KeyboardInput } from '../input';
+import { CommandHistory } from 'app/services/command-history';
+import { SetInspectorCommand } from '../commands/set-inspector-command';
+import { LaneLinkInspector } from 'app/views/inspectors/lane-link-inspector/lane-link-inspector.component';
+import { JunctionEntryObject } from 'app/modules/three-js/objects/junction-entry.object';
+import { MultiCmdsCommand } from '../commands/multi-cmds-command';
+import { RoadControlPoint } from 'app/modules/three-js/objects/road-control-point';
+import { PickingHelper } from '../services/picking-helper.service';
+import { SetValueCommand } from 'app/modules/three-js/commands/set-value-command';
+import { UpdateRoadPointCommand } from '../commands/update-road-point-command';
+import { TvRoadCoord } from 'app/modules/tv-map/models/tv-lane-coord';
+
+export interface JunctionDot {
+ id: number,
+ s: number,
+ roadId: number,
+ laneId: number,
+ type: 'start' | 'end',
+ direction: 'forward' | 'backward',
+ position: Vector3,
+ hdg: number,
+ sDirection?: 'same' | 'opposite',
+ color: number
+}
+
+export interface IJunctionConnection {
+ type?: 'straight' | 'left-turn' | 'right-turn',
+ incomingRoad?: number,
+ outgoingRoad?: number,
+ fromLane?: number,
+ toLane?: number,
+ point?: 'start' | 'end',
+ entry?: JunctionDot,
+ exit?: JunctionDot,
+ spline?: AbstractSpline,
+ mesh?: Mesh
+}
+
+export interface TempIntersection {
+ x: number,
+ y: number,
+ s1: number,
+ s2: number,
+ road1: number,
+ road2: number,
+ coordA?: TvRoadCoord,
+ coordB?: TvRoadCoord,
+}
+
+/**
+ * Tool to show,edit junction maneuvers
+ *
+ * 1. Shows all junction areas on enable/init
+ * a. highlight junction area on hover
+ * b. select junction area on click
+ * P
+ * 2. When Junction Clicked
+ * a. show dots, maneuvers
+ * b. show rebuild maneuver button in inspector
+ *
+ * 3. Dots can be highlighted and clicked
+ * a. when clicked, dots show nothing
+ *
+ * 4. Maneuvers can be clicked
+ * a. when clicked show, inspector with turn type etc info
+ * b. when clicked show 4 control points that move in +- s direction
+ * c. maneuevers have distance node for start and end which affects maneuver path
+ */
+export class AutoManeuverTool extends BaseTool {
+
+ public static DOTCOUNT = 0;
+
+ name: string = 'ManeuverTool';
+
+ pointEditor: AbstractShapeEditor;
+
+ private connections: IJunctionConnection[] = [];
+
+ private pointerDown = false;
+
+ private pointerDownAt: Vector3;
+
+ private roadChanged = false;
+
+ private connectingRoad: TvRoad;
+
+ private roadControlPoint: RoadControlPoint;
+
+ private lanePathObject: LanePathObject;
+
+ init () {
+
+ this.pointEditor = new PointEditor();
+
+ }
+
+ enable () {
+
+ this.loadJunctions();
+
+ const intersections = this.findIntersectionsSlow( [ ...this.openDrive.roads.values() ] );
+
+ intersections.forEach( intersection => {
+
+ const results = this.calculateJunctionDistances( intersection.coordA, intersection.coordB );
+
+ results.forEach( result => {
+
+ this.divideRoad( result );
+
+ } );
+
+ } );
+
+ // const junctions = this.createJunctionAreas( intersections );
+
+ // this.connections = this.prepareJunctionConnections( junctions );
+
+ // this.connections.forEach( connection => {
+ // this.makePath( connection );
+ // } )
+ }
+
+ divideRoad ( result ) {
+
+ // const startSCoord = result.start.s;
+ // const startRoad = this.openDrive.getRoadById( result.start.roadId );
+ // const startPosition = startRoad.getRoadPosition( result.start.s );
+ // const p1 = new RoadControlPoint( startRoad, startPosition.toVector3() );
+
+ // // start position become the limit/length for the road
+ // startRoad.length = startSCoord;
+
+ // const geometry = startRoad.getGeometryAt( startSCoord );
+
+ // geometry.length = startSCoord - geometry.s;
+
+
+ // const endRoad = this.openDrive.getRoadById( result.end.roadId );
+ // const endPosition = endRoad.getRoadPosition( result.end.s );
+ // const p2 = new RoadControlPoint( endRoad, endPosition.toVector3() );
+
+
+ // SceneService.add( p1 );
+ // SceneService.add( p2 );
+ }
+
+ disable () {
+
+ this.pointEditor.removeAllControlPoints();
+
+ this.connections.forEach( connection => {
+
+ if ( connection.spline ) {
+
+ connection.spline.hide();
+
+ }
+
+ if ( connection.mesh ) {
+
+ connection.mesh.visible = false;
+ }
+
+ } );
+
+ this.hideLinks();
+ }
+
+ onPointerDown ( e: PointerEventData ) {
+
+ if ( e.button === MouseButton.RIGHT || e.button === MouseButton.MIDDLE ) return;
+
+ this.pointerDown = true;
+
+ this.pointerDownAt = e.point.clone();
+
+ const shiftKeyDown = KeyboardInput.isShiftKeyDown;
+
+ let hasInteracted = false;
+
+ if ( !shiftKeyDown && !hasInteracted ) hasInteracted = this.checkRoadControlPointInteraction( e );
+
+ if ( !shiftKeyDown && !hasInteracted ) hasInteracted = this.checkJunctionEntryInteraction( e );
+
+ if ( !shiftKeyDown && !hasInteracted ) hasInteracted = this.checkPathInteraction( e );
+
+ if ( !hasInteracted ) {
+
+ const commands = [];
+
+ commands.push( new SetInspectorCommand( null, null ) );
+
+ CommandHistory.execute( new MultiCmdsCommand( commands ) );
+ }
+ }
+
+ onPointerUp ( e: PointerEventData ) {
+
+ if ( this.connectingRoad && this.roadControlPoint && this.roadChanged ) {
+
+ const updateRoadPointCommand = new UpdateRoadPointCommand(
+ this.connectingRoad,
+ this.roadControlPoint,
+ this.roadControlPoint.position,
+ this.pointerDownAt
+ );
+
+ CommandHistory.execute( updateRoadPointCommand );
+
+ LanePathFactory.update( this.lanePathObject );
+ }
+
+ this.pointerDown = false;
+
+ this.roadChanged = false;
+
+ this.pointerDownAt = null;
+ }
+
+ onPointerMoved ( e: PointerEventData ) {
+
+ if ( this.pointerDown && this.roadControlPoint && this.connectingRoad ) {
+
+ this.roadControlPoint.copyPosition( e.point );
+
+ this.connectingRoad.spline.update();
+
+ this.roadChanged = true;
+
+ }
+
+ }
+
+ checkPathInteraction ( event: PointerEventData ): boolean {
+
+ if ( event.button !== MouseButton.LEFT ) return;
+
+ let hasInteracted = false;
+
+ for ( let i = 0; i < event.intersections.length; i++ ) {
+
+ const intersection = event.intersections[ i ];
+
+ if ( intersection.object[ 'tag' ] === LanePathObject.tag ) {
+
+ hasInteracted = true;
+
+ this.lanePathObject = intersection.object.parent as LanePathObject;
+
+ const commands = [];
+
+ this.connectingRoad = this.lanePathObject.connectingRoad;
+
+ commands.push( new SetInspectorCommand( LaneLinkInspector, {
+ link: this.lanePathObject.link,
+ connection: this.lanePathObject.connection
+ } ) );
+
+ CommandHistory.execute( new MultiCmdsCommand( commands ) );
+
+ break;
+ }
+ }
+
+ return hasInteracted;
+ }
+
+ static createDots ( road: TvRoad, point: Vector3, sCoord?: number, startSCoord?: number, endSCoord?: number ) {
+
+ const dots: JunctionDot[] = [];
+
+ const laneSection = road.getLaneSectionAt( sCoord );
+
+ laneSection.getLeftLanes().filter( l => l.type == TvLaneType.driving ).forEach( lane => {
+
+ const startRef = new TvPosTheta();
+ const startPosition = TvMapQueries.getLanePosition( road.id, lane.id, startSCoord, 0, startRef );
+
+ // start dot
+ dots.push( {
+ id: this.DOTCOUNT++,
+ color: 0x69F0AE,
+ s: startSCoord,
+ roadId: road.id,
+ laneId: lane.id,
+ type: 'start',
+ direction: 'forward',
+ position: startPosition,
+ hdg: startRef.hdg
+ } );
+
+ const endRef = new TvPosTheta();
+ const endPosition = TvMapQueries.getLanePosition( road.id, lane.id, endSCoord, 0, endRef );
+
+ // end dot
+ dots.push( {
+ id: this.DOTCOUNT++,
+ color: 0xFF5252,
+ s: endSCoord,
+ roadId: road.id,
+ laneId: lane.id,
+ type: 'end',
+ direction: 'forward',
+ position: endPosition,
+ hdg: endRef.hdg,
+ } );
+
+ } );
+
+ laneSection.getRightLanes().filter( l => l.type == TvLaneType.driving ).forEach( lane => {
+
+ const startRef = new TvPosTheta();
+ const startPosition = TvMapQueries.getLanePosition( road.id, lane.id, endSCoord, 0, startRef );
+
+ // start dot
+ dots.push( {
+ id: this.DOTCOUNT++,
+ color: 0x69F0AE,
+ s: endSCoord,
+ roadId: road.id,
+ laneId: lane.id,
+ type: 'start',
+ direction: 'backward',
+ position: startPosition,
+ hdg: startRef.hdg + Math.PI, // add 180 degree
+ } );
+
+ const endRef = new TvPosTheta();
+ const endPosition = TvMapQueries.getLanePosition( road.id, lane.id, startSCoord, 0, endRef );
+
+ // end dot
+ dots.push( {
+ id: this.DOTCOUNT++,
+ color: 0xFF5252,
+ s: startSCoord,
+ roadId: road.id,
+ laneId: lane.id,
+ type: 'end',
+ direction: 'backward',
+ position: endPosition,
+ hdg: endRef.hdg + Math.PI, // add 180 degree
+ } );
+
+ } );
+
+ return dots;
+ }
+
+ checkRoadControlPointInteraction ( e: PointerEventData ): boolean {
+
+ if ( !this.connectingRoad || !this.connectingRoad.spline ) return;
+
+ if ( !e.point ) return;
+
+ const maxDistance = Math.max( 0.5, e.approxCameraDistance * 0.01 );
+
+ const roadControlPoints = [];
+
+ this.connectingRoad.spline.controlPoints.forEach( ( cp: RoadControlPoint ) => {
+
+ roadControlPoints.push( cp );
+
+ if ( cp.frontTangent ) roadControlPoints.push( cp.frontTangent );
+
+ if ( cp.backTangent ) roadControlPoints.push( cp.backTangent );
+
+ } );
+
+ const roadControlPoint = PickingHelper.findNearest( e.point, roadControlPoints, maxDistance );
+
+ if ( roadControlPoint ) {
+
+ const commands = [];
+
+ // commands.push( new SetInspectorCommand( RoadInspector, {
+ // road: this.connectingRoad,
+ // controlPoint: roadControlPoint
+ // } ) );
+
+ commands.push( new SetValueCommand( this, 'roadControlPoint', roadControlPoint ) );
+
+ // if ( this.node ) commands.push( new SetValueCommand( this, 'node', null ) );
+
+ CommandHistory.execute( new MultiCmdsCommand( commands ) );
+
+ } else if ( !this.roadControlPoint && this.roadControlPoint ) {
+
+ this.roadControlPoint.unselect();
+
+ this.roadControlPoint = null;
+
+ }
+
+ return roadControlPoint != null;
+ }
+
+ hideLinks () {
+
+ this.openDrive.junctions.forEach( junction => {
+
+ junction.connections.forEach( connection => {
+
+ const incomingRoad = this.openDrive.getRoadById( connection.incomingRoad );
+ const connectingRoad = this.openDrive.getRoadById( connection.connectingRoad );
+
+ connection.laneLink.forEach( link => {
+
+ link.hide();
+
+ } );
+
+ } );
+
+ } );
+ }
+
+ loadJunctions (): void {
+
+ this.openDrive.junctions.forEach( junction => {
+
+ junction.connections.forEach( connection => {
+
+ const incomingRoad = this.openDrive.getRoadById( connection.incomingRoad );
+ const connectingRoad = this.openDrive.getRoadById( connection.connectingRoad );
+
+ connection.laneLink.forEach( link => {
+
+ this.makeJunctionConnectionPath( incomingRoad, connectingRoad, connection, link );
+
+ } );
+
+ } );
+
+ } );
+
+ }
+
+ static createTurnConnectionPaths ( entry: JunctionDot, exit: JunctionDot ): AbstractSpline {
+
+ const autoSpline = new AutoSpline();
+
+ autoSpline.addControlPointAt( entry.position );
+
+ if ( entry.direction == 'forward' && exit.direction == 'forward' ) {
+
+ const entry2 = TvMapQueries.getLanePosition( entry.roadId, entry.laneId, entry.s + 2.5, 0 );
+ autoSpline.addControlPointAt( entry2 );
+
+ const entry3 = TvMapQueries.getLanePosition( exit.roadId, exit.laneId, exit.s - 2.5, 0 );
+ autoSpline.addControlPointAt( entry3 );
+
+ } else if ( entry.direction == 'backward' && exit.direction == 'backward' ) {
+
+ const entry2 = TvMapQueries.getLanePosition( entry.roadId, entry.laneId, entry.s - 2.5, 0 );
+ autoSpline.addControlPointAt( entry2 );
+
+ const entry3 = TvMapQueries.getLanePosition( exit.roadId, exit.laneId, exit.s + 2.5, 0 );
+ autoSpline.addControlPointAt( entry3 );
+
+ } else if ( entry.direction == 'forward' && exit.direction == 'backward' ) {
+
+ const entry2 = TvMapQueries.getLanePosition( entry.roadId, entry.laneId, entry.s + 2.5, 0 );
+ autoSpline.addControlPointAt( entry2 );
+
+ const entry3 = TvMapQueries.getLanePosition( exit.roadId, exit.laneId, exit.s + 2.5, 0 );
+ autoSpline.addControlPointAt( entry3 );
+
+ } else if ( entry.direction == 'backward' && exit.direction == 'forward' ) {
+
+ const entry2 = TvMapQueries.getLanePosition( entry.roadId, entry.laneId, entry.s - 2.5, 0 );
+ autoSpline.addControlPointAt( entry2 );
+
+ const entry3 = TvMapQueries.getLanePosition( exit.roadId, exit.laneId, exit.s - 2.5, 0 );
+ autoSpline.addControlPointAt( entry3 );
+
+ }
+
+ autoSpline.addControlPointAt( exit.position );
+
+ autoSpline.update();
+
+ // console.log( entry, exit, autoSpline.exportGeometries() );
+
+ return autoSpline;
+ }
+
+ makePath ( connection: IJunctionConnection ) {
+
+ const spline = connection.spline;
+
+ const shape = new THREE.Shape();
+ shape.moveTo( 0, -0.25 );
+ shape.lineTo( 0, 0.25 );
+
+ // const arcLine = new LineArcSplineCurve( spline.controlPoints );
+
+ const path = new CatmullRomPath( spline.controlPointPositions );
+
+ // const randomPoints = [];
+ // for ( let i = 0; i < 10; i++ ) {
+ // randomPoints.push( new THREE.Vector3( ( i - 4.5 ) * 50, THREE.Math.randFloat( - 50, 50 ), 0 ) );
+ // }
+ // const path = new THREE.CatmullRomSpline( randomPoints );
+
+ const extrudeSettings = {
+ steps: path.getLength() * 2,
+ bevelEnabled: false,
+ extrudePath: path
+ };
+
+ const geometry = new THREE.ExtrudeGeometry( shape, extrudeSettings );
+ const material = new THREE.MeshBasicMaterial( {
+ color: 0x00ff00,
+ wireframe: false,
+ opacity: 0.5,
+ transparent: true,
+ depthTest: false,
+ map: OdTextures.asphalt
+ } );
+
+ connection.mesh = new THREE.Mesh( geometry, material );
+
+ connection.mesh.renderOrder = 3;
+
+ SceneService.add( connection.mesh );
+
+ }
+
+ private curve: THREE.Mesh;
+
+ updateCurve () {
+
+ const shape = new THREE.Shape();
+ shape.moveTo( 0, 0 );
+ shape.lineTo( 0, 12 );
+
+ const p0 = this.pointEditor.controlPointPositions[ 0 ];
+ const p1 = this.pointEditor.controlPointPositions[ 1 ];
+ const p2 = this.pointEditor.controlPointPositions[ 2 ];
+ const p3 = this.pointEditor.controlPointPositions[ 3 ];
+
+ const a = 0;
+ const b = 0;
+ const c = 0;
+ const d = 0;
+
+
+ const bezier = new THREE.CubicBezierCurve3( p0, p1, p2, p3 );
+ const hermite = new HermiteSplineCurve( p0, p1, p2, p3 );
+
+ const extrudeSettings = {
+ steps: 200,
+ bevelEnabled: false,
+ extrudePath: hermite
+ };
+
+ this.curve.geometry.dispose();
+
+ this.curve.geometry = new THREE.ExtrudeGeometry( shape, extrudeSettings );
+ }
+
+ makeSampleCurve () {
+
+ const shape = new THREE.Shape();
+ shape.moveTo( 0, 0 );
+ shape.lineTo( 0, 12 );
+
+ const p0 = new Vector3( 0, 0, 0 );
+ const p1 = new Vector3( 100, 0, 0 );
+ const p2 = new Vector3( 150, 50, 0 );
+ const p3 = new Vector3( 150, 100, 0 );
+
+ this.pointEditor.addControlPoint( p0 );
+ this.pointEditor.addControlPoint( p1 );
+ this.pointEditor.addControlPoint( p2 );
+ this.pointEditor.addControlPoint( p3 );
+
+ // const m = new Matrix4().set(
+ // 0, 0, -3, 3,
+ // -3, 3, 0, 0,
+ // 0, 0, 0, 1,
+ // 1, 0, 0, 0
+ // );
+
+ // const m = new Matrix4().set(
+ // 1, 0, 0, 0,
+ // 0, 0, 0, 1,
+ // -3, 3, 0, 0,
+ // 0, 0, -3, 3,
+ // );
+
+ // // console.log( 'm', m );
+
+ // const m_inv = new Matrix4().getInverse( m );
+
+ // console.log( 'm-inv', m_inv );
+
+ // const g = new Matrix4().set(
+ // 0, 0, 0, 0,
+ // 100, 0, 0, 0,
+ // 150, 0, 0, 0,
+ // 150, 0, 0, 0
+ // );
+
+ const bezier = new THREE.CubicBezierCurve3( p0, p1, p2, p3 );
+ const hermite = new HermiteSplineCurve( p0, p1, p2, p3 );
+
+ // console.log( m_inv.multiply( g ), bezier.getLength() );
+
+ const extrudeSettings = {
+ steps: 200,
+ bevelEnabled: false,
+ extrudePath: hermite
+ };
+
+ const geometry = new THREE.ExtrudeGeometry( shape, extrudeSettings );
+ const material = new THREE.MeshBasicMaterial( { color: 0x00ff00, wireframe: true } );
+ const mesh = this.curve = new THREE.Mesh( geometry, material );
+
+ SceneService.add( mesh );
+
+ }
+
+ static createStraightConnections ( road: TvRoad, dots: JunctionDot[] = [] ) {
+
+ const connections: IJunctionConnection[] = [];
+
+ const entries = dots.filter( d => d.roadId == road.id ).filter( d => d.type == 'start' );
+
+ entries.forEach( entry => {
+
+ const exits = dots.filter( d => d.roadId == road.id ).filter( d => d.laneId == entry.laneId ).filter( d => d.type == 'end' );
+
+ if ( exits.length > 0 ) {
+
+ const exit = exits[ 0 ];
+
+ const spline = this.createStraightConnectionPath( entry, exit );
+
+ connections.push( {
+ type: 'straight',
+ incomingRoad: entry.roadId,
+ outgoingRoad: exit.roadId,
+ fromLane: entry.laneId,
+ toLane: exit.laneId,
+ point: 'start',
+ entry: entry,
+ exit: exit,
+ spline: spline
+ } );
+ }
+
+ } );
+
+ return connections;
+ }
+
+ static createLeftTurnConnections ( road: TvRoad, dots: JunctionDot[] = [] ) {
+
+ const connections: IJunctionConnection[] = [];
+
+ const directions = [ 'forward', 'backward' ];
+
+ directions.forEach( direction => {
+
+ let entry: JunctionDot = null;
+
+ let entries = dots.filter( d => d.roadId == road.id )
+ .filter( d => d.direction == direction )
+ .filter( d => d.type == 'start' );
+
+ if ( entries.length > 0 && direction == 'forward' ) {
+
+ // find left most lane
+ entry = entries.reduce( ( x, y ) => {
+ return x.laneId > y.laneId ? x : y;
+ } );
+
+ } else if ( entries.length > 0 && direction == 'backward' ) {
+
+ // find left most lane
+ entry = entries.reduce( ( x, y ) => {
+ return x.laneId < y.laneId ? x : y;
+ } );
+
+ }
+
+ if ( entry ) {
+
+ const exits = dots.filter( d => d.roadId != road.id )
+ .filter( d => d.type == 'end' )
+ .filter( d => Maths.findSide( d.position, entry.position, entry.hdg ) == TvSide.LEFT );
+
+ if ( exits.length > 0 ) {
+
+ const exit = exits.reduce( ( a, b ) => {
+ return a.position.distanceTo( entry.position ) < b.position.distanceTo( entry.position ) ? a : b;
+ } );
+
+ const spline = this.createTurnConnectionPaths( entry, exit );
+
+ connections.push( {
+ type: 'left-turn',
+ incomingRoad: entry.roadId,
+ outgoingRoad: exit.roadId,
+ fromLane: entry.laneId,
+ toLane: exit.laneId,
+ point: 'start',
+ entry: entry,
+ exit: exit,
+ spline: spline
+ } );
+ }
+ }
+
+ } );
+
+ return connections;
+ }
+
+ findIntersectionsSlow ( roads: TvRoad[] ): TempIntersection[] {
+
+ const step = 1;
+
+ const pointCache: any[] = [];
+
+ // const t1 = performance.now();
+
+ for ( let i = 0; i < roads.length; i++ ) {
+
+ const road = roads[ i ];
+
+ const positions: any[] = [];
+
+ const geom = {
+ road: road.id,
+ positions: positions,
+ };
+
+ const geometries = road.geometries;
+
+ for ( let g = 0; g < geometries.length; g++ ) {
+
+ const geometry = geometries[ g ];
+
+ const posTheta = new TvPosTheta();
+
+ for ( let s = geometry.s; s <= geometry.s2; s += step ) {
+
+ geometry.getCoords( s, posTheta );
+
+ positions.push( { x: posTheta.x, y: posTheta.y, z: 0, s: s } );
+
+ }
+ }
+
+ pointCache.push( geom );
+ }
+
+ // const t2 = performance.now();
+
+ // const timeToMakeGeometries = t2 - t1; console.log( "step-1", timeToMakeGeometries );
+
+ const intersections: TempIntersection[] = [];
+
+ for ( let i = 0; i < pointCache.length; i++ ) {
+
+ const points = pointCache[ i ].positions;
+
+ for ( let j = i + 1; j < pointCache.length; j++ ) {
+
+ const otherPoints = pointCache[ j ].positions;
+
+ for ( let k = 0; k < otherPoints.length; k++ ) {
+
+ const otherPoint = otherPoints[ k ];
+
+ for ( let l = 0; l < points.length; l++ ) {
+
+ const point = points[ l ];
+
+ // const distance = position.distanceTo( otherPosition );
+
+ const distance = Math.sqrt(
+ ( point.x - otherPoint.x ) * ( point.x - otherPoint.x ) +
+ ( point.y - otherPoint.y ) * ( point.y - otherPoint.y )
+ );
+
+ if ( distance < ( step * 0.9 ) ) {
+
+ intersections.push( {
+ x: point.x,
+ y: point.y,
+ s1: point.s,
+ s2: otherPoint.s,
+ road1: pointCache[ i ].road,
+ road2: pointCache[ j ].road,
+ coordA: new TvRoadCoord( pointCache[ i ].road, point.s ),
+ coordB: new TvRoadCoord( pointCache[ j ].road, otherPoint.s ),
+ } );
+
+ // skip a few steps
+ l += step * 2;
+ k += step * 2;
+ }
+ }
+ }
+ }
+ }
+
+ // const t3 = performance.now();
+
+ // const timeToFindIntersections = t3 - t2; console.log( "step2", timeToFindIntersections );
+
+ return intersections;
+ }
+
+ calculateDistance ( roadAHdg: number, roadBHdg: number ) {
+
+ // const roadA = this.openDrive.getRoadById( coordA.roadId );
+ // const roadB = this.openDrive.getRoadById( coordB.roadId );
+
+ // const roadAHdg = roadA.getRoadPosition( coordA.s ).hdg;
+ // const roadBHdg = roadB.getRoadPosition( coordB.s ).hdg;
+
+ let hdgDifference: number;
+
+ if ( roadAHdg > roadBHdg )
+ hdgDifference = roadAHdg - roadBHdg;
+ else
+ hdgDifference = roadBHdg - roadAHdg;
+
+ const absDifference = Math.abs( hdgDifference );
+
+ // const absDifference = Maths.clamp( Math.abs( hdgDifference ), 0, Maths.M_PI_2 );
+
+ // const sDirection = this.calculateSDirection( roadAHdg, roadBHdg );
+
+ const distanceFromJunction = Math.abs( Maths.M_PI - absDifference ) * 1.5;
+
+ return distanceFromJunction;
+ }
+
+ calculateSDirection ( hdgA, hdgB ): TvDirection {
+
+ let difference;
+
+ if ( hdgA > hdgB ) {
+
+ difference = hdgB - hdgA;
+
+ } else {
+
+ difference = hdgA - hdgB;
+
+ }
+
+ return Math.abs( difference ) > Maths.M_PI_2 ?
+ TvDirection.OPPOSITE :
+ TvDirection.SAME;
+ }
+
+ toPositiveAngle ( angle: number ) {
+
+ angle = angle % ( Math.PI * 2 );
+
+ while ( angle < 0 ) {
+
+ angle += Math.PI * 2;
+
+ }
+
+ return angle;
+ }
+
+ prepareJunctionConnections ( junctions: any[] ): IJunctionConnection[] {
+
+ const connections: IJunctionConnection[] = [];
+
+ for ( let i = 0; i < junctions.length; i++ ) {
+
+ const junction = junctions[ i ];
+
+ // point of intersection
+ this.pointEditor.addControlPoint( junction.position );
+
+ const dots = this.createDotsForJunction( junction );
+
+ // ManeuverTool.createConnections( [ junction.road1, junction.road2 ], dots ).forEach( connection => {
+
+ // connections.push( connection );
+
+ // } );
+
+ }
+
+ return connections;
+ }
+
+ createDotsForJunction ( junction ) {
+
+ const dots: JunctionDot[] = [];
+
+ const road1_dots = AutoManeuverTool.createDots(
+ junction.road1,
+ junction.position,
+ junction.road1_s,
+ junction.road1_StartS,
+ junction.road1_EndS
+ );
+
+ const road2_dots = AutoManeuverTool.createDots(
+ junction.road2,
+ junction.position,
+ junction.road2_s,
+ junction.road2_StartS,
+ junction.road2_EndS
+ );
+
+ road1_dots.forEach( dot => dots.push( dot ) );
+
+ road2_dots.forEach( dot => dots.push( dot ) );
+
+ dots.forEach( dot => {
+
+ dot.sDirection = junction.sDirection;
+
+ const cp = this.pointEditor.addControlPoint( dot.position, null, 20 );
+
+ ( cp.material as PointsMaterial ).color.set( dot.color );
+
+ } );
+
+ return dots;
+ }
+
+ calculateJunctionDistances ( coordA: TvRoadCoord, coordB: TvRoadCoord ) {
+
+ const result: { start: TvRoadCoord, end: TvRoadCoord }[] = [];
+
+ const coords: TvRoadCoord[] = [];
+
+ const roadA = this.openDrive.getRoadById( coordA.roadId );
+ const roadB = this.openDrive.getRoadById( coordB.roadId );
+
+ const roadALeftWidth = roadA.getLeftSideWidth( coordA.s );
+ const roadARightWidth = roadA.getRightsideWidth( coordA.s );
+
+ const roadBLeftWidth = roadB.getLeftSideWidth( coordB.s );
+ const roadBRightWidth = roadB.getRightsideWidth( coordB.s );
+
+ const roadAHdg = roadA.getRoadPosition( coordA.s ).hdg;
+ const roadBHdg = roadB.getRoadPosition( coordB.s ).hdg;
+
+ let roadAStart, roadAEnd, roadBStart, roadBEnd;
+
+ const distanceFromJunction = this.calculateDistance( roadAHdg, roadBHdg );
+
+ const sDirection = this.calculateSDirection( roadAHdg, roadBHdg );
+
+ if ( sDirection === TvDirection.SAME ) {
+
+ roadAStart = coordA.s - roadBLeftWidth - distanceFromJunction;
+ roadAEnd = coordA.s + roadBRightWidth + distanceFromJunction;
+
+ roadBStart = coordB.s - roadALeftWidth - distanceFromJunction;
+ roadBEnd = coordB.s + roadARightWidth + distanceFromJunction;
+
+ } else {
+
+ roadAStart = coordA.s - roadBLeftWidth - distanceFromJunction;
+ roadAEnd = coordA.s + roadBRightWidth + distanceFromJunction;
+
+ roadBStart = coordB.s - roadALeftWidth - distanceFromJunction;
+ roadBEnd = coordB.s + roadARightWidth + distanceFromJunction;
+
+ }
+
+ result.push( {
+ start: new TvRoadCoord( coordA.roadId, roadAStart ),
+ end: new TvRoadCoord( coordA.roadId, roadAEnd ),
+ } );
+
+ coords.push( new TvRoadCoord( coordA.roadId, roadAStart ) );
+ coords.push( new TvRoadCoord( coordA.roadId, roadAEnd ) );
+
+ result.push( {
+ start: new TvRoadCoord( coordB.roadId, roadBStart ),
+ end: new TvRoadCoord( coordB.roadId, roadBEnd ),
+ } );
+
+ coords.push( new TvRoadCoord( coordB.roadId, roadBStart ) );
+ coords.push( new TvRoadCoord( coordB.roadId, roadBEnd ) );
+
+ return result;
+ }
+
+ static createConnections ( roads: TvRoad[], dots: JunctionDot[] ): IJunctionConnection[] {
+
+ const connections: IJunctionConnection[] = [];
+
+ roads.forEach( road => {
+
+ this.createStraightConnections( road, dots )
+ .forEach( connection => connections.push( connection ) );
+
+ this.createLeftTurnConnections( road, dots )
+ .forEach( connection => connections.push( connection ) );
+
+ this.createRightTurnConnections( road, dots )
+ .forEach( connection => connections.push( connection ) );
+
+ } );
+
+ return connections;
+ }
+
+ static createRightTurnConnections ( road: TvRoad, dots: JunctionDot[] = [] ) {
+
+ const connections: IJunctionConnection[] = [];
+
+ const directions = [ 'forward', 'backward', ];
+
+ directions.forEach( direction => {
+
+ let entry = null;
+
+ let entries = dots.filter( d => d.roadId == road.id )
+ .filter( d => d.direction == direction )
+ .filter( d => d.type == 'start' );
+
+ if ( entries.length > 0 && direction == 'forward' ) {
+
+ // find right most lane
+ entry = entries.reduce( ( x, y ) => {
+ return x.laneId < y.laneId ? x : y;
+ } );
+
+ } else if ( entries.length > 0 && direction == 'backward' ) {
+
+ // find right most lane
+ entry = entries.reduce( ( x, y ) => {
+ return x.laneId > y.laneId ? x : y;
+ } );
+
+ }
+
+ if ( entry ) {
+
+ const exits = dots
+ .filter( d => d.roadId != road.id )
+ .filter( d => d.type == 'end' )
+ .filter( d => Maths.findSide( d.position, entry.position, entry.hdg ) == TvSide.RIGHT );
+
+ if ( exits.length > 0 ) {
+
+ // get the nearest exit by lowest distance from entry
+ const exit = exits.reduce( ( a, b ) => {
+ return a.position.distanceTo( entry.position ) < b.position.distanceTo( entry.position ) ? a : b;
+ } );
+
+ const spline = this.createTurnConnectionPaths( entry, exit );
+
+ connections.push( {
+ type: 'right-turn',
+ incomingRoad: entry.roadId,
+ outgoingRoad: exit.roadId,
+ fromLane: entry.laneId,
+ toLane: exit.laneId,
+ point: 'start',
+ entry: entry,
+ exit: exit,
+ spline: spline
+ } );
+ }
+ }
+
+ } );
+
+ return connections;
+ }
+
+ checkJunctionEntryInteraction ( event: PointerEventData ): boolean {
+
+ if ( event.button !== MouseButton.LEFT ) return;
+
+ let hasInteracted = false;
+
+ for ( let i = 0; i < event.intersections.length; i++ ) {
+
+ const intersection = event.intersections[ i ];
+
+ if ( intersection.object[ 'tag' ] === JunctionEntryObject.tag ) {
+
+ hasInteracted = true;
+
+ console.log( 'junction-entry' );
+
+ break;
+ }
+ }
+
+ return hasInteracted;
+ }
+
+ createJunctionAreas ( intersections: any[] ) {
+
+ const junctions: {
+ position,
+ sDirection,
+ road1,
+ road1_s,
+ road1_hdg,
+ road1_StartS,
+ road1_EndS,
+ road2,
+ road2_s,
+ road2_hdg,
+ road2_StartS,
+ road2_EndS
+ }[] = [];
+
+ for ( let i = 0; i < intersections.length; i++ ) {
+
+ const intersection = intersections[ i ];
+
+ const position = new Vector3( intersection.x, intersection.y, 0 );
+
+ const road_1 = TvMapSourceFile.openDrive.getRoadById( intersection.road1 );
+ const road_2 = TvMapSourceFile.openDrive.getRoadById( intersection.road2 );
+
+ const road1_s = intersection.s1;
+ const road2_s = intersection.s2;
+
+ let road1_LeftWidth = 0;
+ let road1_RightWidth = 0;
+
+ road_1.getLaneSectionAt( road1_s ).getLeftLanes().forEach( lane => {
+ road1_LeftWidth += lane.getWidthValue( road1_s );
+ } );
+ road_1.getLaneSectionAt( road1_s ).getRightLanes().forEach( lane => {
+ road1_RightWidth += lane.getWidthValue( road1_s );
+ } );
+
+ let road2_LeftWidth = 0;
+ let road2_RightWidth = 0;
+
+ road_2.getLaneSectionAt( road2_s ).getLeftLanes().forEach( lane => {
+ road2_LeftWidth += lane.getWidthValue( road2_s );
+ } );
+ road_2.getLaneSectionAt( road2_s ).getRightLanes().forEach( lane => {
+ road2_RightWidth += lane.getWidthValue( road2_s );
+ } );
+
+ let hdg1 = road_1.getPositionAt( road1_s, 0 ).hdg * Maths.Rad2Deg;
+ let hdg2 = road_2.getPositionAt( road2_s, 0 ).hdg * Maths.Rad2Deg;
+
+ let difference = ( hdg2 - hdg1 ) % 360;
+
+ let road1_StartS, road1_EndS, road2_StartS, road2_EndS;
+
+ let sDirection;
+
+ // same
+ if ( difference > 0 && difference < 180 ) {
+
+ sDirection = 'same';
+
+ let distanceFromJunction = Math.abs( 90 - difference ) * 0.2 + 2.5;
+
+ road1_StartS = road1_s - road2_LeftWidth - distanceFromJunction;
+ road1_EndS = road1_s + road2_RightWidth + distanceFromJunction;
+
+ road2_StartS = road2_s - road1_LeftWidth - distanceFromJunction;
+ road2_EndS = road2_s + road1_RightWidth + distanceFromJunction;
+
+ } else {
+
+ sDirection = 'opposite';
+
+ let distanceFromJunction = Math.abs( 90 - Math.abs( difference ) ) * 0.2 + 2.5;
+
+ road1_StartS = road1_s - road2_LeftWidth - distanceFromJunction;
+ road1_EndS = road1_s + road2_RightWidth + distanceFromJunction;
+
+ road2_StartS = road2_s - road1_LeftWidth - distanceFromJunction;
+ road2_EndS = road2_s + road1_RightWidth + distanceFromJunction;
+
+ }
+
+ junctions.push( {
+ position,
+ sDirection,
+ road1: road_1,
+ road1_s,
+ road1_hdg: hdg1,
+ road1_StartS,
+ road1_EndS,
+ road2: road_2,
+ road2_s,
+ road2_hdg: hdg2,
+ road2_StartS,
+ road2_EndS
+ } );
+ }
+
+ return junctions;
+ }
+
+ static createStraightConnectionPath ( entry: JunctionDot, exit: JunctionDot ): AbstractSpline {
+
+ const spline = new AutoSpline();
+
+ spline.addControlPointAt( entry.position );
+ spline.addControlPointAt( exit.position );
+
+ spline.update();
+
+ return spline;
+ }
+
+ makeJunctionConnectionPath (
+ incomingRoad: TvRoad,
+ connectingRoad: TvRoad,
+ connection: TvJunctionConnection,
+ link: TvJunctionLaneLink
+ ) {
+
+ // 1. find connection positions which are basically dots
+ // 2. find whether this connection is straight,left,right based on start->end position relation
+
+ let start: Vector3, end: Vector3;
+
+ if ( connection.contactPoint === 'start' ) {
+
+ start = TvMapQueries.getLanePosition(
+ connectingRoad.id,
+ link.to,
+ 0,
+ );
+
+ end = TvMapQueries.getLanePosition(
+ connectingRoad.id,
+ link.to,
+ connectingRoad.length,
+ );
+
+
+ } else {
+
+ start = TvMapQueries.getLanePosition(
+ connectingRoad.id,
+ link.to,
+ connectingRoad.length,
+ );
+
+ end = TvMapQueries.getLanePosition(
+ connectingRoad.id,
+ link.to,
+ 0,
+ );
+
+ }
+
+ // SceneService.add( new JunctionEntryObject( "start-dot", start, OdContactPoints.START, incomingRoad, link.from ) );
+ // SceneService.add( new JunctionEntryObject( "start-dot", end, OdContactPoints.END, incomingRoad, link.from ) );
+
+ if ( !link.lanePath ) {
+
+ link.lanePath = LanePathFactory.create( incomingRoad, connectingRoad, connection, link );
+
+ SceneService.add( link.lanePath );
+
+ } else {
+
+ link.lanePath.visible = true;
+
+ }
+
+
+ // //////////////////////////////////////////////////////////////
+
+ // const spline = connectingRoad.spline;
+
+ // const shape = new THREE.Shape(); shape.moveTo( 0, -0.3 ); shape.lineTo( 0, 0.3 );
+
+ // if ( spline.controlPointPositions.length < 2 ) return;
+
+ // let offset = targetLaneWidth;
+
+ // if ( targetLane.id < 0 ) offset *= -1;
+
+ // const path = new ExplicitSplinePath( spline as ExplicitSpline, offset );
+
+ // // const extrudeSettings = {
+ // // steps: path.getLength() * 2,
+ // // bevelEnabled: false,
+ // // extrudePath: path
+ // // };
+
+ // // const geometry = new THREE.ExtrudeGeometry( shape, extrudeSettings );
+ // // const material = new THREE.MeshBasicMaterial( {
+ // // color: 0x00ff00,
+ // // wireframe: false,
+ // // opacity: 0.5,
+ // // transparent: true,
+ // // map: OdTextures.asphalt
+ // // } );
+
+ // // link.mesh = new THREE.Mesh( geometry, material );
+
+ // // link.mesh.renderOrder = 3;
+
+ // // SceneService.add( link.mesh );
+
+ // const lineMaterial = new LineBasicMaterial( {
+ // color: 0x00ff00,
+ // linewidth: 100,
+ // opacity: 0.5,
+ // transparent: true,
+ // } );
+
+ // const lineGeometry = new BufferGeometry().setFromPoints( path.getSpacedPoints( 50 ) );
+
+ // link.mesh = new Line( lineGeometry, lineMaterial );
+
+ // link.mesh.castShadow = true;
+
+ // link.mesh.renderOrder = 3;
+
+ // link.mesh.frustumCulled = false;
+
+ // SceneService.add( link.mesh );
+ }
+
+ // create multiple roads
+ createStraightIntersectionRoad ( incomingRoad: TvRoad, sStart, sIntersection, sEnd, spline: AbstractSpline ) {
+
+
+ }
+
+ /**
+ *
+ * @param junctionId
+ * @param connection
+ * @deprecated not working
+ */
+ createStraightJunctionRoad ( junctionId: number, connection: IJunctionConnection ) {
+
+ const incomingRoad = this.openDrive.getRoadById( connection.entry.roadId );
+ const incomingLane = connection.entry.laneId;
+
+ const originalLenth = incomingRoad.length;
+
+ // change geometry of road 1
+ const startPos = new TvPosTheta();
+ const endPos = new TvPosTheta();
+
+ incomingRoad.getGeometryCoordsAt( connection.entry.s, 0, startPos );
+ incomingRoad.getGeometryCoordsAt( connection.exit.s, 0, endPos );
+
+ const laneSection = incomingRoad.getLaneSectionAt( connection.entry.s );
+ const geometry = incomingRoad.getGeometryAt( connection.entry.s );
+
+ // split
+ incomingRoad.split( connection.exit.s );
+
+ // connecting road
+
+ const connectingRoadLength = connection.exit.s - connection.entry.s;
+
+ const connectingRoad = new TvRoad( 'Connecting', connectingRoadLength, this.openDrive.roads.size, -1 );
+
+ connection.spline.exportGeometries().forEach( g => connectingRoad.addGeometry( g ) );
+
+
+ // outgoing road
+
+ const outgoingRoadLength = originalLenth - connection.exit.s;
+
+ const outgoingRoad = new TvRoad( 'Outgoing', outgoingRoadLength, this.openDrive.roads.size, -1 );
+
+
+ // update incoming finally
+
+ incomingRoad.length = connection.entry.s;
+
+ geometry.length = geometry.length - outgoingRoadLength - connectingRoadLength;
+
+ return {
+ incoming: incomingRoad,
+ connecting: connectingRoad,
+ outgoing: outgoingRoad
+ };
+
+ }
+}
diff --git a/src/app/core/tools/base-point-tool.ts b/src/app/core/tools/base-point-tool.ts
new file mode 100644
index 00000000..16795ab5
--- /dev/null
+++ b/src/app/core/tools/base-point-tool.ts
@@ -0,0 +1,6 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+export class BasePointTool {
+}
diff --git a/src/app/core/tools/base-road-plan-tool.ts b/src/app/core/tools/base-road-plan-tool.ts
new file mode 100644
index 00000000..05aa54f0
--- /dev/null
+++ b/src/app/core/tools/base-road-plan-tool.ts
@@ -0,0 +1,405 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseTool } from './base-tool';
+import { AbstractShapeEditor } from '../editors/abstract-shape-editor';
+import { TvRoad } from '../../modules/tv-map/models/tv-road.model';
+import { TvColors, TvLaneSide, TvLaneType, TvRoadMarkTypes, TvRoadMarkWeights } from '../../modules/tv-map/models/tv-common';
+import { TvMapSourceFile } from '../../modules/tv-map/services/tv-map-source-file';
+import { RoadInspector } from '../../views/inspectors/road-inspector/road-inspector.component';
+import { AppInspector } from '../inspector';
+import { PointEditor } from '../editors/point-editor';
+import { TvAbstractRoadGeometry } from '../../modules/tv-map/models/geometries/tv-abstract-road-geometry';
+import { RoadPlanHelper } from '../helpers/road-plan-helper';
+import { PointerEventData } from '../../events/pointer-event-data';
+import { Subscription } from 'rxjs';
+import { AnyControlPoint } from '../../modules/three-js/objects/control-point';
+import { KeyboardInput } from '../input';
+
+export class BaseRoadPlanTool extends BaseTool {
+
+ name: string = 'RoadPlan';
+
+ protected controlPointSelectedSubscriber: Subscription;
+ protected controlPointUnselectedSubscriber: Subscription;
+ protected controlPointAddedSubscriber: Subscription;
+ protected controlPointMovedSubscriber: Subscription;
+ protected controlPointUpdatedSubscriber: Subscription;
+
+ onKeyDownSub: Subscription;
+
+ protected shapeEditor: AbstractShapeEditor;
+ protected road: TvRoad;
+ protected roadAdded: boolean;
+ protected helper: RoadPlanHelper;
+
+ constructor () {
+
+ super();
+
+ this.shapeEditor = new PointEditor();
+ }
+
+ init () {
+
+ super.init();
+
+ }
+
+ enable (): void {
+
+ super.enable();
+
+ this.controlPointSelectedSubscriber = this.shapeEditor.controlPointSelected.subscribe( e => this.onControlPointSelected( e ) );
+ this.controlPointUnselectedSubscriber = this.shapeEditor.controlPointUnselected.subscribe( e => this.onControlPointUnselected( e ) );
+ this.controlPointAddedSubscriber = this.shapeEditor.controlPointAdded.subscribe( e => this.onControlPointAdded( e ) );
+ this.controlPointMovedSubscriber = this.shapeEditor.controlPointMoved.subscribe( e => this.onControlPointMoved( e ) );
+ this.controlPointUpdatedSubscriber = this.shapeEditor.controlPointUpdated.subscribe( e => this.onControlPointUpdated( e ) );
+
+ this.onKeyDownSub = KeyboardInput.keyDown.subscribe( e => {
+
+ if ( e.key === "Delete" ) {
+
+ this.unselectRoad( this.road );
+
+ this.road.remove( TvMapSourceFile.openDrive.gameObject );
+
+ TvMapSourceFile.openDrive.deleteRoad( this.road.id );
+
+ this.road = null;
+
+ }
+
+ } )
+ }
+
+ disable (): void {
+
+ super.disable();
+
+ this.controlPointSelectedSubscriber.unsubscribe();
+ this.controlPointUnselectedSubscriber.unsubscribe();
+ this.controlPointAddedSubscriber.unsubscribe();
+ this.controlPointMovedSubscriber.unsubscribe();
+ this.controlPointUpdatedSubscriber.unsubscribe();
+
+ if ( this.onKeyDownSub ) this.onKeyDownSub.unsubscribe();
+
+ this.shapeEditor.destroy();
+
+ }
+
+ onPointerClicked ( e: PointerEventData ) {
+
+ let hasInteracted = false;
+
+ this.checkControlPointIntersection( e.intersections, ( point ) => {
+
+ hasInteracted = true;
+
+ this.shapeEditor.selectControlPoint( point as AnyControlPoint );
+
+ } );
+
+ if ( hasInteracted ) return;
+
+ this.checkRoadIntersection( e.intersections, ( object ) => {
+
+ hasInteracted = true;
+
+ this.selectRoad( object.userData.road );
+
+ } );
+
+ if ( hasInteracted ) return;
+
+ // Finally, If no objects were intersected then clear the inspector
+ if ( !KeyboardInput.isShiftKeyDown ) this.clickedOutside();
+ }
+
+ protected clickedOutside () {
+
+ this.road == null;
+
+ AppInspector.clear()
+
+ // this.shapeEditor.removeAllControlPoints();
+
+ }
+
+ onPointerMoved ( e: PointerEventData ) {
+
+ let hoveringPoint: AnyControlPoint = null;
+
+ let selectectPoint: AnyControlPoint = this.shapeEditor.currentPoint;
+
+ this.checkControlPointIntersection( e.intersections, cp => hoveringPoint = cp );
+
+ let points: AnyControlPoint[] = [];
+
+ if ( selectectPoint ) {
+
+ points = this.shapeEditor.controlPoints.filter( cp => cp.id != selectectPoint.id );
+
+ } else {
+
+ points = this.shapeEditor.controlPoints;
+
+ }
+
+ if ( hoveringPoint ) {
+
+ if ( !selectectPoint || selectectPoint.id != hoveringPoint.id )
+ this.shapeEditor.onControlPointHovered( hoveringPoint );
+
+ points = points.filter( i => i.id != hoveringPoint.id );
+
+ }
+
+ points.forEach( p => this.shapeEditor.onControlPointUnhovered( p ) );
+
+ }
+
+ protected onControlPointSelected ( e: AnyControlPoint ) { }
+
+ protected onControlPointUnselected ( e: AnyControlPoint ) { }
+
+ protected onControlPointAdded ( e: AnyControlPoint ) { }
+
+ protected onControlPointMoved ( e: AnyControlPoint ) { }
+
+ protected onControlPointUpdated ( e: AnyControlPoint ) { }
+
+ protected getRoadIdFromControlPoint ( e: AnyControlPoint ) {
+ return e.userData.roadId;
+ }
+
+ protected setRoadIdOnControlPoint ( e: AnyControlPoint, road: TvRoad ) {
+ e.userData.roadId = road.id
+ }
+
+ //
+ // // TODO: Handle multiple openDrive geometry type
+ // // currently only handles bezier cube
+ // onRoadGeometryChanged ( e: THREE.Curve ) {
+ //
+ // // this.lineGeometry( e as LineCurve3 );
+ //
+ // // this.addParamPoly3( e as CubicBezierCurve3 );
+ //
+ // }
+ //
+ // onRoadGeometryAdded ( e: THREE.Curve ) {
+ //
+ //
+ // }
+ //
+ // lineGeometry ( line: LineCurve3 ) {
+ //
+ // // return;
+ //
+ // // TODO: Do this from openDrive class
+ // // this.road = new OdRoad( 'road', 0, 1, -1 );
+ //
+ // const p1 = line.v1;
+ // const p2 = line.v2;
+ // const hdg = Math.atan2( p2.y - p1.y, p2.x - p1.x );
+ // const length = line.getLength();
+ //
+ //
+ // if ( !this.roadAdded ) {
+ //
+ // const roadId = this.openDrive.getRoadCount() + 2;
+ //
+ // this.road = this.openDrive.addRoad( '', length, roadId, -1 );
+ //
+ // this.road.addPlanView();
+ //
+ // this.addDefaultLanes( this.road );
+ //
+ // const s = 0;
+ //
+ // this.road.planView.addGeometryLine( s, p1.x, p1.y, hdg, length );
+ //
+ // this.roadAdded = true;
+ //
+ // } else {
+ //
+ // this.road.attr_length = length;
+ //
+ // const last = this.road.planView.geometries[ this.road.planView.geometries.length - 1 ];
+ //
+ // last.x = p1.x;
+ // last.y = p1.y;
+ // last.hdg = hdg;
+ // last.length = length;
+ //
+ // // this.road.planView.addGeometryLine( s, p1.x, p1.y, hdg, length );
+ // }
+ //
+ // OdSourceFile.roadNetworkChanged.emit( OdSourceFile.openDrive );
+ // }
+
+ public addDefaultLanes ( road: TvRoad ) {
+
+ road.addLaneSection( 0, false );
+
+ const laneSection = road.getLastAddedLaneSection();
+
+ const leftLane3 = laneSection.addLane( TvLaneSide.LEFT, 3, TvLaneType.sidewalk, true, true );
+ const leftLane2 = laneSection.addLane( TvLaneSide.LEFT, 2, TvLaneType.shoulder, true, true );
+ const leftLane1 = laneSection.addLane( TvLaneSide.LEFT, 1, TvLaneType.driving, true, true );
+ const centerLane = laneSection.addLane( TvLaneSide.CENTER, 0, TvLaneType.driving, true, true );
+ const rightLane1 = laneSection.addLane( TvLaneSide.RIGHT, -1, TvLaneType.driving, true, true );
+ const rightLane2 = laneSection.addLane( TvLaneSide.RIGHT, -2, TvLaneType.shoulder, true, true );
+ const rightLane3 = laneSection.addLane( TvLaneSide.RIGHT, -3, TvLaneType.sidewalk, true, true );
+
+ leftLane1.addRoadMarkRecord( 0, TvRoadMarkTypes.NONE, TvRoadMarkWeights.STANDARD, TvColors.STANDARD, 0.15, 'none', 0 );
+ centerLane.addRoadMarkRecord( 0, TvRoadMarkTypes.BROKEN, TvRoadMarkWeights.STANDARD, TvColors.STANDARD, 0.15, 'none', 0 );
+ rightLane1.addRoadMarkRecord( 0, TvRoadMarkTypes.NONE, TvRoadMarkWeights.STANDARD, TvColors.STANDARD, 0.15, 'none', 0 );
+
+ laneSection.getLaneVector().forEach( lane => {
+
+ if ( lane.side !== TvLaneSide.CENTER ) {
+
+ if ( lane.type == TvLaneType.driving ) lane.addWidthRecord( 0, 3.6, 0, 0, 0 );
+
+ else if ( lane.type == TvLaneType.sidewalk ) lane.addWidthRecord( 0, 2, 0, 0, 0 );
+
+ else lane.addWidthRecord( 0, 0.5, 0, 0, 0 );
+
+ }
+
+ } );
+
+ this.selectRoad( road );
+
+ }
+
+ protected selectRoad ( road: TvRoad ) {
+
+ this.unselectRoad( this.road );
+
+ this.road = road;
+
+ // skip if the same road is selected
+ // if ( road.id == this.road.id ) return;
+
+ let controlPoint = this.shapeEditor.currentPoint;
+
+ let node = null;
+
+ AppInspector.setInspector( RoadInspector, { road, controlPoint, node } );
+
+ this.showRoadControlPoints( road, 'auto' );
+ }
+
+ protected unselectRoad ( road: TvRoad ) {
+
+ if ( !road ) return;
+
+ this.road.spline.hide();
+
+ }
+
+ protected selectRoadControlPoint ( controlPoint?: AnyControlPoint, node?: any ) {
+
+ // skip if the same control point is selected
+ // if ( this.shapeEditor.currentPoint.id == controlPoint.id ) return;
+
+ let road = this.road;
+
+ AppInspector.setInspector( RoadInspector, { road, controlPoint, node } );
+ }
+
+ protected showRoadControlPoints ( road: TvRoad, type: 'auto' | 'explicit' = 'auto' ) {
+
+ road.getPlanView()
+
+ if ( type == 'auto' ) {
+
+ //
+
+ }
+
+ }
+
+ // private addParamPoly3 ( curve: CubicBezierCurve3 ) {
+ //
+ // const x = curve.v0.x;
+ // const y = curve.v0.y;
+ //
+ // const hdg = 0;
+ // const length = curve.getLength();
+ //
+ // const a = new Vector3();
+ // const b = curve.v1;
+ // const c = curve.v2;
+ // const d = curve.v3;
+ //
+ // if ( !this.roadAdded ) {
+ //
+ // const roadId = this.openDrive.getRoadCount() + 2;
+ //
+ // this.road = this.openDrive.addRoad( '', length, roadId, -1 );
+ //
+ // this.road.addPlanView();
+ //
+ // this.addDefaultLanes( this.road );
+ //
+ // const s = 0;
+ //
+ // this.road.planView.addGeometryParamPoly3( 0, x, y, hdg, length, a.x, b.x, c.x, d.x, a.y, b.y, c.y, d.y );
+ // // this.road.planView.addGeometryPoly3( 0, x, y, hdg, length, a.y, b.y, c.y, d.y );
+ //
+ // this.roadAdded = true;
+ //
+ // } else {
+ //
+ // this.road.attr_length = length;
+ //
+ // const last = this.road.planView.geometries[ this.road.planView.geometries.length - 1 ] as OdParamPoly3Geometry;
+ //
+ // last.x = x;
+ // last.y = y;
+ // last.hdg = hdg;
+ // last.length = length;
+ //
+ // last.aU = a.x;
+ // last.bU = b.x;
+ // last.cU = c.x;
+ // last.dU = d.x;
+ //
+ // last.aV = a.y;
+ // last.bV = b.y;
+ // last.cV = c.y;
+ // last.dV = d.y;
+ //
+ // // this.road.planView.addGeometryLine( s, p1.x, p1.y, hdg, length );
+ // }
+ //
+ // OdSourceFile.roadNetworkChanged.emit( OdSourceFile.openDrive );
+ //
+ // }
+
+
+ protected createRoad () {
+
+ const roadId = this.openDrive.getRoadCount() + 1;
+ const road = this.openDrive.addRoad( '', 0, roadId, -1 );
+
+ road.addPlanView();
+
+ this.addDefaultLanes( road );
+
+ return road;
+ }
+
+ private addGeometry ( road: TvRoad, geometry: TvAbstractRoadGeometry ) {
+
+ road.planView.addGeometry( geometry );
+
+ TvMapSourceFile.roadNetworkChanged.emit( TvMapSourceFile.openDrive );
+ }
+}
diff --git a/src/app/core/tools/base-tool.ts b/src/app/core/tools/base-tool.ts
new file mode 100644
index 00000000..023020a1
--- /dev/null
+++ b/src/app/core/tools/base-tool.ts
@@ -0,0 +1,143 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { IComponent } from 'app/core/game-object';
+import { MonoBehaviour } from '../components/mono-behaviour';
+import { AppInspector } from 'app/core/inspector';
+import { Type } from '@angular/core';
+import { TvMapSourceFile } from '../../modules/tv-map/services/tv-map-source-file';
+import { IEditorState } from './i-editor-state';
+import { Color, Intersection, Mesh, MeshBasicMaterial, Object3D } from 'three';
+import { ObjectTypes } from '../../modules/tv-map/models/tv-common';
+import { AnyControlPoint } from '../../modules/three-js/objects/control-point';
+
+export abstract class BaseTool extends MonoBehaviour implements IEditorState {
+
+ abstract name: string;
+
+ // highlighting variables
+ private previousColor = new Color();
+ private previousMaterial: MeshBasicMaterial;
+
+ constructor () {
+
+ super();
+
+ this.clearInspector();
+
+ }
+
+ get openDrive () {
+
+ return TvMapSourceFile.openDrive;
+
+ }
+
+ init () {
+
+ }
+
+ enable (): void {
+
+ this.subscribeToEvents();
+
+ }
+
+ disable (): void {
+
+ this.unsubscribeToEvents();
+
+ this.removeHighlight();
+
+ }
+
+ clearInspector () {
+
+ AppInspector.clear();
+
+ }
+
+ setInspector ( component: Type, data: any ) {
+
+ AppInspector.setInspector( component, data );
+
+ }
+
+ protected checkRoadIntersection ( intersections: Intersection[], callback: ( object: Object3D ) => void ): void {
+
+ this.checkIntersection( ObjectTypes.LANE, intersections, ( obj ) => {
+
+ callback( obj.parent.parent );
+
+ } );
+
+ }
+
+
+ protected checkLaneIntersection ( intersections: Intersection[], callback: ( object: Object3D ) => void ) {
+
+ this.checkIntersection( ObjectTypes.LANE, intersections, callback );
+
+ }
+
+ protected checkVehicleIntersection ( intersections: Intersection[], callback: ( object: Object3D ) => void ) {
+
+ this.checkIntersection( ObjectTypes.VEHICLE, intersections, callback );
+
+ }
+
+ protected checkControlPointIntersection ( intersections: Intersection[], callback: ( object: AnyControlPoint ) => void ) {
+
+ for ( const i of intersections ) {
+
+ if ( i.object != null && i.object.type == 'Points' ) {
+
+ callback( i.object as AnyControlPoint );
+
+ break;
+ }
+ }
+ }
+
+ protected checkIntersection ( tag: string, intersections: Intersection[], callback: ( object: Object3D ) => void ): void {
+
+ for ( const i of intersections ) {
+
+ if ( i.object[ 'tag' ] == tag ) {
+
+ callback( i.object );
+
+ break;
+ }
+
+ }
+
+ }
+
+ protected highlight ( object: Mesh ) {
+
+ const material = ( object.material as MeshBasicMaterial );
+
+ // clone because we want a new instance
+ this.previousColor = material.color.clone();
+
+ // set the current material property to highlighted color
+ material.color.add( new Color( 0, 0, 0.2 ) );
+ // material.color.addScalar(0.1)
+ // material.opacity = 0.6;
+
+ // dont clone we want the same instance
+ this.previousMaterial = material;
+ }
+
+ protected removeHighlight () {
+
+ if ( this.previousMaterial == null ) return;
+
+ this.previousMaterial.color.copy( this.previousColor );
+
+ }
+
+}
+
diff --git a/src/app/core/tools/i-editor-state.ts b/src/app/core/tools/i-editor-state.ts
new file mode 100644
index 00000000..44bf1e23
--- /dev/null
+++ b/src/app/core/tools/i-editor-state.ts
@@ -0,0 +1,41 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseEventData, PointerEventData, PointerMoveData } from '../../events/pointer-event-data';
+
+export interface IEditorState {
+
+ enable (): void;
+
+ disable (): void;
+
+ onPointerClicked ( e: PointerEventData ): void;
+
+ onPointerUp ( e: PointerEventData ): void;
+
+ onPointerDown ( e: PointerEventData ): void;
+
+ onPointerEnter ( e: PointerEventData ): void;
+
+ onPointerExit ( e: PointerEventData ): void;
+
+ onPointerOut ( e: PointerEventData ): void;
+
+ onPointerLeave ( e: PointerEventData ): void;
+
+ onBeginDrag ( e: PointerEventData ): void;
+
+ onEndDrag ( e: PointerEventData ): void;
+
+ onDrag ( e: PointerEventData ): void;
+
+ onDrop ( e: PointerEventData ): void;
+
+ onPointerMoved ( e: PointerMoveData ): void;
+
+ onSelect ( e: BaseEventData ): void;
+
+ onDeSelect ( e: BaseEventData ): void;
+
+}
diff --git a/src/app/core/tools/lane-add-tool.ts b/src/app/core/tools/lane-add-tool.ts
new file mode 100644
index 00000000..3f443c30
--- /dev/null
+++ b/src/app/core/tools/lane-add-tool.ts
@@ -0,0 +1,161 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseTool } from './base-tool';
+import { MouseButton, PointerEventData } from '../../events/pointer-event-data';
+import { TvLane } from '../../modules/tv-map/models/tv-lane';
+import { TvLaneSide } from '../../modules/tv-map/models/tv-common';
+import { KeyboardInput } from '../input';
+import { PickingHelper } from '../services/picking-helper.service';
+import { LineType, OdLaneReferenceLineBuilder } from 'app/modules/tv-map/builders/od-lane-reference-line-builder';
+import { COLOR } from 'app/shared/utils/colors.service';
+import { TvRoad } from 'app/modules/tv-map/models/tv-road.model';
+import { SceneService } from '../services/scene.service';
+import { TvMapBuilder } from 'app/modules/tv-map/builders/od-builder.service';
+import { AppInspector } from '../inspector';
+import { LaneInspectorComponent } from 'app/views/inspectors/lane-type-inspector/lane-inspector.component';
+
+export class LaneAddTool extends BaseTool {
+
+ public name: string = 'AddLane';
+
+ private lane: TvLane;
+
+ private laneHelper = new OdLaneReferenceLineBuilder( null, LineType.SOLID, COLOR.MAGENTA );
+
+ constructor () {
+
+ super();
+
+ }
+
+ enable (): void {
+
+ super.enable();
+
+ }
+
+ disable (): void {
+
+ super.disable();
+
+ if ( this.laneHelper ) this.laneHelper.clear();
+ }
+
+ public onPointerDown ( e: PointerEventData ) {
+
+ if ( e.button == MouseButton.RIGHT || e.button == MouseButton.MIDDLE ) return;
+
+ const shiftKeyDown = KeyboardInput.isShiftKeyDown;
+
+ let hasInteracted = false;
+
+ if ( shiftKeyDown && !hasInteracted ) hasInteracted = this.checkReferenceLineInteraction( e );
+
+ if ( !hasInteracted ) hasInteracted = this.checkLaneObjectInteraction( e );
+
+ }
+
+ private checkLaneObjectInteraction ( e: PointerEventData ): boolean {
+
+ const newLane = PickingHelper.checkLaneObjectInteraction( e );
+
+ if ( this.lane && newLane == null ) {
+
+ // clear
+
+ this.laneHelper.clear();
+
+ this.lane = null;
+
+ AppInspector.clear();
+
+ } else if ( this.lane && newLane && this.lane.gameObject.id != newLane.gameObject.id ) {
+
+ // clear and select new
+
+ this.laneHelper.clear();
+
+ this.lane = newLane;
+
+ const newRoad = this.openDrive.getRoadById( newLane.roadId );
+
+ this.laneHelper.drawRoad( newRoad, LineType.SOLID );
+
+ AppInspector.setInspector( LaneInspectorComponent, newLane );
+
+ } else if ( !this.lane && newLane ) {
+
+ // select new
+
+ this.lane = newLane;
+
+ const newRoad = this.openDrive.getRoadById( newLane.roadId );
+
+ this.laneHelper.drawRoad( newRoad, LineType.SOLID );
+
+ AppInspector.setInspector( LaneInspectorComponent, newLane );
+
+ } else if ( !this.lane && newLane == null ) {
+
+ // clear
+
+ AppInspector.clear();
+
+ }
+
+ return newLane != null;
+ }
+
+ private checkReferenceLineInteraction ( e: PointerEventData ) {
+
+ let hasInteracted = false;
+
+ for ( let i = 0; i < e.intersections.length; i++ ) {
+
+ const intersection = e.intersections[ i ];
+
+ if ( e.button === MouseButton.LEFT && intersection.object && intersection.object[ 'tag' ] == this.laneHelper.tag ) {
+
+ hasInteracted = true;
+
+ if ( intersection.object.userData.lane ) {
+
+ this.cloneLane( intersection.object.userData.lane as TvLane );
+
+ }
+
+ break;
+ }
+ }
+
+ return hasInteracted;
+ }
+
+ private cloneLane ( lane: TvLane ): void {
+
+ const road = this.openDrive.getRoadById( lane.roadId );
+
+ const laneSection = road.getLaneSectionById( lane.laneSectionId );
+
+ const newLaneId = lane.side === TvLaneSide.LEFT ? lane.id + 1 : lane.id - 1;
+
+ const newLane = lane.clone( newLaneId );
+
+ laneSection.addLaneInstance( newLane, true );
+
+ this.rebuild( road );
+ }
+
+ private rebuild ( road: TvRoad ): void {
+
+ if ( !road ) return;
+
+ SceneService.removeWithChildren( road.gameObject, true );
+
+ TvMapBuilder.buildRoad( this.openDrive.gameObject, road );
+
+ this.laneHelper.redraw( LineType.SOLID );
+ }
+}
diff --git a/src/app/core/tools/lane-direction-tool.ts b/src/app/core/tools/lane-direction-tool.ts
new file mode 100644
index 00000000..d96e1919
--- /dev/null
+++ b/src/app/core/tools/lane-direction-tool.ts
@@ -0,0 +1,70 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseTool } from './base-tool';
+import { PointerEventData } from '../../events/pointer-event-data';
+import { Mesh, Object3D } from 'three';
+import { TvLane } from '../../modules/tv-map/models/tv-lane';
+import { OdLaneDirectionBuilder } from '../../modules/tv-map/builders/od-lane-direction-builder';
+
+export class LaneDirectionTool extends BaseTool {
+
+ name: string = 'LaneDirection';
+
+ private laneDirectionHelper: OdLaneDirectionBuilder;
+
+ constructor () {
+
+ super();
+
+ }
+
+ init () {
+
+ super.init();
+
+ this.laneDirectionHelper = new OdLaneDirectionBuilder( null );
+ }
+
+
+ disable (): void {
+
+ super.disable();
+
+ this.laneDirectionHelper.clear();
+ }
+
+ onPointerDown ( e: PointerEventData ) {
+
+ super.onPointerDown( e );
+
+ let laneFound = false;
+
+ this.checkLaneIntersection( e.intersections, ( object: Object3D ) => {
+
+ laneFound = true;
+
+ this.selectLane( object as Mesh );
+
+ } );
+
+ if ( !laneFound ) {
+
+ this.laneDirectionHelper.clear();
+ this.clearInspector();
+ }
+ }
+
+ private selectLane ( object: Mesh ) {
+
+ let lane = (object.userData.lane as TvLane);
+
+ this.laneDirectionHelper.setRoad( this.openDrive.getRoadById( lane.roadId ) );
+
+ this.laneDirectionHelper.create();
+
+ // AppInspector.setInspector( LaneTypeInspectorComponent, data );
+ }
+
+}
diff --git a/src/app/core/tools/lane-marking-tool.spec.ts b/src/app/core/tools/lane-marking-tool.spec.ts
new file mode 100644
index 00000000..f48e8c04
--- /dev/null
+++ b/src/app/core/tools/lane-marking-tool.spec.ts
@@ -0,0 +1,139 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { LaneMarkingTool } from './lane-marking-tool';
+import { PointerEventData, MouseButton } from 'app/events/pointer-event-data';
+import { Vector3 } from 'three';
+import { TvMap } from 'app/modules/tv-map/models/tv-map.model';
+import { OdRoadBuilder } from 'app/modules/tv-map/builders/od-road-builder';
+import { TvMapBuilder } from 'app/modules/tv-map/builders/od-builder.service';
+import { ViewportComponent } from 'app/modules/three-js/viewport/viewport.component';
+import { ThemeService } from 'app/shared/services/theme.service';
+import { EventSystem } from 'app/events/event-system.service';
+import { InputService } from '../services/input.service';
+import { ThreeService } from 'app/modules/three-js/three.service';
+import { TvRoad } from 'app/modules/tv-map/models/tv-road.model';
+import { TvMapSourceFile } from 'app/modules/tv-map/services/tv-map-source-file';
+import { AppInspector } from '../inspector';
+
+
+fdescribe( 'LaneMarkingTool Test', () => {
+
+ let tool: LaneMarkingTool;
+ let map: TvMap;
+ let road: TvRoad;
+
+ beforeEach( () => {
+
+ tool = new LaneMarkingTool();
+
+ map = TvMapSourceFile.openDrive = new TvMap();
+
+ road = map.addDefaultRoad();
+
+ road.addGeometryLine( 0, 0, 0, 0, 100 );
+
+ TvMapBuilder.buildMap( map );
+
+ } );
+
+ it( 'should do nothing on right-click', () => {
+
+ tool.onPointerDown( PointerEventData.new( MouseButton.RIGHT, new Vector3() ) );
+
+ expect( tool.lane ).toBeUndefined();
+ expect( tool.controlPoint ).toBeUndefined();
+ expect( tool.node ).toBeUndefined();
+ expect( tool.roadMark ).toBeUndefined();
+
+ expect( tool.pointerDown ).toBeTruthy();
+ expect( tool.pointerDownAt ).toBeUndefined();
+
+ } );
+
+ it( 'should set right tool variables on left-click', () => {
+
+ tool.onPointerDown( PointerEventData.new( MouseButton.LEFT, new Vector3() ) );
+
+ expect( tool.lane ).toBeNull();
+ expect( tool.controlPoint ).toBeNull();
+ expect( tool.node ).toBeNull();
+
+ expect( tool.pointerDown ).toBeTruthy();
+ expect( tool.pointerDownAt ).toBeDefined();
+
+ } );
+
+ it( 'should select lane & highlight reference lines', () => {
+
+ const selectedLane = road.getLaneSectionAt( 0 ).getLeftLanes()[ 0 ];
+
+ const intersection = {
+ distance: 1,
+ distanceToRay: 1,
+ point: new Vector3(),
+ object: selectedLane.gameObject,
+ }
+
+ tool.onPointerDown( PointerEventData.new( MouseButton.LEFT, new Vector3(), [ intersection ] ) );
+
+ expect( tool.lane ).toBeDefined();
+ expect( tool.lane.id ).toEqual( selectedLane.id );
+
+ expect( tool.controlPoint ).toBeUndefined();
+ expect( tool.node ).toBeUndefined();
+
+ expect( tool.pointerDown ).toBeTruthy();
+ expect( tool.pointerDownAt ).toBeDefined();
+
+ road.getLaneSectionAt( 0 ).lanes.forEach( lane => {
+
+ // now lets check if the lane lines are visible on all the lanes
+ expect( lane.startLine ).toBeDefined();
+ expect( lane.startLine.visible ).toBeTruthy();
+ expect( lane.startLine.isSelected ).toBeFalsy();
+
+ } );
+
+ expect( AppInspector.currentInspector ).toBeNull();
+
+ } );
+
+ it( 'should select lane & highlight reference lines', () => {
+
+ const selectedLane = road.getLaneSectionAt( 0 ).getLeftLanes()[ 0 ];
+
+ const laneIntersection = {
+ distance: 1,
+ distanceToRay: 1,
+ point: new Vector3(),
+ object: selectedLane.gameObject,
+ }
+
+ tool.onPointerDown( PointerEventData.new( MouseButton.LEFT, new Vector3(), [ laneIntersection ] ) );
+
+ expect( tool.lane ).toBeDefined();
+ expect( tool.lane.id ).toEqual( selectedLane.id );
+
+ // at this point lane is selected and reference lines should be visible
+
+ const lineIntersection = {
+ distance: 1,
+ distanceToRay: 1,
+ point: new Vector3(),
+ object: selectedLane.startLine,
+ }
+
+ tool.onPointerDown( PointerEventData.new( MouseButton.LEFT, new Vector3(), [ lineIntersection ] ) );
+
+ // at this point line lane line should be selected
+
+ expect( tool.lane.startLine.isSelected ).toBe( true );
+
+ expect( tool.roadMark ).toBeDefined();
+
+
+ } );
+
+} );
\ No newline at end of file
diff --git a/src/app/core/tools/lane-marking-tool.ts b/src/app/core/tools/lane-marking-tool.ts
new file mode 100644
index 00000000..7b61c406
--- /dev/null
+++ b/src/app/core/tools/lane-marking-tool.ts
@@ -0,0 +1,383 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseTool } from './base-tool';
+import { Vector3 } from 'three';
+import { MouseButton, PointerEventData } from '../../events/pointer-event-data';
+import { TvLane } from '../../modules/tv-map/models/tv-lane';
+import { LaneRoadmarkInspectorComponent } from '../../views/inspectors/lane-roadmark-inspector/lane-roadmark-inspector.component';
+import { TvLaneRoadMark } from '../../modules/tv-map/models/tv-lane-road-mark';
+import { TvPosTheta } from '../../modules/tv-map/models/tv-pos-theta';
+import { KeyboardInput } from '../input';
+import { OdRoadMarkBuilder } from '../../modules/tv-map/builders/od-road-mark-builder';
+import { AnyControlPoint, LaneRoadMarkNode } from '../../modules/three-js/objects/control-point';
+import { OdLaneReferenceLineBuilder } from '../../modules/tv-map/builders/od-lane-reference-line-builder';
+import { TvMapQueries } from '../../modules/tv-map/queries/tv-map-queries';
+import { PickingHelper } from '../services/picking-helper.service';
+import { LaneInspectorComponent } from 'app/views/inspectors/lane-type-inspector/lane-inspector.component';
+import { SceneService } from '../services/scene.service';
+import { NodeFactoryService } from '../factories/node-factory.service';
+import { TvRoad } from 'app/modules/tv-map/models/tv-road.model';
+import { SetValueCommand } from 'app/modules/three-js/commands/set-value-command';
+import { ShowLaneMarkingCommand } from '../commands/show-lane-marking-command';
+import { CommandHistory } from 'app/services/command-history';
+import { MultiCmdsCommand } from '../commands/multi-cmds-command';
+import { SetInspectorCommand } from '../commands/set-inspector-command';
+import { AddRoadmarkNodeCommand } from '../commands/add-roadmark-node';
+import { UpdateRoadmarkNodeCommand } from '../commands/update-roadmark-node';
+
+export class LaneMarkingTool extends BaseTool {
+
+ public name: string = 'LaneMarking';
+
+ public pointerDown: boolean;
+
+ public pointerDownAt: Vector3;
+
+ public pointerObject: any;
+
+ public markingDistanceChanged: boolean;
+
+ private lane: TvLane;
+
+ private roadMark: TvLaneRoadMark;
+
+ private controlPoint: AnyControlPoint;
+
+ private node: LaneRoadMarkNode;
+
+ private roadMarkBuilder = new OdRoadMarkBuilder();
+
+ private laneHelper = new OdLaneReferenceLineBuilder( null );
+
+ constructor () {
+
+ super();
+
+ }
+
+ init () {
+
+ super.init();
+
+ }
+
+ enable (): void {
+
+ super.enable();
+
+ }
+
+ disable (): void {
+
+ super.disable();
+
+ if ( this.laneHelper ) this.laneHelper.clear();
+
+ this.openDrive.roads.forEach( road => this.hideNodes( road ) );
+ }
+
+ public onPointerDown ( e: PointerEventData ) {
+
+ this.pointerDown = true;
+
+ if ( e.button === MouseButton.RIGHT || e.button === MouseButton.MIDDLE ) return;
+
+ this.pointerDownAt = e.point ? e.point.clone() : null;
+
+ const shiftKeyDown = KeyboardInput.isShiftKeyDown;
+
+ let hasInteracted = false;
+
+ if ( !shiftKeyDown && !hasInteracted ) hasInteracted = this.checkNodePointInteraction( e );
+
+ if ( !hasInteracted ) hasInteracted = this.checkReferenceLineInteraction( e );
+
+ if ( !hasInteracted ) hasInteracted = this.checkLaneObjectInteraction( e );
+
+ if ( !hasInteracted ) {
+
+ this.node = null;
+
+ this.lane = null;
+
+ this.controlPoint = null;
+
+ }
+ }
+
+ public onPointerUp ( e: PointerEventData ) {
+
+ if ( this.markingDistanceChanged && this.node ) {
+
+ // e.point is null for some reason, so use node position
+ // const newPosition = e.point.clone();
+
+ // use the current positino of the node as the new position
+ const newPosition = this.node.point.position.clone();
+
+ // old position is the location where the mouse was last down
+ const oldPosition = this.pointerDownAt.clone();
+
+ const command = new UpdateRoadmarkNodeCommand( this.node, newPosition, oldPosition, this.roadMarkBuilder );
+
+ CommandHistory.execute( command );
+
+ }
+
+ this.pointerDown = false;
+
+ this.pointerDownAt = null;
+
+ this.pointerObject = null;
+
+ this.markingDistanceChanged = false;
+ }
+
+ public onPointerMoved ( e: PointerEventData ) {
+
+ if ( this.pointerDown && this.node && this.node.point.isSelected ) {
+
+ this.markingDistanceChanged = true;
+
+ NodeFactoryService.updateRoadMarkNodeByPosition( this.node, e.point );
+
+ }
+ }
+
+ private checkNodePointInteraction ( e: PointerEventData ): boolean {
+
+ const maxDistance = Math.max( 0.5, e.approxCameraDistance * 0.01 );
+
+ // first chceck for control point interactions
+ // doing in 2 loop to prioritise control points
+ const controlPoint = PickingHelper.checkControlPointInteraction( e, LaneRoadMarkNode.pointTag, maxDistance );
+
+ if ( controlPoint ) {
+
+ const node = controlPoint.parent as LaneRoadMarkNode;
+
+ const roadMark = node.roadmark;
+
+ CommandHistory.executeAll( [
+
+ new SetValueCommand( this, 'controlPoint', controlPoint ),
+
+ new SetValueCommand( this, 'node', node ),
+
+ new SetInspectorCommand( LaneRoadmarkInspectorComponent, roadMark )
+
+ ] );
+
+ } else if ( !this.controlPoint && this.controlPoint ) {
+
+ CommandHistory.executeAll( [
+
+ new SetValueCommand( this, 'controlPoint', null ),
+
+ new SetValueCommand( this, 'node', null ),
+
+ new SetInspectorCommand( null, null )
+
+ ] );
+
+ }
+
+ return controlPoint != null;
+
+ }
+
+ private checkLaneObjectInteraction ( e: PointerEventData ): boolean {
+
+ const newLane = PickingHelper.checkLaneObjectInteraction( e );
+
+ if ( this.lane && newLane == null ) {
+
+ // clear
+
+ const commands = [];
+
+ commands.push( new ShowLaneMarkingCommand( null, this.lane, this.laneHelper ) );
+
+ commands.push( new SetInspectorCommand( null, null ) );
+
+ commands.push( new SetValueCommand( this, 'lane', null ) );
+
+ CommandHistory.execute( new MultiCmdsCommand( commands ) );
+
+ } else if ( this.lane && newLane && this.lane.gameObject.id !== newLane.gameObject.id && this.lane.roadId != newLane.roadId ) {
+
+ // clear and select new
+
+ const commands = [];
+
+ commands.push( new ShowLaneMarkingCommand( newLane, this.lane, this.laneHelper ) );
+
+ commands.push( new SetInspectorCommand( LaneInspectorComponent, newLane ) );
+
+ commands.push( new SetValueCommand( this, 'lane', newLane ) );
+
+ CommandHistory.execute( new MultiCmdsCommand( commands ) );
+
+ } else if ( !this.lane && newLane ) {
+
+ // select new
+
+ const commands = [];
+
+ commands.push( new SetValueCommand( this, 'lane', newLane ) );
+
+ commands.push( new ShowLaneMarkingCommand( newLane, this.lane, this.laneHelper ) );
+
+ commands.push( new SetInspectorCommand( LaneInspectorComponent, newLane ) );
+
+ CommandHistory.execute( new MultiCmdsCommand( commands ) );
+
+ } else if ( !this.lane && newLane == null ) {
+
+ // clear
+
+ CommandHistory.execute( new SetInspectorCommand( null, null ) );
+
+ }
+
+ return newLane != null;
+ }
+
+ private checkReferenceLineInteraction ( e: PointerEventData ) {
+
+ const isShiftKeyDown = KeyboardInput.isShiftKeyDown;
+
+ let hasInteracted = false;
+
+ for ( let i = 0; i < e.intersections.length; i++ ) {
+
+ const intersection = e.intersections[ i ];
+
+ if ( e.button === MouseButton.LEFT && intersection.object && intersection.object[ 'tag' ] == this.laneHelper.tag ) {
+
+ hasInteracted = true;
+
+ if ( intersection.object.userData.lane ) {
+
+ this.lane = intersection.object.userData.lane;
+
+ if ( isShiftKeyDown ) {
+
+ this.addRoadmarkNodeAt( e.point );
+
+ } else {
+
+ this.selectRoadMarkAt( e.point, this.lane );
+
+ }
+
+ }
+
+ break;
+ }
+ }
+
+ return hasInteracted;
+ }
+
+ private addRoadmarkNodeAt ( position: Vector3 ) {
+
+ if ( !this.lane ) return;
+
+ const posTheta = new TvPosTheta();
+
+ // getting position on track in s/t coordinates
+ TvMapQueries.getRoadByCoords( position.x, position.y, posTheta );
+
+ // get the exisiting lane road mark at s and clone it
+ const roadMark = this.lane.getRoadMarkAt( posTheta.s ).clone( posTheta.s );
+
+ roadMark.node = NodeFactoryService.createRoadMarkNode( this.lane, roadMark );
+
+ CommandHistory.executeAll( [
+
+ new AddRoadmarkNodeCommand( this.lane, roadMark, this.roadMarkBuilder ),
+
+ new SetValueCommand( this, 'roadMark', roadMark ),
+
+ new SetInspectorCommand( LaneRoadmarkInspectorComponent, roadMark ),
+
+ ] )
+
+ }
+
+ private selectRoadMarkAt ( position: Vector3, lane: TvLane ) {
+
+ const posTheta = new TvPosTheta();
+
+ TvMapQueries.getRoadByCoords( position.x, position.y, posTheta );
+
+ const roadMark = lane.getRoadMarkAt( posTheta.s );
+
+ CommandHistory.executeAll( [
+
+ new SetValueCommand( this, 'roadMark', roadMark ),
+
+ new SetInspectorCommand( LaneRoadmarkInspectorComponent, roadMark ),
+
+ ] );
+
+ }
+
+ private rebuild ( roadId: number ) {
+
+ // TODO may only road->lane need to be built
+
+ const road = this.openDrive.getRoadById( roadId );
+
+ this.roadMarkBuilder.buildRoad( road );
+ }
+
+ private showNodes ( road: TvRoad ) {
+
+ road.laneSections.forEach( laneSection => {
+
+ laneSection.lanes.forEach( lane => {
+
+ lane.getRoadMarks().forEach( roadmark => {
+
+ if ( roadmark.node ) {
+
+ roadmark.node.visible = true;
+
+ } else {
+
+ roadmark.node = NodeFactoryService.createRoadMarkNode( lane, roadmark );
+
+ SceneService.add( roadmark.node );
+
+ }
+
+ } )
+
+ } )
+
+ } );
+
+ }
+
+ private hideNodes ( road: TvRoad ) {
+
+ road.laneSections.forEach( laneSection => {
+
+ laneSection.lanes.forEach( lane => {
+
+ lane.getRoadMarks().forEach( roadmark => {
+
+ if ( roadmark.node ) roadmark.node.visible = false;
+
+ } );
+
+ } );
+
+ } );
+
+ }
+}
diff --git a/src/app/core/tools/lane-offset-tool.ts b/src/app/core/tools/lane-offset-tool.ts
new file mode 100644
index 00000000..a03e4254
--- /dev/null
+++ b/src/app/core/tools/lane-offset-tool.ts
@@ -0,0 +1,542 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseTool } from './base-tool';
+import { MouseButton, PointerEventData } from '../../events/pointer-event-data';
+import { Object3D, Vector3 } from 'three';
+import { TvLane } from '../../modules/tv-map/models/tv-lane';
+import { TvRoad } from 'app/modules/tv-map/models/tv-road.model';
+import { AnyControlPoint, LaneOffsetNode, LaneWidthNode } from 'app/modules/three-js/objects/control-point';
+import { SceneService } from '../services/scene.service';
+import { Subscription } from 'rxjs';
+import { KeyboardInput } from '../input';
+import { NodeFactoryService } from '../factories/node-factory.service';
+import { TvMapBuilder } from 'app/modules/tv-map/builders/od-builder.service';
+import { AppInspector } from '../inspector';
+import { TvPosTheta } from 'app/modules/tv-map/models/tv-pos-theta';
+import { LineType, OdLaneReferenceLineBuilder } from 'app/modules/tv-map/builders/od-lane-reference-line-builder';
+import { PickingHelper } from '../services/picking-helper.service';
+import { COLOR } from 'app/shared/utils/colors.service';
+import { LaneOffsetInspector, LaneOffsetInspectorData } from 'app/views/inspectors/lane-offset-inspector/lane-offset-inspector.component';
+import { TvMapQueries } from '../../modules/tv-map/queries/tv-map-queries';
+import { UpdateLaneOffsetDistanceCommand } from '../commands/update-lane-offset-distance-command';
+import { CommandHistory } from 'app/services/command-history';
+import { UpdateLaneOffsetValueCommand } from '../commands/update-lane-offset-value-command';
+import { AddLaneOffsetCommand } from '../commands/add-lane-offset-command';
+import { SetInspectorCommand } from '../commands/set-inspector-command';
+import { SetValueCommand } from 'app/modules/three-js/commands/set-value-command';
+
+export class LaneOffsetTool extends BaseTool {
+
+ public name: string = 'LaneOffset';
+
+ private distanceSub: Subscription;
+ private offsetSub: Subscription;
+
+ private laneWidthChanged: boolean = false;
+ private pointerDown: boolean = false;
+ private pointerObject: Object3D;
+
+ private lane: TvLane;
+ private controlPoint: AnyControlPoint;
+ private node: LaneOffsetNode;
+
+ private laneHelper = new OdLaneReferenceLineBuilder( null, LineType.SOLID, COLOR.MAGENTA );
+
+ constructor () {
+
+ super();
+
+ }
+
+ init () {
+
+
+ }
+
+ enable () {
+
+ super.enable();
+
+ // this.distanceSub = LaneOffsetInspector.distanceChanged.subscribe( distance => {
+
+ // const road = this.openDrive.getRoadById( data.node.roadId );
+
+ // road.updateLaneOffsetValues();
+
+ // NodeFactoryService.updateLaneOffsetNode( data.node );
+
+ // this.rebuild( road );
+
+ // } );
+
+ this.distanceSub = LaneOffsetInspector.distanceChanged.subscribe( distance => {
+
+ CommandHistory.execute( new UpdateLaneOffsetDistanceCommand( this.node, distance, null, this.laneHelper ) );
+
+ } );
+
+
+ this.offsetSub = LaneOffsetInspector.offsetChanged.subscribe( offset => {
+
+ CommandHistory.execute( new UpdateLaneOffsetValueCommand( this.node, offset, null, this.laneHelper ) );
+
+ } );
+
+ }
+
+ disable () {
+
+ super.disable();
+
+ if ( this.laneHelper ) this.laneHelper.clear();
+
+ this.distanceSub.unsubscribe();
+
+ this.openDrive.roads.forEach( road => this.hideNodes( road ) );
+ }
+
+ public onPointerDown ( e: PointerEventData ) {
+
+ if ( e.button == MouseButton.RIGHT || e.button == MouseButton.MIDDLE ) return;
+
+ this.pointerDown = true;
+
+ const shiftKeyDown = KeyboardInput.isShiftKeyDown;
+
+ let hasInteracted = false;
+
+ // check for control point interactions first
+ if ( !shiftKeyDown && !hasInteracted ) hasInteracted = this.checkNodePointInteraction( e );
+
+ // // check for line segment interactions
+ // if ( !shiftKeyDown && !hasInteracted ) hasInteracted = this.checkNodeLineInteraction( e );
+
+ // check for lane game object interactions
+ if ( !shiftKeyDown && !hasInteracted ) hasInteracted = this.checkLaneObjectInteraction( e );
+
+ // if ( !hasInteracted ) hasInteracted = this.checkReferenceLineInteraction( e );
+
+ // if ( hasInteracted ) {
+
+ // if ( this.controlPoint ) {
+
+ // AppInspector.setInspector( LaneWidthInspectorComponent, {
+
+ // node: this.controlPoint.parent as LaneWidthNode
+
+ // } );
+
+ // }
+
+ // } else {
+
+ // // no interaction with line, lane, points etc
+ // if ( this.lane ) {
+
+ // this.hideNodes( this.openDrive.getRoadById( this.lane.roadId ) );
+
+ // this.laneHelper.clear();
+
+ // this.lane = null;
+
+ // }
+
+ // }
+
+ }
+
+ public onPointerClicked ( e: PointerEventData ) {
+
+ if ( e.button === MouseButton.LEFT && KeyboardInput.isShiftKeyDown && e.point != null ) {
+
+ this.addNode( e.point );
+
+ }
+ }
+
+ public onPointerUp () {
+
+ // if ( this.laneWidthChanged && this.lane ) {
+
+ // const newPosition = new TvPosTheta();
+
+ // const road = TvMapQueries.getRoadByCoords( this.node.point.position.x, this.node.point.position.y, newPosition );
+
+ // // new road should be same
+ // if ( road.id === this.node.road.id ) {
+
+ // const command = new UpdateLaneOffsetDistanceCommand( this.node, newPosition.s, null, this.laneHelper );
+
+ // CommandHistory.execute( command );
+
+ // }
+
+ // }
+
+ this.pointerDown = false;
+
+ this.pointerObject = null;
+
+ this.laneWidthChanged = false;
+ }
+
+ public onPointerMoved ( e: PointerEventData ) {
+
+ if ( this.pointerDown && this.node ) {
+
+ this.laneWidthChanged = true;
+
+ const newPosition = new TvPosTheta();
+
+ const road = TvMapQueries.getRoadByCoords( e.point.x, e.point.y, newPosition );
+
+ // new road should be same
+ if ( road.id === this.node.road.id ) {
+
+ const command = ( new UpdateLaneOffsetDistanceCommand( this.node, newPosition.s, null, this.laneHelper ) );
+
+ command.execute();
+
+ }
+
+ }
+
+
+ // if ( this.pointerDown && this.pointerObject && this.pointerObject[ 'tag' ] == LaneWidthNode.pointTag ) {
+
+ // this.laneWidthChanged = true;
+
+ // NodeFactoryService.updateLaneWidthNode( this.pointerObject.parent as LaneWidthNode, e.point );
+
+ // this.updateLaneWidth( this.pointerObject.parent as LaneWidthNode );
+
+ // if ( this.lane ) this.laneHelper.redraw( LineType.DASHED );
+
+ // } else if ( this.pointerDown && this.pointerObject && this.pointerObject[ 'tag' ] == LaneWidthNode.lineTag ) {
+
+ // this.laneWidthChanged = true;
+
+ // NodeFactoryService.updateLaneWidthNode( this.pointerObject.parent as LaneWidthNode, e.point );
+
+ // this.updateLaneWidth( this.pointerObject.parent as LaneWidthNode );
+
+ // if ( this.lane ) this.laneHelper.redraw( LineType.DASHED );
+
+ // }
+ }
+
+ private checkNodePointInteraction ( e: PointerEventData ): boolean {
+
+ // // first chceck for control point interactions
+ // // doing in 2 loop to prioritise control points
+ const controlPoint = PickingHelper.checkControlPointInteraction( e, LaneOffsetNode.pointTag, 1.0 );
+
+ if ( controlPoint ) {
+
+ const node = controlPoint.parent as LaneOffsetNode;
+
+ const road = this.openDrive.getRoadById( node.roadId );
+
+ CommandHistory.executeMany(
+
+ new SetValueCommand( this, 'controlPoint', controlPoint ),
+
+ new SetValueCommand( this, 'node', node ),
+
+ new SetInspectorCommand( LaneOffsetInspector, new LaneOffsetInspectorData( node, road ) ),
+
+ );
+
+ } else if ( this.controlPoint ) {
+
+ CommandHistory.executeMany(
+
+ new SetValueCommand( this, 'controlPoint', null ),
+
+ new SetValueCommand( this, 'node', null ),
+
+ new SetInspectorCommand( null, null ),
+
+ );
+
+ }
+
+ // 4 scenarios
+ // old and new both present then check is new or not
+ // old and new is null
+ // old is null and new
+ // old is null and new is null
+
+ // if ( this.controlPoint && controlPoint && this.controlPoint.id != controlPoint.id ) {
+
+ // this.controlPoint.unselect();
+
+ // this.controlPoint = controlPoint;
+
+ // this.controlPoint.select();
+
+ // AppInspector.setInspector( LaneOffsetInspector, new LaneOffsetInspectorData( this.controlPoint.parent as LaneOffsetNode ) );
+
+ // } else if ( this.controlPoint && !controlPoint ) {
+
+ // this.controlPoint.unselect();
+
+ // this.controlPoint = null;
+
+ // AppInspector.clear();
+
+ // } else if ( !this.controlPoint && controlPoint ) {
+
+ // this.controlPoint = controlPoint;
+
+ // this.controlPoint.select();
+
+ // AppInspector.setInspector( LaneOffsetInspector, new LaneOffsetInspectorData( this.controlPoint.parent as LaneOffsetNode ) );
+
+ // } else if ( !this.controlPoint && !controlPoint ) {
+
+ // AppInspector.clear();
+
+ // }
+
+ return controlPoint != null;
+ }
+
+ private checkNodeLineInteraction ( e: PointerEventData ) {
+
+ // let hasInteracted = false;
+
+ // // check for line segment interactions
+ // for ( let i = 0; i < e.intersections.length; i++ ) {
+
+ // const intersection = e.intersections[ i ];
+
+ // if ( e.button === MouseButton.LEFT && intersection.object && intersection.object[ 'tag' ] == 'width-line' ) {
+
+ // hasInteracted = true;
+
+ // // const road = intersection.object.userData.road;
+
+ // // // check if old or a new road is selected
+ // // if ( !this.road || this.road.id != road.id ) {
+
+ // // this.road = road;
+
+ // // this.road.spline.show();
+
+ // // }
+
+ // break;
+ // }
+ // }
+
+ // return hasInteracted;
+ }
+
+ private checkLaneObjectInteraction ( e: PointerEventData ): boolean {
+
+ const newLane = PickingHelper.checkLaneObjectInteraction( e );
+
+ if ( newLane ) {
+
+ const road = this.openDrive.getRoadById( newLane.roadId );
+
+ CommandHistory.executeMany(
+
+ new SetValueCommand( this, 'lane', newLane ),
+
+ new SetInspectorCommand( LaneOffsetInspector, new LaneOffsetInspectorData( null, road ) )
+
+ );
+
+ } else if ( this.lane ) {
+
+ CommandHistory.executeMany(
+
+ new SetValueCommand( this, 'lane', null ),
+
+ new SetInspectorCommand( null, null ),
+
+ );
+
+ }
+
+ // lane exists new not found -> 1 clear
+ // lane exists new found -> 2 change
+ // lane does not exist new found -> set new
+ // lane does not exist and new not found => nothing
+
+ // if ( this.lane && newLane == null ) {
+
+ // this.hideNodes( this.openDrive.getRoadById( this.lane.roadId ) );
+
+ // this.laneHelper.clear();
+
+ // this.lane = null;
+
+ // } else if ( this.lane && newLane && this.lane.roadId != newLane.roadId ) {
+
+ // this.hideNodes( this.openDrive.getRoadById( this.lane.roadId ) );
+
+ // this.laneHelper.clear();
+
+ // this.lane = newLane;
+
+ // const newRoad = this.openDrive.getRoadById( newLane.roadId );
+
+ // this.showNodes( newRoad );
+
+ // this.laneHelper.drawRoad( newRoad, LineType.SOLID );
+
+ // } else if ( !this.lane && newLane ) {
+
+ // this.lane = newLane;
+
+ // const newRoad = this.openDrive.getRoadById( newLane.roadId );
+
+ // this.showNodes( newRoad );
+
+ // this.laneHelper.drawRoad( newRoad, LineType.SOLID );
+
+
+ // } else if ( !this.lane && newLane == null ) {
+
+ // // do nothing
+
+ // }
+
+ return newLane != null;
+ }
+
+ private checkReferenceLineInteraction ( e: PointerEventData ) {
+
+ // let hasInteracted = false;
+
+ // this.checkIntersection( this.laneHelper.tag, e.intersections, ( obj ) => {
+
+ // hasInteracted = true;
+
+ // this.laneHelper.onLineSelected( obj as Line );
+
+ // } );
+
+ // return hasInteracted;
+ }
+
+ private addNode ( position: Vector3 ): void {
+
+ if ( !this.lane ) return;
+
+ const road = this.openDrive.getRoadById( this.lane.roadId );
+
+ const posTheta = new TvPosTheta();
+
+ // getting position on track in s/t coordinates
+ TvMapQueries.getRoadByCoords(
+ position.x,
+ position.y,
+ posTheta
+ );
+
+ const laneOffset = road.getLaneOffsetAt( posTheta.s ).clone( posTheta.s );
+
+ const node = NodeFactoryService.createLaneOffsetNode( road, laneOffset );
+
+ CommandHistory.executeMany(
+
+ new AddLaneOffsetCommand( node ),
+
+ new SetInspectorCommand( LaneOffsetInspector, new LaneOffsetInspectorData( node, road ) ),
+
+ );
+
+ // // getting position on track in s/t coordinates
+ // TvMapQueries.getRoadByCoords(
+ // position.x,
+ // position.y,
+ // posTheta
+ // );
+
+ // // // get the exisiting lane Offset at s
+ // // // and clone the lane Offset
+ // const newLaneOffset = road.getLaneOffsetAt( posTheta.s ).clone( posTheta.s );
+
+ // // // add the with back to lane to
+ // // this.lane.addOffsetRecordInstance( newLaneOffset );
+ // road.addLaneOffsetInstance( newLaneOffset );
+
+ // // // make mesh for the lane Offset node
+ // // const laneOffsetNode = newLaneOffset.mesh = NodeFactoryService.createLaneOffsetNode(
+ // // this.lane.roadId, this.lane.id, newLaneOffset.s, newLaneOffset
+ // // );
+ // const node = newLaneOffset.mesh = NodeFactoryService.createLaneOffsetNode( road, newLaneOffset );
+
+ // // // add it to scene to enable rendering
+ // // SceneService.add( newLaneOffset.mesh );
+ // SceneService.add( node );
+
+ // // set the node in inpsector
+ // AppInspector.setInspector( LaneOffsetInspector, new LaneOffsetInspectorData( node, road ) );
+
+ // this.rebuild( road );
+
+ }
+
+ private updateLaneWidth ( node: LaneWidthNode ) {
+
+ // if ( !node ) return;
+
+ // if ( !this.lane ) return;
+
+ // const road = this.openDrive.getRoadById( node.roadId );
+
+ // road.getLaneSectionAt( node.s ).updateLaneWidthValues( this.lane );
+ }
+
+ private hideNodes ( road: TvRoad ): void {
+
+ road.getLaneOffsets().forEach( laneOffset => {
+
+ if ( laneOffset.mesh ) {
+
+ laneOffset.mesh.visible = false;
+
+ }
+
+ } );
+
+ }
+
+ private showNodes ( road: TvRoad ) {
+
+ road.getLaneOffsets().forEach( laneOffset => {
+
+ if ( laneOffset.mesh ) {
+
+ laneOffset.mesh.visible = true;
+
+ } else {
+
+ laneOffset.mesh = NodeFactoryService.createLaneOffsetNode( road, laneOffset );
+
+ SceneService.add( laneOffset.mesh );
+
+ }
+
+ } );
+
+ }
+
+ private rebuild ( road: TvRoad ): void {
+
+ if ( !this.lane ) return;
+
+ SceneService.removeWithChildren( road.gameObject, true );
+
+ TvMapBuilder.buildRoad( this.openDrive.gameObject, road );
+
+ this.laneHelper.redraw( LineType.SOLID );
+ }
+}
diff --git a/src/app/core/tools/lane-tool.ts b/src/app/core/tools/lane-tool.ts
new file mode 100644
index 00000000..4481e700
--- /dev/null
+++ b/src/app/core/tools/lane-tool.ts
@@ -0,0 +1,73 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { PointerEventData } from '../../events/pointer-event-data';
+import { Mesh, Object3D } from 'three';
+import { TvLane } from '../../modules/tv-map/models/tv-lane';
+import { BaseTool } from './base-tool';
+import { AppInspector } from '../inspector';
+import { LaneInspectorComponent } from '../../views/inspectors/lane-type-inspector/lane-inspector.component';
+import { OdLaneDirectionBuilder } from '../../modules/tv-map/builders/od-lane-direction-builder';
+
+export class LaneTool extends BaseTool {
+
+ name: string = 'LaneTool';
+
+ init () {
+
+ super.init();
+
+ this.laneDirectionHelper = new OdLaneDirectionBuilder( null );
+ }
+
+ disable (): void {
+
+ super.disable();
+
+ this.laneDirectionHelper.clear();
+ }
+
+ private laneDirectionHelper: OdLaneDirectionBuilder;
+
+ onPointerDown ( e: PointerEventData ) {
+
+ super.onPointerDown( e );
+
+ let laneFound = false;
+
+ this.checkLaneIntersection( e.intersections, ( object: Object3D ) => {
+
+ laneFound = true;
+
+ // this.removeHighlight();
+
+ // this.highlight( object as Mesh );
+
+ this.selectLane( object as Mesh );
+
+ } );
+
+ if ( !laneFound ) {
+
+ // this.removeHighlight();
+
+ this.clearInspector();
+ }
+ }
+
+ private selectLane ( object: Mesh ) {
+
+ let lane = ( object.userData.lane as TvLane );
+
+ if ( lane == null ) return;
+
+ AppInspector.setInspector( LaneInspectorComponent, lane );
+
+ this.laneDirectionHelper.clear();
+
+ const road = this.openDrive.getRoadById( lane.roadId );
+
+ this.laneDirectionHelper.drawSingleLane( road, lane );
+ }
+}
diff --git a/src/app/core/tools/lane-width-tool.ts b/src/app/core/tools/lane-width-tool.ts
new file mode 100644
index 00000000..c4b00477
--- /dev/null
+++ b/src/app/core/tools/lane-width-tool.ts
@@ -0,0 +1,359 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseTool } from './base-tool';
+import { MouseButton, PointerEventData } from '../../events/pointer-event-data';
+import { Vector3 } from 'three';
+import { TvLane } from '../../modules/tv-map/models/tv-lane';
+import { TvRoad } from 'app/modules/tv-map/models/tv-road.model';
+import { AnyControlPoint, LaneWidthNode } from 'app/modules/three-js/objects/control-point';
+import { SceneService } from '../services/scene.service';
+import { Subscription } from 'rxjs';
+import { KeyboardInput } from '../input';
+import { ObjectTypes } from 'app/modules/tv-map/models/tv-common';
+import { NodeFactoryService } from '../factories/node-factory.service';
+import { LaneWidthInspector } from 'app/views/inspectors/lane-width-inspector/lane-width-inspector.component';
+import { LineType, OdLaneReferenceLineBuilder } from 'app/modules/tv-map/builders/od-lane-reference-line-builder';
+import { PickingHelper } from '../services/picking-helper.service';
+import { CommandHistory } from 'app/services/command-history';
+import { SetValueCommand } from 'app/modules/three-js/commands/set-value-command';
+import { SetInspectorCommand } from '../commands/set-inspector-command';
+import { UpdateWidthNodePositionCommand } from '../commands/update-width-node-position-command';
+import { AddWidthNodeCommand } from '../commands/add-width-node-command';
+import { UpdateWidthNodeValueCommand } from '../commands/update-width-node-value-command';
+
+export class LaneWidthTool extends BaseTool {
+
+ public name: string = 'LaneWidth';
+
+ private widthChangeSub: Subscription;
+
+ private laneWidthChanged: boolean = false;
+ private pointerDown: boolean = false;
+ private pointerDownAt: Vector3;
+
+ private lane: TvLane;
+ private controlPoint: AnyControlPoint;
+ private widthNode: LaneWidthNode;
+
+ private laneHelper: OdLaneReferenceLineBuilder;
+
+
+ constructor () {
+
+ super();
+
+ }
+
+ init () {
+
+
+ }
+
+ enable () {
+
+ super.enable();
+
+ this.laneHelper = new OdLaneReferenceLineBuilder( null, LineType.DASHED );
+
+ // this.widthChangeSub = LaneWidthInspector.widthChanged.subscribe( ( data: LaneWidthInspectorData ) => {
+
+ // data.node.road.getLaneSectionAt( data.node.s ).updateLaneWidthValues( data.node.lane );
+
+ // NodeFactoryService.updateLaneWidthNodeLine( data.node );
+
+ // this.rebuild( data.node.road );
+
+ // } );
+
+ this.widthChangeSub = LaneWidthInspector.widthChanged.subscribe( width => {
+
+ CommandHistory.execute( new UpdateWidthNodeValueCommand( this.widthNode, width, null, this.laneHelper ) );
+
+ } );
+
+
+
+ }
+
+ disable () {
+
+ super.disable();
+
+ if ( this.laneHelper ) this.laneHelper.clear();
+
+ if ( this.widthChangeSub ) this.widthChangeSub.unsubscribe();
+
+ this.openDrive.roads.forEach( road => LaneWidthTool.hideNodes( road ) );
+ }
+
+ public onPointerDown ( e: PointerEventData ) {
+
+ if ( e.button === MouseButton.RIGHT || e.button === MouseButton.MIDDLE ) return;
+
+ this.pointerDown = true;
+
+ this.pointerDownAt = e.point;
+
+ const shiftKeyDown = KeyboardInput.isShiftKeyDown;
+
+ let hasInteracted = false;
+
+ // check for control point interactions first
+ if ( !shiftKeyDown && !hasInteracted ) hasInteracted = this.checkNodePointInteraction( e );
+
+ // check for line segment interactions
+ // if ( !shiftKeyDown && !hasInteracted ) hasInteracted = this.checkNodeLineInteraction( e );
+
+ // if ( !hasInteracted ) hasInteracted = this.checkReferenceLineInteraction( e );
+
+ // check for lane game object interactions
+ if ( !hasInteracted ) hasInteracted = this.checkLaneObjectInteraction( e );
+
+ if ( !hasInteracted ) {
+
+ // // no interaction with line, lane, points etc
+ // if ( this.lane ) {
+
+ // LaneWidthTool.hideNodes( this.openDrive.getRoadById( this.lane.roadId ) );
+
+ // this.laneHelper.clear();
+
+ // this.lane = null;
+
+ // }
+
+ }
+
+ }
+
+ public onPointerClicked ( e: PointerEventData ) {
+
+ if ( e.button === MouseButton.LEFT && KeyboardInput.isShiftKeyDown && e.point != null ) {
+
+ this.addNode( e.point );
+
+ }
+ }
+
+ public onPointerUp ( e ) {
+
+ if ( this.laneWidthChanged && this.widthNode ) {
+
+ const newPosition = this.widthNode.point.position.clone();
+
+ const oldPosition = this.pointerDownAt.clone();
+
+ CommandHistory.execute( new UpdateWidthNodePositionCommand( this.widthNode, newPosition, oldPosition, this.laneHelper ) )
+
+ }
+
+ this.pointerDown = false;
+
+ this.pointerDownAt = null;
+
+ this.laneWidthChanged = false;
+ }
+
+ public onPointerMoved ( e: PointerEventData ) {
+
+ if ( this.pointerDown && this.widthNode ) {
+
+ this.laneWidthChanged = true;
+
+ NodeFactoryService.updateLaneWidthNode( this.widthNode, e.point );
+
+ this.widthNode.updateLaneWidthValues();
+
+ // this.updateLaneWidth( this.pointerObject.parent as LaneWidthNode );
+
+ // if ( this.lane ) this.laneHelper.redraw( LineType.DASHED );
+
+ }
+
+ // else if ( this.pointerDown && this.pointerObject && this.pointerObject[ 'tag' ] == LaneWidthNode.lineTag ) {
+
+ // this.laneWidthChanged = true;
+
+ // NodeFactoryService.updateLaneWidthNode( this.pointerObject.parent as LaneWidthNode, e.point );
+
+ // this.updateLaneWidth( this.pointerObject.parent as LaneWidthNode );
+
+ // if ( this.lane ) this.laneHelper.redraw( LineType.DASHED );
+
+ // }
+ }
+
+ private checkNodePointInteraction ( e: PointerEventData ): boolean {
+
+ // first chceck for control point interactions
+ // doing in 2 loop to prioritise control points
+ const controlPoint = PickingHelper.checkControlPointInteraction( e, LaneWidthNode.pointTag );
+
+ if ( controlPoint ) {
+
+ const laneWidthNode = controlPoint.parent as LaneWidthNode;
+
+ CommandHistory.executeMany(
+
+ new SetValueCommand( this, 'controlPoint', controlPoint ),
+
+ new SetValueCommand( this, 'widthNode', laneWidthNode ),
+
+ new SetInspectorCommand( LaneWidthInspector, { node: laneWidthNode } ),
+
+ );
+
+ } else if ( this.controlPoint ) {
+
+ CommandHistory.executeMany(
+
+ new SetValueCommand( this, 'controlPoint', null ),
+
+ new SetInspectorCommand( LaneWidthInspector, { node: null, lane: this.lane } ),
+
+ );
+
+ }
+
+ return controlPoint != null;
+ }
+
+
+ private checkLaneObjectInteraction ( e: PointerEventData ) {
+
+ let hasInteracted = false;
+
+ for ( let i = 0; i < e.intersections.length; i++ ) {
+
+ const intersection = e.intersections[ i ];
+
+ // tslint:disable-next-line: no-string-literal
+ if ( intersection.object && intersection.object[ 'tag' ] === ObjectTypes.LANE ) {
+
+ hasInteracted = true;
+
+ if ( intersection.object.userData.lane ) {
+
+ const newLane = intersection.object.userData.lane as TvLane;
+
+
+
+ // check if old or a new lane is selected
+ if ( !this.lane || this.lane.id !== newLane.id || this.lane.roadId !== newLane.roadId ) {
+
+ CommandHistory.executeMany(
+
+ new SetValueCommand( this, 'lane', newLane ),
+
+ new SetInspectorCommand( LaneWidthInspector, { lane: newLane } ),
+
+ );
+
+ }
+ }
+
+ break;
+ }
+ }
+
+ if ( !hasInteracted && !this.controlPoint ) {
+
+ CommandHistory.executeMany(
+
+ new SetValueCommand( this, 'lane', null ),
+
+ new SetInspectorCommand( null, null ),
+
+ );
+
+ this.laneHelper.clear();
+ }
+
+ return hasInteracted;
+ }
+
+ // private checkReferenceLineInteraction ( e: PointerEventData ) {
+
+ // let hasInteracted = false;
+
+ // this.checkIntersection( this.laneHelper.tag, e.intersections, ( obj ) => {
+
+ // hasInteracted = true;
+
+ // this.laneHelper.onLineSelected( obj as Line );
+
+ // } );
+
+ // return hasInteracted;
+ // }
+
+ private addNode ( position: Vector3 ): void {
+
+ if ( !this.lane ) return;
+
+ const road = this.openDrive.getRoadById( this.lane.roadId );
+
+ const laneWidthNode = NodeFactoryService.createLaneWidthNodeByPosition( road, this.lane, position );
+
+ CommandHistory.executeMany(
+
+ new SetValueCommand( this, 'widthNode', laneWidthNode ),
+
+ new AddWidthNodeCommand( laneWidthNode, this.laneHelper ),
+
+ new SetInspectorCommand( LaneWidthInspector, { node: laneWidthNode } ),
+
+ );
+ }
+
+
+ // tslint:disable-next-line: member-ordering
+ static hideNodes ( road: TvRoad ): void {
+
+ road.laneSections.forEach( laneSection => {
+
+ laneSection.lanes.forEach( lane => {
+
+ lane.getLaneWidthVector().forEach( laneWidth => {
+
+ if ( laneWidth.mesh ) laneWidth.mesh.visible = false;
+
+ } );
+
+ } );
+
+ } );
+
+ }
+
+ // tslint:disable-next-line: member-ordering
+ static showNodes ( road: TvRoad ) {
+
+ road.laneSections.forEach( laneSection => {
+
+ laneSection.lanes.forEach( lane => {
+
+ lane.getLaneWidthVector().forEach( laneWidth => {
+
+ if ( laneWidth.mesh ) {
+
+ laneWidth.mesh.visible = true;
+
+ } else {
+
+ laneWidth.mesh = NodeFactoryService.createLaneWidthNode( road, lane, laneWidth.s, laneWidth );
+
+ SceneService.add( laneWidth.mesh );
+
+ }
+
+ } )
+
+ } )
+
+ } );
+ }
+
+}
diff --git a/src/app/core/tools/maneuver-tool.spec.ts b/src/app/core/tools/maneuver-tool.spec.ts
new file mode 100644
index 00000000..bb4b38e9
--- /dev/null
+++ b/src/app/core/tools/maneuver-tool.spec.ts
@@ -0,0 +1,449 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvMap } from 'app/modules/tv-map/models/tv-map.model';
+import { TvMapSourceFile } from 'app/modules/tv-map/services/tv-map-source-file';
+import { TvContactPoint, TvLaneSide } from 'app/modules/tv-map/models/tv-common';
+import { JunctionEntryObject } from 'app/modules/three-js/objects/junction-entry.object';
+import { ManeuverTool } from './maneuver-tool';
+
+
+describe( 'ManeuverTool Test', () => {
+
+ let tool: ManeuverTool;
+ let openDrive: TvMap;
+
+ beforeEach( () => {
+
+ tool = new ManeuverTool();
+
+ tool.init();
+
+ openDrive = TvMapSourceFile.openDrive = new TvMap();
+
+ } );
+
+ it( 'should return correct entry exit side', () => {
+
+ // start-end <-> start:end
+ // 1L <--- <---- 1L
+ // -1R ---> ----> -1R
+
+ const roadA = openDrive.addDefaultRoad();
+ roadA.addGeometryLine( 0, 0, 0, 0, 10 );
+
+ const roadB = openDrive.addDefaultRoad();
+ roadB.addGeometryLine( 0, 15, 0, 0, 10 );
+
+ const positionA = roadA.endPosition().toVector3();
+ const positionB = roadB.startPosition().toVector3();
+
+ const aL2 = new JunctionEntryObject( "", positionA, TvContactPoint.END, roadA, roadA.getFirstLaneSection().getLaneById( 2 ) );
+ const aL1 = new JunctionEntryObject( "", positionA, TvContactPoint.END, roadA, roadA.getFirstLaneSection().getLaneById( 1 ) );
+ const aR1 = new JunctionEntryObject( "", positionA, TvContactPoint.END, roadA, roadA.getFirstLaneSection().getLaneById( -1 ) );
+ const aR2 = new JunctionEntryObject( "", positionA, TvContactPoint.END, roadA, roadA.getFirstLaneSection().getLaneById( -2 ) );
+
+ const bL2 = new JunctionEntryObject( "", positionB, TvContactPoint.START, roadB, roadB.getFirstLaneSection().getLaneById( 2 ) );
+ const bL1 = new JunctionEntryObject( "", positionB, TvContactPoint.START, roadB, roadB.getFirstLaneSection().getLaneById( 1 ) );
+ const bR1 = new JunctionEntryObject( "", positionB, TvContactPoint.START, roadB, roadB.getFirstLaneSection().getLaneById( -1 ) );
+ const bR2 = new JunctionEntryObject( "", positionB, TvContactPoint.START, roadB, roadB.getFirstLaneSection().getLaneById( -2 ) );
+
+ let result = tool.findEntryExitSide( aL1, bL1 );
+
+ expect( result.entry.id ).toBe( bL1.id );
+ expect( result.exit.id ).toBe( aL1.id );
+ expect( result.side ).toBe( TvLaneSide.RIGHT );
+
+ result = tool.findEntryExitSide( bL1, aL1 );
+
+ expect( result.entry.id ).toBe( bL1.id );
+ expect( result.exit.id ).toBe( aL1.id );
+ expect( result.side ).toBe( TvLaneSide.RIGHT );
+
+ result = tool.findEntryExitSide( aR1, bR1 );
+
+ expect( result.entry.id ).toBe( aR1.id );
+ expect( result.exit.id ).toBe( bR1.id );
+ expect( result.side ).toBe( TvLaneSide.RIGHT );
+
+ result = tool.findEntryExitSide( bR1, aR1 );
+
+ expect( result.entry.id ).toBe( aR1.id );
+ expect( result.exit.id ).toBe( bR1.id );
+ expect( result.side ).toBe( TvLaneSide.RIGHT );
+
+ } );
+
+ it( 'should return true for existing connection ', () => {
+
+ const roadA = openDrive.addDefaultRoad();
+ roadA.addGeometryLine( 0, 0, 0, 0, 10 );
+
+ const roadB = openDrive.addDefaultRoad();
+ roadB.addGeometryLine( 0, 15, 0, 0, 10 );
+
+ const roadC = openDrive.addDefaultRoad();
+ roadC.addGeometryLine( 0, 15, 10, 0, 10 );
+
+ const positionA = roadA.endPosition().toVector3();
+ const positionB = roadB.startPosition().toVector3();
+ const positionC = roadC.startPosition().toVector3();
+
+ const aL2 = new JunctionEntryObject( "", positionA, TvContactPoint.END, roadA, roadA.getFirstLaneSection().getLaneById( 2 ) );
+ const aL1 = new JunctionEntryObject( "", positionA, TvContactPoint.END, roadA, roadA.getFirstLaneSection().getLaneById( 1 ) );
+ const aR1 = new JunctionEntryObject( "", positionA, TvContactPoint.END, roadA, roadA.getFirstLaneSection().getLaneById( -1 ) );
+ const aR2 = new JunctionEntryObject( "", positionA, TvContactPoint.END, roadA, roadA.getFirstLaneSection().getLaneById( -2 ) );
+
+ const bL2 = new JunctionEntryObject( "", positionB, TvContactPoint.START, roadB, roadB.getFirstLaneSection().getLaneById( 2 ) );
+ const bL1 = new JunctionEntryObject( "", positionB, TvContactPoint.START, roadB, roadB.getFirstLaneSection().getLaneById( 1 ) );
+ const bR1 = new JunctionEntryObject( "", positionB, TvContactPoint.START, roadB, roadB.getFirstLaneSection().getLaneById( -1 ) );
+ const bR2 = new JunctionEntryObject( "", positionB, TvContactPoint.START, roadB, roadB.getFirstLaneSection().getLaneById( -2 ) );
+
+ const cL2 = new JunctionEntryObject( "", positionC, TvContactPoint.START, roadC, roadC.getFirstLaneSection().getLaneById( 2 ) );
+ const cL1 = new JunctionEntryObject( "", positionC, TvContactPoint.START, roadC, roadC.getFirstLaneSection().getLaneById( 1 ) );
+ const cR1 = new JunctionEntryObject( "", positionC, TvContactPoint.START, roadC, roadC.getFirstLaneSection().getLaneById( -1 ) );
+ const cR2 = new JunctionEntryObject( "", positionC, TvContactPoint.START, roadC, roadC.getFirstLaneSection().getLaneById( -2 ) );
+
+ const entryExitSide = tool.findEntryExitSide( aL1, bL1 );
+
+ const laneWidth = aL1.lane.getWidthValue( 0 );
+
+ const entry = entryExitSide.entry;
+ const exit = entryExitSide.exit;
+ const side = entryExitSide.side;
+
+ const junction = tool.findJunction( entry, exit );
+
+ const connectingRoad = tool.createConnectingRoad( entry, exit, side, laneWidth, junction );
+
+ let result = tool.hasConnection( junction, entry, exit );
+
+ expect( result.connectionFound ).toBe( false );
+
+ tool.createConnections( junction, entry, connectingRoad, exit );
+
+ result = tool.hasConnection( junction, entry, exit );
+
+ expect( result.connectionFound ).toBe( true );
+
+ result = tool.hasConnection( junction, exit, entry );
+
+ expect( result.connectionFound ).toBe( false );
+
+ expect( openDrive.roads.size ).toBe( 4 );
+
+ expect( junction.connections.size ).toBe( 1 );
+
+ result = tool.hasConnection( junction, aR2, bR2 );
+
+ // // false because till now only left side connection has been created
+ // // if we check for right side connection it should return false
+ // expect( result.connectionFound ).toBe( false );
+
+ // result = tool.hasConnection( junction, aL2, bL2 );
+
+ // // true because left side connection already exists
+ // expect( result.connectionFound ).toBe( true );
+
+ // // create another connection road a and c left side
+ // tool.createConnections( junction, aL1, tool.createConnectingRoad( aL1, cL1, side, laneWidth, junction ), cL1 );
+
+ // expect( openDrive.roads.size ).toBe( 5 );
+
+ // expect( junction.connections.size ).toBe( 4 );
+
+ // // []
+
+ // // expect(result.)
+
+ } )
+
+ it( 'should connect road for connection from left to right', () => {
+
+ // start-end <-> start:end
+ // 1L <--- <---- 1L
+ // -1R ---> ----> -1R
+
+ const roadA = openDrive.addDefaultRoad();
+ roadA.addGeometryLine( 0, 0, 0, 0, 10 );
+
+ const roadB = openDrive.addDefaultRoad();
+ roadB.addGeometryLine( 0, 15, 0, 0, 10 );
+
+ const positionA = roadA.endPosition().toVector3();
+ const positionB = roadB.startPosition().toVector3();
+
+ const leftA = new JunctionEntryObject( "", positionA, TvContactPoint.END, roadA, roadA.getFirstLaneSection().getLaneById( 1 ) );
+ const rightA = new JunctionEntryObject( "", positionA, TvContactPoint.END, roadA, roadA.getFirstLaneSection().getLaneById( -1 ) );
+
+ const leftB = new JunctionEntryObject( "", positionB, TvContactPoint.START, roadB, roadB.getFirstLaneSection().getLaneById( 1 ) );
+ const rightB = new JunctionEntryObject( "", positionB, TvContactPoint.START, roadB, roadB.getFirstLaneSection().getLaneById( -1 ) );
+
+ let nodes = tool.getSplinePositions( leftA, leftB, TvLaneSide.LEFT );
+ expect( nodes.start.x ).toBe( 10 );
+ expect( nodes.end.x ).toBe( 15 );
+ expect( nodes.side ).toBe( TvLaneSide.LEFT );
+
+ nodes = tool.getSplinePositions( leftB, leftA, TvLaneSide.RIGHT );
+ expect( nodes.start.x ).toBe( 15 );
+ expect( nodes.end.x ).toBe( 10 );
+ expect( nodes.side ).toBe( TvLaneSide.RIGHT );
+
+ nodes = tool.getSplinePositions( rightA, rightB, TvLaneSide.RIGHT );
+ expect( nodes.start.x ).toBe( 10 );
+ expect( nodes.end.x ).toBe( 15 );
+ expect( nodes.side ).toBe( TvLaneSide.RIGHT );
+
+ nodes = tool.getSplinePositions( rightB, rightA, TvLaneSide.LEFT );
+ expect( nodes.start.x ).toBe( 15 );
+ expect( nodes.end.x ).toBe( 10 );
+ expect( nodes.side ).toBe( TvLaneSide.LEFT );
+
+ // this will create junctions and connections
+ tool.connectJunctionObject( leftA, leftB );
+ tool.connectJunctionObject( rightA, rightB );
+
+ ////////////////////////////////////////////////////////////////////////
+ // connecting road successor/predecssor tests
+
+ const roadC = openDrive.roads.get( 3 );
+ const roadD = openDrive.roads.get( 4 );
+
+ expect( !roadC ).toBe( false );
+ expect( !roadD ).toBe( false );
+
+ expect( roadC.successor.elementType ).toBe( "road" );
+ expect( roadC.successor.elementId ).toBe( roadA.id );
+ expect( roadC.successor.contactPoint ).toBe( TvContactPoint.END );
+
+ expect( roadC.predecessor.elementType ).toBe( "road" );
+ expect( roadC.predecessor.elementId ).toBe( roadB.id );
+ expect( roadC.predecessor.contactPoint ).toBe( TvContactPoint.START );
+
+ expect( roadD.successor.elementType ).toBe( "road" );
+ expect( roadD.successor.elementId ).toBe( roadB.id );
+ expect( roadD.successor.contactPoint ).toBe( TvContactPoint.START );
+
+ expect( roadD.predecessor.elementType ).toBe( "road" );
+ expect( roadD.predecessor.elementId ).toBe( roadA.id );
+ expect( roadD.predecessor.contactPoint ).toBe( TvContactPoint.END );
+
+ ////////////////////////////////////////////////////////////////////////
+ // incoming/outgoing road successor/predecessor relation
+
+ expect( openDrive.junctions.size ).toBe( 1 );
+
+ const junction = openDrive.junctions.get( 1 );
+
+ expect( junction.connections.size ).toBe( 2 );
+
+ expect( roadA.successor.elementType ).toBe( "junction" )
+ expect( roadA.successor.elementId ).toBe( junction.id )
+
+ expect( roadB.predecessor.elementType ).toBe( "junction" )
+ expect( roadB.predecessor.elementId ).toBe( junction.id )
+
+ ////////////////////////////////////////////////////////////////////////
+ // junction connection tests
+ let connection = junction.getJunctionConnection( 1 );
+ let link = connection.laneLink[ 0 ];
+
+ expect( !connection ).toBe( false );
+ expect( connection.incomingRoad ).toBe( 2 );
+ expect( connection.connectingRoad ).toBe( 3 );
+ expect( connection.contactPoint ).toBe( TvContactPoint.START );
+ expect( link.from ).toBe( +1 );
+ expect( link.to ).toBe( -1 );
+
+ connection = junction.getJunctionConnection( 2 );
+ link = connection.laneLink[ 0 ];
+
+ expect( !connection ).toBe( false );
+ expect( connection.incomingRoad ).toBe( 1 );
+ expect( connection.connectingRoad ).toBe( 4 );
+ expect( connection.contactPoint ).toBe( TvContactPoint.START );
+ expect( link.from ).toBe( -1 );
+ expect( link.to ).toBe( -1 );
+
+
+ ////////////////////////////////////////////////////////////////////////
+ // connection road lane links
+ ////////////////////////////////////////////////////////////////////////
+
+ const laneC = roadC.getFirstLaneSection().getLaneById( -1 );
+
+ expect( !laneC ).toBe( false );
+ expect( laneC.predecessor ).toBe( 1 );
+ expect( laneC.succcessor ).toBe( 1 );
+
+ const laneD = roadD.getFirstLaneSection().getLaneById( -1 );
+
+ expect( !laneD ).toBe( false );
+ expect( laneD.predecessor ).toBe( -1 );
+ expect( laneD.succcessor ).toBe( -1 );
+
+ } );
+
+ it( 'should connect road for connection from right to left', () => {
+
+ // start-end <-> start:end
+ // 1L <--- <---- 1L
+ // -1R ---> ----> -1R
+
+ const roadA = openDrive.addDefaultRoad();
+ roadA.addGeometryLine( 0, 0, 0, 0, 10 );
+
+ const roadB = openDrive.addDefaultRoad();
+ roadB.addGeometryLine( 0, 15, 0, 0, 10 );
+
+ const positionA = roadA.endPosition().toVector3();
+ const positionB = roadB.startPosition().toVector3();
+
+ const leftA = new JunctionEntryObject( "", positionA, TvContactPoint.END, roadA, roadA.getFirstLaneSection().getLaneById( 1 ) );
+ const rightA = new JunctionEntryObject( "", positionA, TvContactPoint.END, roadA, roadA.getFirstLaneSection().getLaneById( -1 ) );
+
+ const leftB = new JunctionEntryObject( "", positionB, TvContactPoint.START, roadB, roadB.getFirstLaneSection().getLaneById( 1 ) );
+ const rightB = new JunctionEntryObject( "", positionB, TvContactPoint.START, roadB, roadB.getFirstLaneSection().getLaneById( -1 ) );
+
+ let nodes = tool.getSplinePositions( leftA, leftB, TvLaneSide.LEFT );
+ expect( nodes.start.x ).toBe( 10 );
+ expect( nodes.end.x ).toBe( 15 );
+ expect( nodes.side ).toBe( TvLaneSide.LEFT );
+
+ nodes = tool.getSplinePositions( leftB, leftA, TvLaneSide.RIGHT );
+ expect( nodes.start.x ).toBe( 15 );
+ expect( nodes.end.x ).toBe( 10 );
+ expect( nodes.side ).toBe( TvLaneSide.RIGHT );
+
+ nodes = tool.getSplinePositions( rightA, rightB, TvLaneSide.RIGHT );
+ expect( nodes.start.x ).toBe( 10 );
+ expect( nodes.end.x ).toBe( 15 );
+ expect( nodes.side ).toBe( TvLaneSide.RIGHT );
+
+ nodes = tool.getSplinePositions( rightB, rightA, TvLaneSide.LEFT );
+ expect( nodes.start.x ).toBe( 15 );
+ expect( nodes.end.x ).toBe( 10 );
+ expect( nodes.side ).toBe( TvLaneSide.LEFT );
+
+ // this will create junctions and connections
+ tool.connectJunctionObject( leftB, leftA );
+ tool.connectJunctionObject( rightB, rightA );
+
+ ////////////////////////////////////////////////////////////////////////
+ // connecting road successor/predecssor tests
+
+ const roadC = openDrive.roads.get( 3 );
+ const roadD = openDrive.roads.get( 4 );
+
+ expect( roadC.successor.elementType ).toBe( "road" );
+ expect( roadC.successor.elementId ).toBe( roadA.id );
+ expect( roadC.successor.contactPoint ).toBe( TvContactPoint.END );
+
+ expect( roadC.predecessor.elementType ).toBe( "road" );
+ expect( roadC.predecessor.elementId ).toBe( roadB.id );
+ expect( roadC.predecessor.contactPoint ).toBe( TvContactPoint.START );
+
+ expect( roadD.successor.elementType ).toBe( "road" );
+ expect( roadD.successor.elementId ).toBe( roadB.id );
+ expect( roadD.successor.contactPoint ).toBe( TvContactPoint.START );
+
+ expect( roadD.predecessor.elementType ).toBe( "road" );
+ expect( roadD.predecessor.elementId ).toBe( roadA.id );
+ expect( roadD.predecessor.contactPoint ).toBe( TvContactPoint.END );
+
+ ////////////////////////////////////////////////////////////////////////
+ // incoming/outgoing road successor/predecessor relation
+
+ expect( openDrive.junctions.size ).toBe( 1 );
+
+ const junction = openDrive.junctions.get( 1 );
+
+ expect( junction.connections.size ).toBe( 2 );
+
+ expect( roadA.successor.elementType ).toBe( "junction" )
+ expect( roadA.successor.elementId ).toBe( junction.id )
+
+ expect( roadB.predecessor.elementType ).toBe( "junction" )
+ expect( roadB.predecessor.elementId ).toBe( junction.id )
+
+ ////////////////////////////////////////////////////////////////////////
+ // junction connection tests
+ let connection = junction.getJunctionConnection( 1 );
+ let link = connection.laneLink[ 0 ];
+
+ expect( !connection ).toBe( false );
+ expect( connection.incomingRoad ).toBe( 2 );
+ expect( connection.connectingRoad ).toBe( 3 );
+ expect( connection.contactPoint ).toBe( TvContactPoint.START );
+ expect( link.from ).toBe( +1 );
+ expect( link.to ).toBe( -1 );
+
+ connection = junction.getJunctionConnection( 2 );
+ link = connection.laneLink[ 0 ];
+
+ expect( !connection ).toBe( false );
+ expect( connection.incomingRoad ).toBe( 1 );
+ expect( connection.connectingRoad ).toBe( 4 );
+ expect( connection.contactPoint ).toBe( TvContactPoint.START );
+ expect( link.from ).toBe( -1 );
+ expect( link.to ).toBe( -1 );
+
+
+ ////////////////////////////////////////////////////////////////////////
+ // connection road lane links
+ ////////////////////////////////////////////////////////////////////////
+
+ const laneC = roadC.getFirstLaneSection().getLaneById( -1 );
+
+ expect( !laneC ).toBe( false );
+ expect( laneC.predecessor ).toBe( 1 );
+ expect( laneC.succcessor ).toBe( 1 );
+
+ const laneD = roadD.getFirstLaneSection().getLaneById( -1 );
+
+ expect( !laneD ).toBe( false );
+ expect( laneD.predecessor ).toBe( -1 );
+ expect( laneD.succcessor ).toBe( -1 );
+
+
+ } );
+
+ // it( 'should connect road for connection', () => {
+
+ // // start-end <-> end:start
+ // // 1L <--- <---- -1R
+ // // -1R ---> ----> 1L
+
+ // const roadA = openDrive.addDefaultRoad();
+ // roadA.addGeometryLine( 0, 0, 0, 0, 10 );
+
+ // const roadB = openDrive.addDefaultRoad();
+ // roadB.addGeometryLine( 0, 25, 0, -Math.PI, 10 );
+
+ // const positionA = roadA.endPosition().toVector3();
+ // const positionB = roadB.startPosition().toVector3();
+
+ // let a = new JunctionEntryObject( "", positionA, OdContactPoints.END, roadA, roadA.getFirstLaneSection().getLaneById( 1 ) );
+ // let b = new JunctionEntryObject( "", positionB, OdContactPoints.END, roadB, roadB.getFirstLaneSection().getLaneById( 1 ) );
+
+ // let nodes = tool.findStartEndForRoad( a, b );
+
+ // expect( nodes ).toBeUndefined();
+
+ // /////////////////////////////////////////////////////////////////////////////
+
+ // a = new JunctionEntryObject( "", positionA, OdContactPoints.END, roadA, roadA.getFirstLaneSection().getLaneById( -1 ) );
+
+ // nodes = tool.findStartEndForRoad( a, b );
+
+ // expect( nodes.start.x ).toBe( 15 );
+ // expect( nodes.end.x ).toBe( 10 );
+
+ // } );
+
+ it( 'should connect right road for connection' );
+
+
+
+} );
\ No newline at end of file
diff --git a/src/app/core/tools/maneuver-tool.ts b/src/app/core/tools/maneuver-tool.ts
new file mode 100644
index 00000000..07ec7806
--- /dev/null
+++ b/src/app/core/tools/maneuver-tool.ts
@@ -0,0 +1,1084 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseTool } from './base-tool';
+import { MouseButton, PointerEventData } from 'app/events/pointer-event-data';
+import { CommandHistory } from 'app/services/command-history';
+import { LanePathFactory } from '../factories/lane-path-factory.service';
+import { KeyboardInput } from '../input';
+import { SetInspectorCommand } from '../commands/set-inspector-command';
+import { MultiCmdsCommand } from '../commands/multi-cmds-command';
+import { UpdateRoadPointCommand } from '../commands/update-road-point-command';
+import { Vector3 } from 'three';
+import { TvRoad } from 'app/modules/tv-map/models/tv-road.model';
+import { RoadControlPoint } from 'app/modules/three-js/objects/road-control-point';
+import { LanePathObject, TvJunctionLaneLink } from 'app/modules/tv-map/models/tv-junction-lane-link';
+import { LaneLinkInspector } from 'app/views/inspectors/lane-link-inspector/lane-link-inspector.component';
+import { JunctionEntryObject } from 'app/modules/three-js/objects/junction-entry.object';
+import { PickingHelper } from '../services/picking-helper.service';
+import { SetValueCommand } from 'app/modules/three-js/commands/set-value-command';
+import { TvContactPoint, TvElementType, TvLaneSide, TvLaneType } from 'app/modules/tv-map/models/tv-common';
+import { TvMapQueries } from 'app/modules/tv-map/queries/tv-map-queries';
+import { SceneService } from '../services/scene.service';
+import { OdLaneDirectionBuilder } from 'app/modules/tv-map/builders/od-lane-direction-builder';
+import { TvPosTheta } from 'app/modules/tv-map/models/tv-pos-theta';
+import { AutoSpline } from '../shapes/auto-spline';
+import { RoadFactory } from '../factories/road-factory.service';
+import { TvJunction } from 'app/modules/tv-map/models/tv-junction';
+import { TvJunctionConnection } from 'app/modules/tv-map/models/tv-junction-connection';
+import { SnackBar } from 'app/services/snack-bar.service';
+import { RoadControlPointInspector } from 'app/views/inspectors/road-control-point-inspector/road-control-point-inspector.component';
+import { JunctionEntryInspector } from 'app/views/inspectors/junction-entry-inspector/junction-entry-inspector.component';
+import { AddConnectionCommand } from '../commands/add-connection-command';
+
+const DEFAULT_SIDE = TvLaneSide.RIGHT;
+
+export class ManeuverTool extends BaseTool {
+
+ name: string = 'ManeuverTool';
+
+ public connectingRoad: TvRoad;
+
+ public roadControlPoint: RoadControlPoint;
+
+ public lanePathObject: LanePathObject;
+
+ private pointerDown = false;
+
+ private pointerDownAt: Vector3;
+
+ private roadChanged = false;
+
+ private junctionEntryObjects = [];
+ private lanePathObjects = [];
+
+ private laneDirectionHelper = new OdLaneDirectionBuilder( null );
+
+ private junctionEntryObject: JunctionEntryObject;
+
+ init () {
+
+
+ }
+
+ enable () {
+
+ super.enable();
+
+ this.junctionEntryObject = null;
+ this.lanePathObject = null;
+ this.connectingRoad = null;
+ this.roadControlPoint = null;
+
+ this.showJunctionEntries();
+
+ this.showLanePathObjects();
+ }
+
+ disable () {
+
+ super.disable();
+
+ this.junctionEntryObject = null;
+ this.lanePathObject = null;
+ this.connectingRoad = null;
+ this.roadControlPoint = null;
+
+ this.hideJunctionEntries();
+
+ this.hideLanePathObjects();
+
+ this.laneDirectionHelper.clear();
+ }
+
+ onPointerDown ( e: PointerEventData ) {
+
+ if ( e.button === MouseButton.RIGHT || e.button === MouseButton.MIDDLE ) return;
+
+ this.pointerDown = true;
+
+ this.pointerDownAt = e.point.clone();
+
+ const shiftKeyDown = KeyboardInput.isShiftKeyDown;
+
+ let hasInteracted = false;
+
+ if ( !shiftKeyDown && !hasInteracted ) hasInteracted = this.checkRoadControlPointInteraction( e );
+
+ if ( !shiftKeyDown && !hasInteracted ) hasInteracted = this.checkJunctionEntryInteraction( e );
+
+ if ( !shiftKeyDown && !hasInteracted ) hasInteracted = this.checkPathInteraction( e );
+
+ if ( !hasInteracted ) {
+
+ const commands = [];
+
+ commands.push( new SetInspectorCommand( null, null ) );
+
+ CommandHistory.execute( new MultiCmdsCommand( commands ) );
+
+ if ( this.connectingRoad ) {
+
+ this.connectingRoad.hideNodes();
+ this.connectingRoad.spline.hide();
+ }
+
+ // if ( this.lanePathObject ) this.lanePathObject.visible = false;
+ }
+ }
+
+ onPointerUp ( e: PointerEventData ) {
+
+ if ( this.connectingRoad && this.roadControlPoint && this.roadChanged ) {
+
+ const updateRoadPointCommand = new UpdateRoadPointCommand(
+ this.connectingRoad,
+ this.roadControlPoint,
+ this.roadControlPoint.position,
+ this.pointerDownAt
+ );
+
+ CommandHistory.execute( updateRoadPointCommand );
+
+ LanePathFactory.update( this.lanePathObject );
+ }
+
+ this.pointerDown = false;
+
+ this.roadChanged = false;
+
+ this.pointerDownAt = null;
+ }
+
+ onPointerMoved ( e: PointerEventData ) {
+
+ if ( this.pointerDown && this.roadControlPoint && this.connectingRoad ) {
+
+ this.roadControlPoint.copyPosition( e.point );
+
+ this.connectingRoad.spline.update();
+
+ this.roadChanged = true;
+
+ }
+
+ }
+
+ checkPathInteraction ( event: PointerEventData ): boolean {
+
+ if ( event.button !== MouseButton.LEFT ) return;
+
+ let hasInteracted = false;
+
+ for ( let i = 0; i < event.intersections.length; i++ ) {
+
+ const intersection = event.intersections[ i ];
+
+ // tslint:disable-next-line: no-string-literal
+ if ( intersection.object[ 'tag' ] === LanePathObject.tag ) {
+
+ hasInteracted = true;
+
+ this.lanePathObject = intersection.object.parent as LanePathObject;
+
+ const commands = [];
+
+ this.connectingRoad = this.lanePathObject.connectingRoad;
+
+ commands.push( new SetInspectorCommand( LaneLinkInspector, this.lanePathObject ) );
+
+ CommandHistory.execute( new MultiCmdsCommand( commands ) );
+
+ break;
+ }
+ }
+
+ return hasInteracted;
+ }
+
+ checkJunctionEntryInteraction ( event: PointerEventData ): boolean {
+
+ if ( event.button !== MouseButton.LEFT ) return;
+
+ let hasInteracted = false;
+
+ for ( let i = 0; i < event.intersections.length; i++ ) {
+
+ const intersection = event.intersections[ i ];
+
+ // tslint:disable-next-line: no-string-literal
+ if ( intersection.object[ 'tag' ] === JunctionEntryObject.tag ) {
+
+ hasInteracted = true;
+
+ const junctionObject = intersection.object as JunctionEntryObject;
+
+ const tryToConnect = this.junctionEntryObject && junctionObject;
+
+ if ( tryToConnect ) {
+
+ this.connectJunctionObject( this.junctionEntryObject, junctionObject );
+
+ // const junctionEntryObject = created ? null : junctionObject;
+
+ // CommandHistory.executeAll( [
+
+ // new SetValueCommand( this, 'junctionEntryObject', junctionEntryObject ),
+
+ // new SetInspectorCommand( JunctionEntryInspector, junctionObject ),
+
+ // ] );
+
+ } else {
+
+ CommandHistory.executeAll( [
+
+ new SetValueCommand( this, 'junctionEntryObject', junctionObject ),
+
+ new SetInspectorCommand( JunctionEntryInspector, junctionObject ),
+
+ ] );
+
+ }
+
+ break;
+ }
+ }
+
+ if ( !hasInteracted ) {
+
+ CommandHistory.executeAll( [
+
+ new SetValueCommand( this, 'junctionEntryObject', null ),
+
+ new SetInspectorCommand( null, null )
+
+ ] );
+
+ }
+
+ return hasInteracted;
+ }
+
+ checkRoadControlPointInteraction ( e: PointerEventData ): boolean {
+
+ // return if no connectingRoad is selected
+ if ( !this.connectingRoad || !this.connectingRoad.spline ) return false;
+
+ if ( !e.point ) return;
+
+ const maxDistance = Math.max( 0.5, e.approxCameraDistance * 0.01 );
+
+ const roadControlPoints = [];
+
+ this.connectingRoad.spline.controlPoints.forEach( ( cp: RoadControlPoint ) => {
+
+ roadControlPoints.push( cp );
+
+ if ( cp.frontTangent ) roadControlPoints.push( cp.frontTangent );
+
+ if ( cp.backTangent ) roadControlPoints.push( cp.backTangent );
+
+ } );
+
+ const roadControlPoint = PickingHelper.findNearest( e.point, roadControlPoints, maxDistance );
+
+ if ( roadControlPoint ) {
+
+ CommandHistory.executeAll( [
+
+ new SetValueCommand( this, 'roadControlPoint', roadControlPoint ),
+
+ new SetInspectorCommand( RoadControlPointInspector, roadControlPoint ),
+
+ ] );
+
+ } else {
+
+ CommandHistory.executeAll( [
+
+ new SetValueCommand( this, 'roadControlPoint', null ),
+
+ new SetInspectorCommand( null, null ),
+
+ ] );
+
+ }
+
+ return roadControlPoint != null;
+ }
+
+ connectJunctionObject ( a: JunctionEntryObject, b: JunctionEntryObject ): void {
+
+ if ( a == null || b == null ) return;
+
+ if ( a.id === b.id ) {
+
+ CommandHistory.execute( new SetValueCommand( this, 'junctionEntryObject', null ) );
+
+ SnackBar.error( "Select a different entry/exit" );
+
+ return;
+ }
+
+ const entryExitSide = this.findEntryExitSide( a, b );
+
+ if ( !entryExitSide ) {
+
+ CommandHistory.execute( new SetValueCommand( this, 'junctionEntryObject', null ) );
+
+ SnackBar.error( "Cannot connect" );
+
+ return;
+ }
+
+ const entry = entryExitSide.entry;
+ const exit = entryExitSide.exit;
+
+ // if ( this.connectingRoad ) {
+
+ // this.connectingRoad.hideNodes();
+ // this.connectingRoad.spline.hide();
+ // }
+
+ try {
+
+ const junction = this.findJunction( a, b );
+
+ const result = this.hasConnection( junction, entry, exit );
+
+ if ( result.connectionFound && result.laneLinkFound ) {
+
+ CommandHistory.execute( new SetValueCommand( this, 'junctionEntryObject', null ) );
+
+ SnackBar.error( "Connection already exists" );
+
+ } else if ( result.connectionFound && !result.laneLinkFound ) {
+
+ CommandHistory.execute( new SetValueCommand( this, 'junctionEntryObject', null ) );
+
+ // this.createLink( result.connection, entry, exit, side, laneWidth, junction );
+
+ // SnackBar.success( "Connection created" );
+
+ } else if ( !result.connectionFound && !result.laneLinkFound ) {
+
+ this.createNewConnection( entry, exit, junction );
+
+ SnackBar.success( "Connection created" );
+ }
+
+ } catch ( error ) {
+
+ console.log( error );
+
+ return;
+ }
+
+ }
+
+ createNewConnection ( entry: JunctionEntryObject, exit: JunctionEntryObject, junction: TvJunction ) {
+
+ // for connection
+ // create road
+ // create connection and link
+ // set neighbours
+ // set lane sucessor/predessor
+ // create lane path
+
+ // const laneWidth = entry.lane.getWidthValue( 0 );
+
+ // const connectingRoad = this.createConnectingRoad( entry, exit, DEFAULT_SIDE, laneWidth, junction );
+
+ // const result = this.createConnections( junction, entry, connectingRoad, exit );
+
+ // if ( result == null ) return;
+
+ // const connection = result.connection;
+
+ // const link = result.link;
+
+ // // TODO: find the connecting lane
+ // const lane = connectingRoad.getFirstLaneSection().getLaneById( -1 );
+
+ // const lanePathObject = result.link.mesh = LanePathFactory.createPathForLane(
+ // entry.road, connectingRoad, lane, result.connection, result.link
+ // );
+
+ // this.lanePathObjects.push( this.lanePathObject );
+
+ // SceneService.add( lanePathObject );
+
+ // RoadFactory.rebuildRoad( connectingRoad );
+
+ // this.connectingRoad = connectingRoad;
+
+
+ // set value command - connectingRoad
+ // set value command - lanePathObject
+ // set
+
+
+ CommandHistory.executeAll( [
+
+ // new SetValueCommand( this, 'lanePathObject', lanePathObject ),
+
+ // new SetValueCommand( this, 'connectingRoad', connectingRoad ),
+
+ new SetValueCommand( this, 'junctionEntryObject', null ),
+
+ new SetInspectorCommand( null, null ),
+
+ new AddConnectionCommand( entry, exit, junction, this ),
+
+ ] );
+
+
+
+
+
+ }
+
+ /**
+ *
+ * @deprecated currently not being used
+ * @param connection
+ * @param entry
+ * @param exit
+ * @param side
+ * @param laneWidth
+ * @param junction
+ */
+ createLink (
+ connection: TvJunctionConnection,
+ entry: JunctionEntryObject,
+ exit: JunctionEntryObject,
+ side: TvLaneSide,
+ laneWidth: number,
+ junction: TvJunction
+ ) {
+
+ const connectionRoad = this.openDrive.getRoadById( connection.connectingRoad );
+
+ const laneSection = connectionRoad.getFirstLaneSection();
+
+ // -ve because its always for right side
+ const newLaneId = -1 * ( laneSection.getRightLaneCount() + 1 );
+
+ const nodes = this.getSplinePositions( entry, exit, side );
+
+ const connectingLane = laneSection.addLane( nodes.side, newLaneId, entry.lane.type, entry.lane.level, true );
+
+ connectingLane.addWidthRecord( 0, laneWidth, 0, 0, 0 );
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ connectionRoad.setPredecessor( "road", entry.road.id, entry.contact );
+
+ connectionRoad.setSuccessor( "road", exit.road.id, exit.contact );
+
+ connectingLane.setPredecessor( entry.lane.id );
+
+ connectingLane.setSuccessor( exit.lane.id );
+
+ const link = connection.addNewLink( entry.lane.id, connectingLane.id );
+
+ if ( entry.contact === TvContactPoint.START ) {
+
+ entry.road.setPredecessor( "junction", junction.id );
+
+ } else if ( entry.contact === TvContactPoint.END ) {
+
+ entry.road.setSuccessor( "junction", junction.id );
+
+ }
+
+ if ( exit.contact === TvContactPoint.START ) {
+
+ exit.road.setPredecessor( "junction", junction.id );
+
+ } else if ( exit.contact === TvContactPoint.END ) {
+
+ exit.road.setSuccessor( "junction", junction.id );
+
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ this.lanePathObject = LanePathFactory.createPathForLane( entry.road, this.connectingRoad, connectingLane, connection, link );
+
+ this.lanePathObjects.push( this.lanePathObject );
+
+ SceneService.add( this.lanePathObject );
+
+ RoadFactory.rebuildRoad( this.connectingRoad );
+
+ }
+
+ createConnectingRoad ( entry, exit, side, laneWidth, junction ) {
+
+ const spline = this.createSpline( entry, exit, side )
+
+ const connectingRoad = this.openDrive.addConnectingRoad( DEFAULT_SIDE, laneWidth, junction.id );
+
+ connectingRoad.spline = spline;
+
+ connectingRoad.updateGeometryFromSpline();
+
+ connectingRoad.spline.hide();
+
+ return connectingRoad;
+ }
+
+ createSpline ( entry, exit, side ) {
+
+ const nodes = this.getSplinePositions( entry, exit, side );
+
+ const spline = new AutoSpline();
+
+ SceneService.add( spline.addControlPointAt( nodes.start ) );
+ SceneService.add( spline.addControlPointAt( nodes.a2.toVector3() ) );
+ SceneService.add( spline.addControlPointAt( nodes.b2.toVector3() ) );
+ SceneService.add( spline.addControlPointAt( nodes.end ) );
+
+ spline.controlPoints.forEach( ( cp: RoadControlPoint ) => cp.allowChange = false );
+
+ return spline;
+ }
+
+ findEntryExitSide ( a: JunctionEntryObject, b: JunctionEntryObject ) {
+
+ const isAEntry =
+ ( a.lane.direction === "forward" && a.contact === TvContactPoint.END ) ||
+ ( a.lane.direction === "backward" && a.contact === TvContactPoint.START );
+
+ const isBEntry =
+ ( b.lane.direction === "forward" && b.contact === TvContactPoint.END ) ||
+ ( b.lane.direction === "backward" && b.contact === TvContactPoint.START );
+
+ const isAExit =
+ ( a.lane.direction === "forward" && a.contact === TvContactPoint.START ) ||
+ ( a.lane.direction === "backward" && a.contact === TvContactPoint.END );
+
+ const isBExit =
+ ( b.lane.direction === "forward" && b.contact === TvContactPoint.START ) ||
+ ( b.lane.direction === "backward" && b.contact === TvContactPoint.END );
+
+
+ if ( isAEntry && isBExit ) {
+
+ return {
+ entry: a,
+ exit: b,
+ side: TvLaneSide.RIGHT
+ }
+
+ // return [ a, b, LaneSide.LEFT ];
+
+ } else if ( isAExit && isBEntry ) {
+
+ return {
+ entry: b,
+ exit: a,
+ side: TvLaneSide.RIGHT
+ }
+
+ // return [ b, a, LaneSide.RIGHT ];
+
+ } else {
+
+ a.unselect();
+
+ b.unselect();
+
+ return;
+ }
+ }
+
+ // hasConnection ( junction: OdJunction, a: JunctionEntryObject, b: JunctionEntryObject, result?: any ) {
+
+ // let connectionFound = false;
+ // let connection: OdJunctionConnection = null;
+ // let laneLinkFound = false;
+ // let laneLink: OdJunctionLaneLink = null;
+
+ // this.openDrive.roads.forEach( road => {
+
+ // if ( road.isJunction ) {
+
+ // const connectionFound =
+ // road.predecessor.elementId === a.road.id &&
+ // road.successor.elementId === b.road.id;
+
+ // const connectingLane = road.getFirstLaneSection().getLaneVector()
+ // .find( lane => lane.predecessor === a.lane.id && lane.succcessor === b.lane.id );
+
+ // // const connection = [ ...junction.connections.values() ]
+ // // .find( i => i.incomingRoad === a.road.id && i.connectingRoad === road.id );
+
+ // // const linkSide = link.from > 0 ? LaneSide.LEFT : LaneSide.RIGHT;
+ // // const aSide = a.lane.id > 0 ? LaneSide.LEFT : LaneSide.RIGHT;
+ // // const laneSideMatches = linkSide === aSide;
+
+ // if ( connectionFound && connectingLane != null ) {
+
+ // // connection and lane both found
+
+
+ // } else if ( connectionFound && connectingLane == null ) {
+
+ // // create lane-link
+
+ // } else if ( !connectionFound && connectingLane == null ) {
+
+ // // create connection and lane-link
+
+ // }
+
+ // }
+
+ // } );
+
+ // return { connectionFound, connection, laneLinkFound, laneLink };
+ // }
+
+ // hasConnection ( junction: OdJunction, a: JunctionEntryObject, b: JunctionEntryObject, result?: any ) {
+
+ // let connectionFound = false;
+ // let connection: OdJunctionConnection = null;
+ // let laneLinkFound = false;
+ // let laneLink: OdJunctionLaneLink = null;
+
+ // for ( const item of junction.connections ) {
+
+ // if ( item[ 1 ].incomingRoad === a.road.id ) {
+
+ // const incomingRoad = a.road;
+
+ // const connectingRoad = this.openDrive.getRoadById( item[ 1 ].connectingRoad );
+
+ // const outgoingRoad = b.road;
+
+ // const outgoingRoadMatches =
+ // connectingRoad.successor.elementId === outgoingRoad.id ||
+ // connectingRoad.predecessor.elementId === outgoingRoad.id;
+
+ // if ( outgoingRoadMatches ) {
+
+ // for ( const link of item[ 1 ].laneLink ) {
+
+ // const linkSide = link.from > 0 ? LaneSide.LEFT : LaneSide.RIGHT;
+
+ // const aSide = a.lane.id > 0 ? LaneSide.LEFT : LaneSide.RIGHT;
+
+ // const laneSideMatches = linkSide === aSide;
+
+ // if ( laneSideMatches ) {
+
+ // connectionFound = true;
+
+ // connection = result = item[ 1 ];
+
+ // const sameLaneId = Math.abs( a.lane.id ) === Math.abs( b.lane.id );
+
+ // // const connectingLane = connectingRoad.getFirstLaneSection().getLaneById( link.to );
+
+ // const connectingLane = connectingRoad
+ // .getFirstLaneSection()
+ // .getLaneVector()
+ // .find( i => i.predecessor === a.lane.id && i.succcessor === b.lane.id );
+
+ // laneLinkFound = link.from === a.lane.id && connectingLane != null;
+
+ // laneLink = link;
+
+ // // we dont want to break the loop unless we have found the right link
+ // if ( laneLinkFound ) break;
+ // }
+ // }
+ // }
+
+ // if ( connectionFound ) break;
+ // }
+ // }
+
+ // return { connectionFound, connection, laneLinkFound, laneLink };
+ // }
+
+ hasConnection ( junction: TvJunction, a: JunctionEntryObject, b: JunctionEntryObject, result?: any ) {
+
+ let connectionFound = false;
+ let connection: TvJunctionConnection = null;
+ let laneLinkFound = false;
+ let laneLink: TvJunctionLaneLink = null;
+
+ for ( const item of junction.connections ) {
+
+ if ( item[ 1 ].incomingRoad === a.road.id ) {
+
+ const incomingRoad = a.road;
+
+ const connectingRoad = this.openDrive.getRoadById( item[ 1 ].connectingRoad );
+
+ const outgoingRoad = b.road;
+
+ const outgoingRoadMatches =
+ connectingRoad.successor.elementId === outgoingRoad.id ||
+ connectingRoad.predecessor.elementId === outgoingRoad.id;
+
+ if ( outgoingRoadMatches ) {
+
+ for ( const link of item[ 1 ].laneLink ) {
+
+ connection = result = item[ 1 ];
+
+ const connectingLane = connectingRoad
+ .getFirstLaneSection()
+ .getLaneVector()
+ .find( i => i.predecessor === a.lane.id && i.succcessor === b.lane.id );
+
+ // basically if the same lane link is found only then it will
+ // return as connection found
+ // for now we will create a new connection for every lane
+ // TODO: fix this to combine lane links in 1 connection
+ connectionFound = laneLinkFound = link.from === a.lane.id && connectingLane != null;
+
+ laneLink = link;
+
+ // we dont want to break the loop unless we have found the right link
+ if ( laneLinkFound ) break;
+ }
+ }
+
+ if ( connectionFound ) break;
+ }
+ }
+
+ return { connectionFound, connection, laneLinkFound, laneLink };
+ }
+
+ // start position is always at the entry
+ // end position is always at the exit
+ getSplinePositions ( entry: JunctionEntryObject, exit: JunctionEntryObject, laneSide: TvLaneSide ) {
+
+ const as = entry.contact === TvContactPoint.START ? 0 : entry.road.length;
+ const aPosTheta = new TvPosTheta();
+ const aPosition = TvMapQueries.getLaneStartPosition( entry.road.id, entry.lane.id, as, 0, aPosTheta );
+
+ const bs = exit.contact === TvContactPoint.START ? 0 : exit.road.length;
+ const bPosTheta = new TvPosTheta();
+ const bPosition = TvMapQueries.getLaneStartPosition( exit.road.id, exit.lane.id, bs, 0, bPosTheta );
+
+ let a2: TvPosTheta;
+ let b2: TvPosTheta;
+
+ const distance = aPosition.distanceTo( bPosition ) * 0.3;
+
+ if ( entry.contact === TvContactPoint.START && exit.contact === TvContactPoint.START ) {
+
+ a2 = aPosTheta.moveForward( -distance );
+ b2 = bPosTheta.moveForward( -distance );
+
+ } else if ( entry.contact === TvContactPoint.START && exit.contact === TvContactPoint.END ) {
+
+ a2 = aPosTheta.moveForward( -distance );
+ b2 = bPosTheta.moveForward( +distance );
+
+ } else if ( entry.contact === TvContactPoint.END && exit.contact === TvContactPoint.END ) {
+
+ a2 = aPosTheta.moveForward( +distance );
+ b2 = bPosTheta.moveForward( +distance );
+
+ } else if ( entry.contact === TvContactPoint.END && exit.contact === TvContactPoint.START ) {
+
+ a2 = aPosTheta.moveForward( +distance );
+ b2 = bPosTheta.moveForward( -distance );
+
+ }
+
+ return {
+ side: laneSide,
+ start: aPosition,
+ startPos: aPosTheta,
+ end: bPosition,
+ endPos: bPosTheta,
+ a2: a2,
+ b2: b2,
+ }
+ }
+
+ findJunction ( a: JunctionEntryObject, b: JunctionEntryObject ) {
+
+ // the nearest junction
+ let nearestJunction: TvJunction = null;
+ let nearestDistance = Number.MAX_VALUE;
+
+ this.openDrive.junctions.forEach( junction => {
+
+ if ( !junction.position && junction.connections.size > 0 ) {
+
+ const firstconnection = [ ...junction.connections.values() ][ 0 ]
+
+ const connectionRoad = this.openDrive.getRoadById( firstconnection.connectingRoad );
+
+ if ( firstconnection.contactPoint === TvContactPoint.START ) {
+
+ junction.position = connectionRoad.startPosition().toVector3();
+
+ } else {
+
+ junction.position = connectionRoad.endPosition().toVector3();
+
+ }
+
+ }
+
+ if ( junction.position ) {
+
+ const aDistance = junction.position.distanceTo( a.position );
+ const bDistance = junction.position.distanceTo( b.position );
+
+ if ( aDistance < nearestDistance ) {
+
+ nearestDistance = aDistance;
+
+ nearestJunction = junction;
+
+ }
+
+ if ( bDistance < nearestDistance ) {
+
+ nearestDistance = bDistance;
+
+ nearestJunction = junction;
+ }
+
+ }
+
+ } );
+
+ if ( !nearestJunction ) nearestJunction = this.openDrive.addNewJunction();
+
+ // todo make it the mid point between a and b
+ nearestJunction.position = a.position;
+
+ return nearestJunction;
+ }
+
+ createConnections (
+ junction: TvJunction, incoming: JunctionEntryObject, connectingRoad: TvRoad, outgoing: JunctionEntryObject
+ ) {
+
+ this.updateNeighbors( junction, incoming, connectingRoad, outgoing );
+
+ let connection = [ ...junction.connections.values() ]
+ .find( i => i.connectingRoad === connectingRoad.id && i.incomingRoad === incoming.road.id );
+
+ if ( !connection ) {
+ connection = junction.addNewConnection( incoming.road.id, connectingRoad.id, TvContactPoint.START, outgoing.road.id );
+ }
+
+ let connectingLane = connectingRoad.getFirstLaneSection().getLaneById( -Math.abs( incoming.lane.id ) );
+
+ if ( !connectingLane ) connectingLane = connectingRoad.getFirstLaneSection().getLaneById( -1 );
+
+ if ( !connectingLane ) throw new Error( "connection lane not found" );
+
+ if ( !connectingLane ) return;
+
+ const link = connection.addNewLink( incoming.lane.id, connectingLane.id );
+
+ connectingLane.setPredecessor( incoming.lane.id );
+
+ connectingLane.setSuccessor( outgoing.lane.id );
+
+ return { connection, link };
+ }
+
+ updateNeighbors (
+ junction: TvJunction,
+ incoming: JunctionEntryObject,
+ connectingRoad: TvRoad,
+ outgoing: JunctionEntryObject
+ ): void {
+
+ connectingRoad.setPredecessor( "road", incoming.road.id, incoming.contact );
+
+ connectingRoad.setSuccessor( "road", outgoing.road.id, outgoing.contact );
+
+ if ( incoming.contact === TvContactPoint.START ) {
+
+ incoming.road.setPredecessor( "junction", junction.id );
+
+ } else if ( incoming.contact === TvContactPoint.END ) {
+
+ incoming.road.setSuccessor( "junction", junction.id );
+
+ }
+
+ if ( outgoing.contact === TvContactPoint.START ) {
+
+ outgoing.road.setPredecessor( "junction", junction.id );
+
+ } else if ( outgoing.contact === TvContactPoint.END ) {
+
+ outgoing.road.setSuccessor( "junction", junction.id );
+
+ }
+ }
+
+ showJunctionEntries () {
+
+ this.openDrive.roads.forEach( road => {
+
+ if ( !road.isJunction ) {
+
+ if ( !road.predecessor || road.predecessor.elementType === TvElementType.junction ) {
+
+ this.showStartEntries( road );
+
+ }
+
+ if ( !road.successor || road.successor.elementType === TvElementType.junction ) {
+
+ this.showEndEntries( road );
+
+ }
+ }
+
+ } );
+
+ }
+
+ // showLanePathObjects () {
+
+ // this.openDrive.roads.forEach( road => {
+
+ // if ( road.isJunction ) {
+
+ // road.laneSections.forEach( section => {
+
+ // section.getLaneVector().forEach( lane => {
+
+ // if ( lane.id !== 0 && lane.type === OdLaneType.driving ) {
+
+ // // TODO: pass connection and lane link as well
+ // const lanePathObject = LanePathFactory.createPathForLane( null, road, lane, null, null );
+
+ // this.lanePathObjects.push( lanePathObject );
+
+ // SceneService.add( lanePathObject );
+
+ // }
+
+ // } )
+
+ // } );
+
+ // }
+
+ // } );
+
+ // }
+
+ showLanePathObjects () {
+
+ this.openDrive.junctions.forEach( junction => {
+
+ junction.connections.forEach( connection => {
+
+ const incomingRoad = this.openDrive.getRoadById( connection.incomingRoad );
+ const connectingRoad = this.openDrive.getRoadById( connection.connectingRoad );
+
+ connection.laneLink.forEach( link => {
+
+ const connectingLane = connectingRoad.getFirstLaneSection().getLaneById( link.to );
+
+ link.lanePath = LanePathFactory.createPathForLane( incomingRoad, connectingRoad, connectingLane, connection, link );
+
+ SceneService.add( link.lanePath );
+
+ } );
+
+ } );
+
+ } );
+
+ }
+
+ showStartEntries ( road: TvRoad ) {
+
+ road.getFirstLaneSection().getLaneVector().forEach( lane => {
+
+ if ( lane.id !== 0 && lane.type === TvLaneType.driving ) {
+
+ this.laneDirectionHelper.drawSingleLane( road, lane );
+
+ const position = TvMapQueries.getLanePosition( road.id, lane.id, 0 );
+
+ const name = `road-${ road.id }-lane-${ lane.id }-${ TvContactPoint.START }`;
+
+ const obj = new JunctionEntryObject( name, position, TvContactPoint.START, road, lane );
+
+ this.junctionEntryObjects.push( obj );
+
+ SceneService.add( obj );
+
+ }
+
+ } );
+ }
+
+ showEndEntries ( road: TvRoad ) {
+
+ road.getLastLaneSection().getLaneVector().forEach( lane => {
+
+ if ( lane.id !== 0 && lane.type === TvLaneType.driving ) {
+
+ this.laneDirectionHelper.drawSingleLane( road, lane );
+
+ const position = TvMapQueries.getLanePosition( road.id, lane.id, road.length );
+
+ const name = `road-${ road.id }-lane-${ lane.id }-${ TvContactPoint.END }`;
+
+ const obj = new JunctionEntryObject( name, position, TvContactPoint.END, road, lane );
+
+ this.junctionEntryObjects.push( obj );
+
+ SceneService.add( obj );
+
+ }
+
+ } );
+ }
+
+ hideJunctionEntries () {
+
+ this.junctionEntryObjects.forEach( obj => SceneService.remove( obj ) );
+
+ this.junctionEntryObjects.splice( 0, this.junctionEntryObjects.length );
+
+ }
+
+ hideLanePathObjects () {
+
+ this.lanePathObjects.forEach( obj => SceneService.remove( obj ) );
+
+ this.lanePathObjects.splice( 0, this.lanePathObjects.length );
+
+ this.openDrive.junctions.forEach( junction => {
+
+ junction.connections.forEach( connection => {
+
+ connection.laneLink.forEach( link => {
+
+ if ( link.lanePath ) SceneService.remove( link.lanePath );
+
+ } );
+
+ } );
+
+ } );
+ }
+}
\ No newline at end of file
diff --git a/src/app/core/tools/marking-line-tool.ts b/src/app/core/tools/marking-line-tool.ts
new file mode 100644
index 00000000..19b5e687
--- /dev/null
+++ b/src/app/core/tools/marking-line-tool.ts
@@ -0,0 +1,218 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseMarkingTool } from './marking-point-tool';
+import { AbstractShapeEditor } from '../editors/abstract-shape-editor';
+import { Subscription } from 'rxjs';
+import { TvRoadSignal } from 'app/modules/tv-map/models/tv-road-signal.model';
+import { LineEditor } from '../editors/line-editor';
+import * as THREE from 'three';
+import {
+ CurvePath,
+ ExtrudeBufferGeometry,
+ ExtrudeGeometryOptions,
+ Mesh,
+ MeshBasicMaterial,
+ Shape,
+ TextureLoader,
+ Vector2,
+ Vector3
+} from 'three';
+
+export class MarkingLineTool extends BaseMarkingTool {
+
+ name: string = 'MarkingLineTool';
+
+ private shapeEditor: AbstractShapeEditor;
+
+ private controlPointAddedSubscriber: Subscription;
+
+ private hasSignal = false;
+ private selectedSignal: TvRoadSignal;
+
+ private cpSubscriptions: Subscription[] = [];
+
+ private mesh: Mesh;
+
+ static texture = new TextureLoader().load( `assets/markings/crosswalk-marking.png` );
+
+ constructor () {
+
+ super();
+
+ }
+
+ init () {
+
+ super.init();
+
+ this.shapeEditor = new LineEditor();
+
+ // this.createControlPoints();
+
+ MarkingLineTool.texture.wrapS = THREE.RepeatWrapping;
+ MarkingLineTool.texture.wrapT = THREE.RepeatWrapping;
+ MarkingLineTool.texture.mapping = THREE.UVMapping;
+ MarkingLineTool.texture.repeat.set( 1, 1 );
+ MarkingLineTool.texture.anisotropy = 5;
+
+
+ }
+
+ enable () {
+
+ super.enable();
+
+ // this.controlPointAddedSubscriber = this.shapeEditor.controlPointAdded.subscribe( e => this.onControlPointAdded( e ) );
+
+ this.shapeEditor.curveGeometryAdded.subscribe( e => this.onGeometryAdded( e ) );
+ this.shapeEditor.curveGeometryChanged.subscribe( e => this.onGeometryChanged( e ) );
+
+ }
+
+ disable (): void {
+
+ super.disable();
+
+ // this.controlPointAddedSubscriber.unsubscribe();
+
+ this.shapeEditor.destroy();
+
+ // this.unsubscribeFromControlPoints();
+
+ }
+
+ getShape (): Shape {
+
+ const shape = new Shape();
+
+ shape.moveTo( 0, -2.5 );
+
+ shape.lineTo( 0, 2.5 );
+
+ return shape;
+
+ }
+
+ getGeometry ( line: THREE.LineCurve3 ) {
+
+ const shape = this.getShape();
+
+ const path = new CurvePath();
+
+ path.add( line );
+
+ const generator = new CustomUvGenerator();
+
+ const geometry = new ExtrudeBufferGeometry( shape, {
+ depth: 16,
+ extrudePath: path,
+ UVGenerator: generator
+ } );
+
+ geometry.attributes.faces;
+
+ return geometry;
+ }
+
+ onGeometryAdded ( line: THREE.LineCurve3 ) {
+
+ const mesh = this.mesh = new Mesh( this.getGeometry( line ), this.getMaterial() );
+
+ this.openDrive.gameObject.add( mesh );
+
+ }
+
+ onGeometryChanged ( line: THREE.LineCurve3 ) {
+
+ if ( this.mesh ) this.openDrive.gameObject.remove( this.mesh );
+
+ const mesh = this.mesh = new Mesh( this.getGeometry( line ), this.getMaterial() );
+
+ this.openDrive.gameObject.add( mesh );
+ }
+
+
+ getMaterial () {
+
+ return new MeshBasicMaterial( {
+ map: MarkingLineTool.texture,
+ transparent: true,
+ alphaTest: 0.1,
+ wireframe: false
+ } );
+
+ }
+
+}
+
+export class CustomUvGenerator implements THREE.UVGenerator {
+
+ generateTopUV ( geometry, vertices, indexA, indexB, indexC ) {
+
+ const a_x = vertices[ indexA * 3 ];
+ const a_y = vertices[ indexA * 3 + 1 ];
+ const b_x = vertices[ indexB * 3 ];
+ const b_y = vertices[ indexB * 3 + 1 ];
+ const c_x = vertices[ indexC * 3 ];
+ const c_y = vertices[ indexC * 3 + 1 ];
+
+ const res = [
+ new Vector2( a_x, a_y ),
+ new Vector2( b_x, b_y ),
+ new Vector2( c_x, c_y )
+ ];
+
+ return res;
+ }
+
+ generateSideWallUV ( geometry, vertices, indexA, indexB, indexC, indexD ) {
+
+ const a_x = vertices[ indexA * 3 ];
+ const a_y = vertices[ indexA * 3 + 1 ];
+ const a_z = vertices[ indexA * 3 + 2 ];
+ const b_x = vertices[ indexB * 3 ];
+ const b_y = vertices[ indexB * 3 + 1 ];
+ const b_z = vertices[ indexB * 3 + 2 ];
+ const c_x = vertices[ indexC * 3 ];
+ const c_y = vertices[ indexC * 3 + 1 ];
+ const c_z = vertices[ indexC * 3 + 2 ];
+ const d_x = vertices[ indexD * 3 ];
+ const d_y = vertices[ indexD * 3 + 1 ];
+ const d_z = vertices[ indexD * 3 + 2 ];
+
+ if ( Math.abs( a_y - b_y ) < 0.01 ) {
+
+ return [
+ new Vector2( a_x, 1 - a_z ),
+ new Vector2( b_x, 1 - b_z ),
+ new Vector2( c_x, 1 - c_z ),
+ new Vector2( d_x, 1 - d_z )
+ ];
+
+ } else {
+
+ return [
+ new Vector2( a_y, 1 - a_z ),
+ new Vector2( b_y, 1 - b_z ),
+ new Vector2( c_y, 1 - c_z ),
+ new Vector2( d_y, 1 - d_z )
+ ];
+
+ }
+
+ }
+
+}
+
+
+export class ExtrudePlaneGeometry {
+
+ constructor ( private shapes: Shape | Shape[], private options?: ExtrudeGeometryOptions ) {
+
+ // const path = options ? options.extrudePath : ;
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/app/core/tools/marking-point-tool.ts b/src/app/core/tools/marking-point-tool.ts
new file mode 100644
index 00000000..631409c1
--- /dev/null
+++ b/src/app/core/tools/marking-point-tool.ts
@@ -0,0 +1,244 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseTool } from './base-tool';
+import { Subscription } from 'rxjs';
+import { TvRoadSignal } from 'app/modules/tv-map/models/tv-road-signal.model';
+import { AbstractShapeEditor } from '../editors/abstract-shape-editor';
+import { PointEditor } from '../editors/point-editor';
+import { PointerEventData } from 'app/events/pointer-event-data';
+import { TvObjectType } from 'app/modules/tv-map/interfaces/i-tv-object';
+import { Mesh, MeshBasicMaterial, Object3D, PlaneBufferGeometry, TextureLoader } from 'three';
+import { OdSignalInspectorComponent } from 'app/views/inspectors/signal-inspector/signal-inspector.component';
+import { AnyControlPoint } from 'app/modules/three-js/objects/control-point';
+import { TvPosTheta } from 'app/modules/tv-map/models/tv-pos-theta';
+import { TvOrientation } from 'app/modules/tv-map/models/tv-common';
+import { SnackBar } from 'app/services/snack-bar.service';
+import { MarkingTypes, TvMarkingService, TvRoadMarking } from 'app/modules/tv-map/services/tv-marking.service';
+import { TvRoadObject } from 'app/modules/tv-map/models/tv-road-object';
+import { TvMapQueries } from '../../modules/tv-map/queries/tv-map-queries';
+
+export abstract class BaseMarkingTool extends BaseTool {
+
+}
+
+export class MarkingPointTool extends BaseMarkingTool {
+
+ name: string = 'MarkingPointTool';
+
+ private shapeEditor: AbstractShapeEditor;
+ private hasSignal = false;
+ private selectedSignal: TvRoadSignal;
+ private cpSubscriptions: Subscription[] = [];
+
+ private controlPointAddedSubscriber: Subscription;
+ private controlPointSelectedSubscriber: Subscription;
+
+ private currentMarking: TvRoadMarking;
+
+ constructor () {
+
+ super();
+
+ }
+
+ init () {
+
+ super.init();
+
+ this.shapeEditor = new PointEditor();
+
+ this.createControlPoints();
+
+ }
+
+ enable () {
+
+ super.enable();
+
+ this.controlPointAddedSubscriber = this.shapeEditor.controlPointAdded.subscribe( e => this.onControlPointAdded( e ) );
+ this.controlPointSelectedSubscriber = this.shapeEditor.controlPointSelected.subscribe( e => this.onControlPointSelected( e ) );
+
+ }
+
+ disable (): void {
+
+ super.disable();
+
+ this.controlPointAddedSubscriber.unsubscribe();
+
+ this.shapeEditor.destroy();
+
+ this.unsubscribeFromControlPoints();
+
+ }
+
+ get marking () {
+
+ return TvMarkingService.currentMarking;
+
+ }
+
+ // onPointerDown ( e: PointerEventData ) {
+
+ // super.onPointerDown( e );
+
+ // this.hasSignal = false;
+
+ // for ( const i of e.intersections ) {
+
+ // if ( i.object[ 'OpenDriveType' ] != null && i.object[ 'OpenDriveType' ] == TvObjectType.SIGNAL ) {
+
+ // this.hasSignal = true;
+
+ // this.inspectSignal( i.object );
+
+ // break;
+
+ // }
+ // }
+
+ // if ( !this.hasSignal ) {
+
+ // this.clearInspector();
+
+ // }
+ // }
+
+ // private inspectSignal ( object: Object3D ) {
+
+ // this.selectedSignal = ( object.userData.data as TvRoadSignal );
+
+ // this.setInspector( OdSignalInspectorComponent, this.selectedSignal );
+ // }
+
+ private onControlPointAdded ( point: AnyControlPoint ) {
+
+ if ( !this.marking ) SnackBar.error( "Select a marking from project browser" );
+
+ if ( !this.marking ) return;
+
+ const pose = new TvPosTheta();
+
+ pose.x = point.position.x;
+
+ pose.y = point.position.y;
+
+ const road = TvMapQueries.getRoadByCoords( pose.x, pose.y, pose );
+
+ if ( !road ) SnackBar.error( "Marking can be added only on road mesh" );
+
+ if ( !road ) this.shapeEditor.removeControlPoint( point );
+
+ if ( !road ) return;
+
+ if ( this.marking && this.marking.type === MarkingTypes.point ) {
+
+ // const id = road.getRoadObjectCount() + 1;
+
+ // const marking = this.marking.name;
+
+ // const texture = new TextureLoader().load( `assets/markings/${ marking }.png` );
+
+ // const material = new MeshBasicMaterial( { map: texture, alphaTest: 0.1 } );
+
+ // const geometry = new PlaneBufferGeometry( 1, 1 );
+
+ // const mesh = new Mesh( geometry, material );
+
+ const marking = point.mainObject = this.marking.clone();
+
+ marking.mesh.position.setX( point.position.x );
+
+ marking.mesh.position.setY( point.position.y );
+
+ this.openDrive.gameObject.add( marking.mesh );
+
+ // const roadObject = new TvRoadObject( 'marking', 'arrow-forward', id, pose.s, pose.t, 0, 0, TvOrientation.MINUS );
+
+ // roadObject.mesh = mesh;
+
+ this.sync( point, marking );
+
+ // road.addRoadObjectInstance( roadObject );
+
+ } else {
+
+ this.shapeEditor.removeControlPoint( point );
+
+ SnackBar.show( 'Please select a sign first' );
+
+ }
+
+ }
+
+ private onControlPointSelected ( point: AnyControlPoint ) {
+
+ console.log( point.mainObject );
+
+ }
+
+ private onConrolPointUpdated ( point: AnyControlPoint ) {
+
+ if ( point.mainObject instanceof TvRoadMarking ) {
+
+ this.currentMarking = point.mainObject;
+
+ point.mainObject.mesh.position.setX( point.position.x );
+
+ point.mainObject.mesh.position.setY( point.position.y );
+
+ }
+
+ }
+
+ private sync ( point: AnyControlPoint, object: TvRoadMarking ): void {
+
+ // const subscription = point.updated.subscribe( e => {
+
+ // object.mesh.position.setX( e.position.x );
+
+ // object.mesh.position.setY( e.position.y );
+
+ // } );
+
+ // this.cpSubscriptions.push( subscription );
+ }
+
+ private createControlPoints () {
+
+ // this.forEachRoadObject( object => {
+
+ // const cp = this.shapeEditor.addControlPoint( object.mesh.position );
+
+ // this.sync( cp, object );
+
+ // } );
+
+ }
+
+ private unsubscribeFromControlPoints () {
+
+ this.cpSubscriptions.forEach( sub => {
+
+ sub.unsubscribe();
+
+ } );
+
+ }
+
+ private forEachRoadObject ( callback: ( object: TvRoadObject ) => void ) {
+
+ // this.openDrive.roads.forEach( road => {
+
+ // road.objects.object.forEach( object => {
+
+ // if ( object.mesh ) callback( object );
+
+ // } );
+
+ // } );
+
+ }
+}
diff --git a/src/app/core/tools/misc-shape-tools.ts b/src/app/core/tools/misc-shape-tools.ts
new file mode 100644
index 00000000..f0dfa6d8
--- /dev/null
+++ b/src/app/core/tools/misc-shape-tools.ts
@@ -0,0 +1,70 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseRoadPlanTool } from './base-road-plan-tool';
+import { AnyControlPoint } from 'app/modules/three-js/objects/control-point';
+import { CatmullRomCurve3, BufferGeometry, LineBasicMaterial, Line, Vector2, Vector3, CubicBezierCurve3 } from 'three';
+import { SceneService } from '../services/scene.service';
+import { CubicSplineCurve } from '../shapes/cubic-spline-curve';
+
+export class MiscShapeTool extends BaseRoadPlanTool {
+
+ private cps: AnyControlPoint[] = [];
+
+ private curve: any;
+ private line: any;
+
+ init () {
+
+ super.init();
+
+ }
+
+ reraw () {
+
+ if (this.cps.length < 4) return;
+
+ if ( this.line != null ) SceneService.remove( this.line, false );
+
+ const positions: Vector3[] = this.cps.map( value => value.position );
+
+ // Create a sine-like wave
+ this.curve = new CubicBezierCurve3( positions[0], positions[1], positions[2], positions[3] );
+
+ const points = this.curve.getPoints( 50 );
+
+ const geometry = new BufferGeometry().setFromPoints( points );
+
+ const material = new LineBasicMaterial( { color: 0xff0000 } );
+
+ // Create the final object to add to the scene
+ this.line = new Line( geometry, material );
+
+ this.line.renderOrder = 3;
+
+ SceneService.add( this.line, false );
+
+ }
+
+ protected onControlPointSelected ( cp: AnyControlPoint ) {
+
+ }
+
+ protected onControlPointAdded ( cp: AnyControlPoint ) {
+
+ this.cps.push( cp );
+
+ // this.curve.points.push( cp.position );
+
+ this.reraw();
+
+ }
+
+ protected onControlPointMoved ( e: AnyControlPoint ) {
+
+ this.reraw();
+
+ }
+
+}
diff --git a/src/app/core/tools/parking-box-tool.ts b/src/app/core/tools/parking-box-tool.ts
new file mode 100644
index 00000000..37d0f322
--- /dev/null
+++ b/src/app/core/tools/parking-box-tool.ts
@@ -0,0 +1,138 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseTool } from './base-tool';
+import { BoxCreatedEvent, BoxEditor } from '../editors/box-editor';
+import { TvRoadObject } from 'app/modules/tv-map/models/tv-road-object';
+import { TvOrientation } from 'app/modules/tv-map/models/tv-common';
+import { TvPosTheta } from 'app/modules/tv-map/models/tv-pos-theta';
+import { PointerEventData } from 'app/events/pointer-event-data';
+import { Object3D } from 'three';
+import { CommandHistory } from 'app/services/command-history';
+import { AddRoadObjectCommand } from 'app/core/commands/add-road-object-command';
+import { Subscription } from 'rxjs';
+import { RoadObjectInspectorComponent } from 'app/views/inspectors/road-object-inspector/road-object-inspector.component';
+import { TvMapQueries } from '../../modules/tv-map/queries/tv-map-queries';
+
+export abstract class BaseParkingTool extends BaseTool {
+
+}
+
+export class ParkingBoxTool extends BaseParkingTool {
+
+ name = 'ParkingBoxTool';
+
+ init () {
+
+ super.init();
+
+ this.shapeEditor = new BoxEditor();
+
+ }
+
+ enable () {
+
+ super.enable();
+
+ this.boxCreatedSub = this.shapeEditor.boxCreated.subscribe( e => this.onBoxCreated( e ) );
+
+ }
+
+ disable (): void {
+
+ super.disable();
+
+ this.boxCreatedSub.unsubscribe();
+
+ this.shapeEditor.destroy();
+ }
+
+ shapeEditor: BoxEditor;
+ boxCreatedSub: Subscription;
+ selectedBox: Object3D;
+ selectedObject: TvRoadObject;
+ objects: TvRoadObject[] = [];
+
+ onBoxCreated ( e: BoxCreatedEvent ) {
+
+ const position = e.mesh.position;
+
+ const theta = new TvPosTheta();
+
+ const road = TvMapQueries.getRoadByCoords( position.x, position.y, theta );
+
+ const type = 'parkingSpace';
+ const name = '';
+ const id = 1;
+ const s = theta.s;
+ const t = theta.t;
+ const z = 0;
+ const validLength = 0.0;
+ const orientation = TvOrientation.NONE;
+ const length = e.length;
+ const width = e.width;
+ const height = e.height;
+ const radius = null;
+ const hdg = 0;
+ const pitch = 0;
+ const roll = 0;
+
+ const object = new TvRoadObject(
+ type, name, id, s, t, z, validLength, orientation, length, width, radius, height, hdg, pitch, roll
+ );
+
+ object.mesh = e.mesh;
+
+ this.objects.push( object );
+
+ CommandHistory.execute( new AddRoadObjectCommand( road.id, object ) );
+
+ }
+
+ onPointerClicked ( e: PointerEventData ) {
+
+ let found = false;
+
+ for ( const intersection of e.intersections ) {
+
+ for ( const object of this.objects ) {
+
+ if ( object.mesh.id === intersection.object.id ) {
+
+ this.selectedBox = intersection.object;
+
+ this.selectedObject = object;
+
+ // AppService.three.select( i.object );
+
+ found = true;
+
+ // this.shapeEditor.disable();
+
+ this.setInspector( RoadObjectInspectorComponent, object );
+
+ break;
+ }
+
+ }
+
+ }
+
+ if ( !found ) {
+
+ this.selectedBox = null;
+
+ this.selectedObject = null;
+
+ this.clearInspector();
+
+ // AppService.three.deselect();
+
+ // this.shapeEditor.enable();
+ }
+
+
+ }
+
+}
diff --git a/src/app/core/tools/prop-curve-tool.ts b/src/app/core/tools/prop-curve-tool.ts
new file mode 100644
index 00000000..8228c9d3
--- /dev/null
+++ b/src/app/core/tools/prop-curve-tool.ts
@@ -0,0 +1,279 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseTool } from './base-tool';
+import { PointEditor } from '../editors/point-editor';
+import { AnyControlPoint } from 'app/modules/three-js/objects/control-point';
+import { Subscription } from 'rxjs';
+import { AppInspector } from '../inspector';
+import { PointerEventData } from 'app/events/pointer-event-data';
+import { KeyboardInput } from '../input';
+import { PropCurve } from "app/modules/tv-map/models/prop-curve";
+import {
+ PropCurveInspectorComponent,
+ PropCurveInspectorData
+} from 'app/views/inspectors/prop-curve-inspector/prop-curve-inspector.component';
+import { PropService } from 'app/services/prop-service';
+import { SnackBar } from 'app/services/snack-bar.service';
+import { PropModel } from '../models/prop-model.model';
+
+export class PropCurveTool extends BaseTool {
+
+ public name: string = 'PropCurveTool';
+
+ public shapeEditor: PointEditor;
+
+ private cpSubscriptions: Subscription[] = [];
+
+ private cpAddedSub: Subscription;
+ private cpMovedSub: Subscription;
+ private cpUpdatedSub: Subscription;
+ private cpSelectedSub: Subscription;
+ private cpUnselectedSub: Subscription;
+
+ // private splines: ParmetricSpline[] = [];
+ // private selectedSpline: ParmetricSpline;
+
+ private curve: PropCurve;
+ private point: AnyControlPoint;
+
+ private get curves () { return this.openDrive.propCurves; }
+
+ private get spline () { return this.curve ? this.curve.spline : null; }
+
+ constructor () {
+
+ super();
+
+ }
+
+ private get prop (): PropModel {
+
+ const prop = PropService.getProp();
+
+ if ( prop ) {
+
+ return new PropModel( prop.guid, prop.data.rotationVariance, prop.data.scaleVariance );
+
+ }
+
+ }
+
+ public init () {
+
+ super.init();
+
+ this.shapeEditor = new PointEditor( 100 );
+ }
+
+ public enable () {
+
+ super.enable();
+
+ this.curves.forEach( curve => {
+
+ curve.spline.show();
+
+ curve.spline.controlPoints.forEach( point => {
+
+ this.shapeEditor.controlPoints.push( point );
+
+ } );
+
+ } );
+
+ this.cpAddedSub = this.shapeEditor.controlPointAdded
+ .subscribe( ( cp: AnyControlPoint ) => this.onControlPointAdded( cp ) );
+
+ this.cpMovedSub = this.shapeEditor.controlPointMoved
+ .subscribe( ( cp: AnyControlPoint ) => this.onControlPointMoved( cp ) );
+
+ this.cpUpdatedSub = this.shapeEditor.controlPointUpdated
+ .subscribe( ( cp: AnyControlPoint ) => this.onControlPointUpdated( cp ) );
+
+ this.cpSelectedSub = this.shapeEditor.controlPointSelected
+ .subscribe( ( cp: AnyControlPoint ) => this.onControlPointSelected( cp ) );
+
+ this.cpUnselectedSub = this.shapeEditor.controlPointUnselected
+ .subscribe( ( cp: AnyControlPoint ) => this.onControlPointUnselected() );
+
+ }
+
+ public disable (): void {
+
+ super.disable();
+
+ this.curves.forEach( curve => curve.spline.hide() );
+
+ this.cpAddedSub.unsubscribe();
+ this.cpMovedSub.unsubscribe();
+ this.cpUpdatedSub.unsubscribe();
+ this.cpSelectedSub.unsubscribe();
+ this.cpUnselectedSub.unsubscribe();
+
+ this.shapeEditor.destroy();
+
+ this.unsubscribeFromControlPoints();
+ }
+
+ public onPointerClicked ( e: PointerEventData ) {
+
+ let hasInteracted = false;
+
+ // first check for any point intersections
+ for ( const i of e.intersections ) {
+
+ if ( i.object != null && i.object.type === 'Points' ) {
+
+ hasInteracted = true;
+
+ this.point = ( i.object as AnyControlPoint );
+
+ this.curve = this.point.mainObject as PropCurve;
+
+ this.showInspector( this.curve, this.point );
+
+ break;
+ }
+
+ }
+
+ // if not point intersections then check for curve intersections
+ for ( const i of e.intersections ) {
+
+ if ( i.object != null && i.object[ 'tag' ] === 'curve' && !hasInteracted ) {
+
+ hasInteracted = true;
+
+ this.curve = i.object.userData.parent;
+
+ this.point = null;
+
+ this.showInspector( this.curve, this.point );
+
+ break;
+
+ }
+ }
+
+ if ( hasInteracted ) return;
+
+ // Finally, If no objects were intersected then clear the inspector
+ if ( !KeyboardInput.isShiftKeyDown ) {
+
+ // unselect the curve in second click
+ if ( !this.point ) {
+
+ this.curve = null;
+
+ this.showInspector( this.curve, this.point );
+
+ } else {
+
+ // in first click, remove focus from control point and hide tangent
+ this.point = null;
+
+ this.showInspector( this.curve, this.point );
+ }
+ }
+ }
+
+ private onControlPointSelected ( cp: AnyControlPoint ) {
+
+ this.point = cp;
+
+ if ( cp.mainObject ) {
+
+ this.curve = cp.mainObject;
+
+ this.showInspector( this.curve, this.point );
+
+ }
+ }
+
+ private onControlPointUnselected () {
+
+ }
+
+ private showInspector ( curve: PropCurve, point: AnyControlPoint ) {
+
+ if ( curve == null && point == null ) {
+
+ AppInspector.clear();
+
+ } else {
+
+ const data = new PropCurveInspectorData( point, curve );
+
+ AppInspector.setInspector( PropCurveInspectorComponent, data );
+ }
+ }
+
+ private onControlPointAdded ( cp: AnyControlPoint ) {
+
+ if ( !this.prop ) SnackBar.error( "Select a prop from the project browser" );
+
+ if ( !this.prop ) this.shapeEditor.removeControlPoint( cp );
+
+ if ( !this.prop ) return;
+
+ if ( !this.curve ) {
+
+ if ( this.spline ) this.spline.hide();
+
+ this.curve = new PropCurve( this.prop.guid );
+
+ this.curves.push( this.curve );
+ }
+
+ cp.mainObject = this.curve;
+
+ this.curve.addControlPoint( cp );
+
+ this.showInspector( this.curve, this.point );
+
+ this.updateCurveProps();
+ }
+
+ private onControlPointUpdated ( cp: AnyControlPoint ) {
+
+ if ( cp.mainObject ) {
+
+ this.curve = cp.mainObject;
+
+ this.curve.update();
+
+ this.updateCurveProps();
+
+ }
+ }
+
+ private onControlPointMoved ( cp: AnyControlPoint ) {
+
+ if ( cp.mainObject ) {
+
+ this.curve = cp.mainObject;
+
+ this.curve.update();
+
+ }
+
+ }
+
+ private unsubscribeFromControlPoints () {
+
+ this.cpSubscriptions.forEach( sub => {
+
+ sub.unsubscribe();
+
+ } );
+
+ }
+
+ private updateCurveProps () {
+
+ PropService.updateCurveProps( this.curve );
+
+ }
+}
\ No newline at end of file
diff --git a/src/app/core/tools/prop-point-tool.ts b/src/app/core/tools/prop-point-tool.ts
new file mode 100644
index 00000000..63e000e8
--- /dev/null
+++ b/src/app/core/tools/prop-point-tool.ts
@@ -0,0 +1,168 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseTool } from './base-tool';
+import { PointEditor } from '../editors/point-editor';
+import { AnyControlPoint } from 'app/modules/three-js/objects/control-point';
+import { Subscription } from 'rxjs';
+import { TvMapQueries } from 'app/modules/tv-map/queries/tv-map-queries';
+import { SnackBar } from 'app/services/snack-bar.service';
+import { AppInspector } from '../inspector';
+import { PropInstance } from '../models/prop-instance.model';
+import { InspectorFactoryService, InspectorType } from '../factories/inspector-factory.service';
+import { ProjectBrowserService } from 'app/views/editor/project-browser/project-browser.service';
+import { ModelImporterService } from 'app/services/model-importer.service';
+import { PropService } from 'app/services/prop-service';
+import { AssetDatabase } from 'app/services/asset-database';
+
+/**
+ * Prop point tool
+ *
+ * Steps
+ * 1. Select a prop (fbx, gltf) from library browser
+ * 2. SHIFT + LEFT-CLICK to drop it in the scene
+ *
+ * Requires an instance of prop to be able to drop them in the scene
+ *
+ *
+ */
+export class PropPointTool extends BaseTool {
+
+ public name: string = 'PropPointTool';
+
+ public shapeEditor: PointEditor;
+
+ private cpSubscriptions: Subscription[] = [];
+
+ private controlPointAddedSubscriber: Subscription;
+ private controlPointSelectedSubscriber: Subscription;
+ private controlPointUnselectedSubscriber: Subscription;
+
+ constructor ( private modelImporter?: ModelImporterService ) {
+
+ super();
+
+ }
+
+ get prop (): PropInstance {
+
+ const prop = PropService.getProp();
+
+ if ( prop ) {
+
+ return new PropInstance( prop.guid, AssetDatabase.getInstance( prop.guid ) );
+
+ }
+
+ }
+
+ init () {
+
+ super.init();
+
+ this.shapeEditor = new PointEditor( 100 );
+
+ }
+
+ enable () {
+
+ super.enable();
+
+ this.openDrive.props.forEach( ( prop: PropInstance ) => {
+
+ const cp = this.shapeEditor.addControlPoint( prop.object.position );
+
+ this.sync( cp, prop );
+
+ } );
+
+ this.controlPointAddedSubscriber = this.shapeEditor.controlPointAdded.subscribe(
+
+ point => this.onControlPointAdded( point )
+
+ );
+
+ this.controlPointSelectedSubscriber = this.shapeEditor.controlPointSelected.subscribe(
+
+ point => this.onControlPointSelected( point )
+
+ );
+
+ this.controlPointUnselectedSubscriber = this.shapeEditor.controlPointUnselected.subscribe(
+
+ point => this.onControlPointUnselected( point )
+
+ );
+
+ }
+
+ disable (): void {
+
+ super.disable();
+
+ this.controlPointAddedSubscriber.unsubscribe();
+ this.controlPointSelectedSubscriber.unsubscribe();
+ this.controlPointUnselectedSubscriber.unsubscribe();
+
+ this.shapeEditor.destroy();
+
+ this.unsubscribeFromControlPoints();
+ }
+
+ private onControlPointSelected ( point: AnyControlPoint ) {
+
+ InspectorFactoryService.setByType( InspectorType.prop_instance_inspector, point.mainObject );
+
+ }
+
+ private onControlPointUnselected ( point: AnyControlPoint ) {
+
+ AppInspector.clear();
+
+ }
+
+ private onControlPointAdded ( point: AnyControlPoint ) {
+
+ if ( !this.prop ) SnackBar.error( "Select a prop from the project browser" );
+
+ if ( !this.prop ) this.shapeEditor.removeControlPoint( point );
+
+ if ( !this.prop ) return;
+
+ const prop = new PropInstance( this.prop.guid, this.prop.object.clone() );
+
+ prop.object.position.set( point.position.x, point.position.y, point.position.z );
+
+ this.sync( point, prop );
+
+ this.openDrive.gameObject.add( prop.object );
+
+ TvMapQueries.openDrive.props.push( prop );
+
+ }
+
+ private sync ( point: AnyControlPoint, prop: PropInstance ): void {
+
+ const subscription = point.updated.subscribe( e => {
+
+ prop.object.position.copy( e.position );
+
+ } );
+
+ point.mainObject = prop;
+
+ this.cpSubscriptions.push( subscription );
+ }
+
+ private unsubscribeFromControlPoints () {
+
+ this.cpSubscriptions.forEach( sub => {
+
+ sub.unsubscribe();
+
+ } );
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/app/core/tools/prop-polygon-tool.ts b/src/app/core/tools/prop-polygon-tool.ts
new file mode 100644
index 00000000..30a1e643
--- /dev/null
+++ b/src/app/core/tools/prop-polygon-tool.ts
@@ -0,0 +1,225 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseTool } from './base-tool';
+import { PointEditor } from '../editors/point-editor';
+import { AnyControlPoint } from 'app/modules/three-js/objects/control-point';
+import { Subscription } from 'rxjs';
+import { PointerEventData } from 'app/events/pointer-event-data';
+import { KeyboardInput } from '../input';
+import { PropPolygon } from 'app/modules/tv-map/models/prop-polygons';
+import { PropService } from 'app/services/prop-service';
+import { SnackBar } from 'app/services/snack-bar.service';
+import { AppInspector } from '../inspector';
+import { PropPolygonInspectorData, PropPolygonInspectorComponent } from 'app/views/inspectors/prop-polygon-inspector/prop-polygon-inspector.component';
+
+export class PropPolygonTool extends BaseTool {
+
+ public name: string = 'PropPolygonTool';
+
+ public shapeEditor: PointEditor;
+
+ private cpSubscriptions: Subscription[] = [];
+
+ private cpAddedSub: Subscription;
+ private cpMovedSub: Subscription;
+ private cpUpdatedSub: Subscription;
+ private cpSelectedSub: Subscription;
+ private cpUnselectedSub: Subscription;
+ private keyDownSub: Subscription;
+
+ private polygon: PropPolygon;
+ private point: AnyControlPoint;
+
+ constructor () {
+
+ super();
+
+ }
+
+ public init () {
+
+ super.init();
+
+ this.shapeEditor = new PointEditor( 100 );
+ }
+
+ public enable () {
+
+ super.enable();
+
+ this.openDrive.propPolygons.forEach( polygon => {
+
+ polygon.showControlPoints();
+
+ polygon.showCurve();
+
+ polygon.spline.controlPoints.forEach( cp => {
+
+ cp.mainObject = polygon;
+
+ this.shapeEditor.controlPoints.push( cp )
+
+ } )
+ } );
+
+ this.keyDownSub = KeyboardInput.keyDown
+ .subscribe( e => this.onDeletePressed( e ) );
+
+ this.cpAddedSub = this.shapeEditor.controlPointAdded
+ .subscribe( ( cp: AnyControlPoint ) => this.onControlPointAdded( cp ) );
+
+ this.cpMovedSub = this.shapeEditor.controlPointMoved
+ .subscribe( () => this.onControlPointMoved() );
+
+ this.cpUpdatedSub = this.shapeEditor.controlPointUpdated
+ .subscribe( () => this.onControlPointUpdated() );
+
+ this.cpSelectedSub = this.shapeEditor.controlPointSelected
+ .subscribe( ( cp: AnyControlPoint ) => this.onControlPointSelected( cp ) );
+
+ this.cpUnselectedSub = this.shapeEditor.controlPointUnselected
+ .subscribe( () => this.onControlPointUnselected() );
+
+ }
+
+ public disable (): void {
+
+ super.disable();
+
+ this.openDrive.propPolygons.forEach( polygon => {
+
+ polygon.hideCurve();
+ polygon.hideControlPoints();
+
+ } );
+
+ this.keyDownSub.unsubscribe();
+ this.cpAddedSub.unsubscribe();
+ this.cpMovedSub.unsubscribe();
+ this.cpUpdatedSub.unsubscribe();
+ this.cpSelectedSub.unsubscribe();
+ this.cpUnselectedSub.unsubscribe();
+
+ this.shapeEditor.destroy();
+ }
+
+ public onPointerClicked ( e: PointerEventData ) {
+
+ for ( let i = 0; i < e.intersections.length; i++ ) {
+
+ const intersection = e.intersections[ i ];
+
+ if ( intersection.object && intersection.object[ 'tag' ] === PropPolygon.tag ) {
+
+ this.polygon = intersection.object.userData.polygon;
+
+ this.polygon.showControlPoints();
+
+ this.showInspector( this.polygon );
+
+ break;
+ }
+ }
+ }
+
+ private onControlPointSelected ( cp: AnyControlPoint ) {
+
+ this.point = cp;
+
+ this.polygon = cp.mainObject;
+
+ this.showInspector( this.polygon, this.point );
+ }
+
+ private onControlPointUnselected () {
+
+ this.polygon = null;
+
+ this.point = null;
+
+ }
+
+ private onControlPointAdded ( cp: AnyControlPoint ) {
+
+ const prop = PropService.getProp();
+
+ if ( !prop ) SnackBar.error( "Select a prop from the project browser" );
+
+ if ( !prop ) this.shapeEditor.removeControlPoint( cp );
+
+ if ( !prop ) return;
+
+ if ( !this.polygon ) {
+
+ this.polygon = new PropPolygon( prop.guid );
+
+ this.openDrive.propPolygons.push( this.polygon );
+
+ }
+
+ this.point = cp;
+
+ cp.mainObject = this.polygon;
+
+ this.polygon.spline.addControlPoint( cp );
+
+ this.polygon.update();
+
+ this.showInspector( this.polygon, this.point );
+ }
+
+ private onControlPointUpdated () {
+
+ this.polygon.update();
+
+ this.showInspector( this.polygon, this.point );
+ }
+
+ private onControlPointMoved () {
+
+ this.polygon.spline.update()
+
+ this.showInspector( this.polygon, this.point );
+
+ }
+
+ private onDeletePressed ( e: KeyboardEvent ) {
+
+ if ( e.key === "Delete" && this.polygon ) {
+
+ this.polygon.delete();
+
+ const index = this.openDrive.surfaces.findIndex( s => s.id === this.polygon.id );
+
+ if ( index > -1 ) {
+
+ this.openDrive.surfaces.splice( index, 1 );
+
+ }
+
+ this.polygon = null;
+
+ delete this.polygon;
+ }
+
+ }
+
+ private showInspector ( polygon?: PropPolygon, point?: AnyControlPoint ) {
+
+ if ( polygon == null && point == null ) {
+
+ AppInspector.clear();
+
+ } else {
+
+ const data = new PropPolygonInspectorData( point, polygon );
+
+ AppInspector.setInspector( PropPolygonInspectorComponent, data );
+ }
+ }
+
+
+
+}
diff --git a/src/app/core/tools/road-sign-tool.ts b/src/app/core/tools/road-sign-tool.ts
new file mode 100644
index 00000000..bd38f594
--- /dev/null
+++ b/src/app/core/tools/road-sign-tool.ts
@@ -0,0 +1,246 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseTool } from './base-tool';
+import { PointerEventData } from 'app/events/pointer-event-data';
+import { Object3D, Points } from 'three';
+import { TvObjectType } from '../../modules/tv-map/interfaces/i-tv-object';
+import { TvRoadSignal } from '../../modules/tv-map/models/tv-road-signal.model';
+import { OdSignalInspectorComponent } from '../../views/inspectors/signal-inspector/signal-inspector.component';
+import { AbstractShapeEditor } from '../editors/abstract-shape-editor';
+import { PointEditor } from '../editors/point-editor';
+import { OdSignalBuilder } from '../../modules/tv-map/builders/od-signal-builder';
+import { TvPosTheta } from '../../modules/tv-map/models/tv-pos-theta';
+import { SnackBar } from '../../services/snack-bar.service';
+import { TvSignService } from '../../modules/tv-map/services/tv-sign.service';
+import { CommandHistory } from '../../services/command-history';
+import { AddSignalCommand } from '../commands/add-signal-command';
+import { TvDynamicTypes, TvOrientation } from '../../modules/tv-map/models/tv-common';
+import { SceneService } from '../services/scene.service';
+import { Subscription } from 'rxjs';
+import { AnyControlPoint } from '../../modules/three-js/objects/control-point';
+import { TvMapQueries } from '../../modules/tv-map/queries/tv-map-queries';
+
+export class RoadSignTool extends BaseTool {
+
+ name: string = 'RoadSignTool';
+
+ init () {
+
+ super.init();
+
+ this.showControlPoints();
+
+ }
+
+ disable (): void {
+
+ super.disable();
+
+ this.controlPointAddedSubscriber.unsubscribe();
+ this.controlPointMovedSubscriber.unsubscribe();
+
+ this.shapeEditor.destroy();
+
+ this.hideControlPoints();
+ }
+
+ private signFactory = new OdSignalBuilder();
+ private controlPointAddedSubscriber: Subscription;
+ private hasSignal = false;
+ private selectedSignal: TvRoadSignal;
+ private shapeEditor: AbstractShapeEditor;
+ private controlPointMovedSubscriber: Subscription;
+
+ constructor ( private signService: TvSignService ) {
+
+ super();
+
+ this.shapeEditor = new PointEditor();
+
+ this.controlPointAddedSubscriber = this.shapeEditor.controlPointAdded.subscribe( e => this.onControlPointAdded( e ) );
+ this.controlPointMovedSubscriber = this.shapeEditor.controlPointMoved.subscribe( e => this.onControlPointMoved( e ) );
+ }
+
+ get sign () {
+ return this.signService.currentSign;
+ }
+
+ onPointerDown ( e: PointerEventData ) {
+
+ super.onPointerDown( e );
+
+ this.hasSignal = false;
+
+ for ( const i of e.intersections ) {
+
+ if ( i.object[ 'OpenDriveType' ] != null && i.object[ 'OpenDriveType' ] == TvObjectType.SIGNAL ) {
+
+ this.hasSignal = true;
+
+ this.inspectSignal( i.object );
+
+ break;
+
+ }
+ }
+
+ if ( !this.hasSignal ) {
+
+ this.clearInspector();
+
+ }
+ }
+
+ private inspectSignal ( object: Object3D ) {
+
+ this.selectedSignal = ( object.userData.data as TvRoadSignal );
+
+ this.setInspector( OdSignalInspectorComponent, this.selectedSignal );
+ }
+
+ private onControlPointAdded ( point: AnyControlPoint ) {
+
+ const pose = new TvPosTheta();
+
+ pose.x = point.position.x;
+ pose.y = point.position.y;
+
+ if ( this.sign ) {
+
+ const road = TvMapQueries.getRoadByCoords( pose.x, pose.y, pose );
+
+ if ( road == null ) throw new Error( 'Could not find any road' );
+
+ const id = road.getRoadSignalCount() + 1;
+
+ const signal = new TvRoadSignal( pose.s, pose.t, id, '', TvDynamicTypes.NO, TvOrientation.MINUS );
+
+ signal.roadId = road.id;
+ signal.height = 4;
+ signal.controlPoint = point;
+ signal.signShape = this.sign.shape;
+ signal.name = this.sign.name;
+ signal.text = this.sign.name;
+
+ signal.addUserData( 'asset_name', this.sign.name );
+ signal.addUserData( 'sign_shape', this.sign.shape as string );
+
+ CommandHistory.execute( new AddSignalCommand( signal ) );
+
+ } else {
+
+ this.shapeEditor.removeControlPoint( point );
+
+ SnackBar.show( 'Please select a sign first' );
+
+ }
+
+ }
+
+
+ // OLD CODE JUST FOR REFERENCE
+ // const ray = new Ray();
+ //
+ // ray.origin.copy( e.position );
+ //
+ // ray.direction.copy( pose.toDirectionVector() );
+ //
+ // // console.log( pose, ray );
+ //
+ // SceneService.add( new ArrowHelper( ray.direction, ray.origin, 300, 0xff0000 ) );
+ //
+ // // e.add( signal.GameObject );
+ //
+ // // const p1 = pose.toVector3();
+ // // const p2 = p1.clone().add( pose.toDirectionVector().multiplyScalar( 10 ) );
+ //
+ // // console.log( p1, p2 );
+ //
+ // // console.log( signal.GameObject.position.dot( ray.direction ) );
+ // // console.log( this.isLeft( p1, p2, signal.GameObject.position ) );
+ //
+ // // const tmp1 = new OdPosTheta();
+ // // const tmp2 = new OdPosTheta();
+ // //
+ // // road.getGeometryCoords( pose.s, tmp1 );
+ // // road.getGeometryCoords( pose.s + Maths.Epsilon, tmp2 );
+ // //
+ // // this.directionOfPoint( tmp1.toVector3(), tmp2.toVector3(), e.position );
+ //
+ // const a = pose.toVector3();
+ // const b = e.position;
+ //
+ // var dot = a.x * b.x + a.y * b.y;
+ // if ( dot > 0 )
+ // console.log( '<90 degrees' );
+ // else if ( dot < 0 )
+ // console.log( '>90 degrees' );
+ // else
+ // console.log( '90 degrees' );
+
+
+ private showControlPoints () {
+
+ this.forEachControlPoint( controlPoint => {
+
+ // add control points to shape editor to sync and avoid errors
+ this.shapeEditor.controlPoints.push( controlPoint );
+
+ controlPoint.visible = true;
+
+ SceneService.add( controlPoint );
+
+ } );
+
+ }
+
+ private hideControlPoints () {
+
+ this.forEachControlPoint( controlPoint => {
+
+ controlPoint.visible = false;
+
+ SceneService.remove( controlPoint );
+
+ } );
+
+ }
+
+ private onControlPointMoved ( point: Points ) {
+
+ for ( const road of this.openDrive.roads ) {
+
+ for ( const map of road[ 1 ].signals ) {
+
+ const signal = map[ 1 ];
+
+ if ( signal.controlPoint && signal.controlPoint.id == point.id ) {
+
+ signal.gameObject.position.copy( point.position );
+
+ break;
+
+ }
+
+ }
+
+ }
+
+ }
+
+ private forEachControlPoint ( callback: ( controlPoint: AnyControlPoint ) => void ) {
+
+ this.openDrive.roads.forEach( road => {
+
+ road.signals.forEach( signal => {
+
+ if ( signal.controlPoint ) callback( signal.controlPoint );
+
+ } );
+
+ } );
+
+ }
+}
diff --git a/src/app/core/tools/road-tool.spec.ts b/src/app/core/tools/road-tool.spec.ts
new file mode 100644
index 00000000..c5252429
--- /dev/null
+++ b/src/app/core/tools/road-tool.spec.ts
@@ -0,0 +1,199 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvMap } from 'app/modules/tv-map/models/tv-map.model';
+import { NodeFactoryService } from '../factories/node-factory.service';
+import { TvRoad } from 'app/modules/tv-map/models/tv-road.model';
+import { Vector3 } from 'three';
+import { RoadTool } from './road-tool';
+import { TvMapSourceFile } from 'app/modules/tv-map/services/tv-map-source-file';
+import { RoadFactory } from '../factories/road-factory.service';
+import { RoadControlPoint } from 'app/modules/three-js/objects/road-control-point';
+import { TvContactPoint, TvRoadType } from 'app/modules/tv-map/models/tv-common';
+
+describe( 'RoadTool', () => {
+
+ let openDrive: TvMap;
+
+ let roadTool: RoadTool;
+
+ beforeEach( () => {
+
+ openDrive = TvMapSourceFile.openDrive = new TvMap();
+
+ roadTool = new RoadTool();
+
+ } )
+
+ it( 'should maintain hdg for connected roads', () => {
+
+ // c is th joining road
+ let roadA: TvRoad, roadB: TvRoad, roadC: TvRoad;
+
+ let roadAHdg, roadBHdg, roadCHdg;
+
+ [ roadA, roadC, roadB ] = createRoads();
+
+ roadAHdg = roadA.spline.controlPoints.map( ( p: RoadControlPoint ) => p.hdg );
+ roadBHdg = roadB.spline.controlPoints.map( ( p: RoadControlPoint ) => p.hdg );
+ roadCHdg = roadC.spline.controlPoints.map( ( p: RoadControlPoint ) => p.hdg );
+
+ roadA.spline.getLastPoint().copyPosition( new Vector3( 120, 0, 0 ) );
+
+ roadA.updateGeometryFromSpline();
+
+ roadAHdg = roadA.spline.controlPoints.map( ( p: RoadControlPoint ) => p.hdg );
+ roadBHdg = roadB.spline.controlPoints.map( ( p: RoadControlPoint ) => p.hdg );
+ roadCHdg = roadC.spline.controlPoints.map( ( p: RoadControlPoint ) => p.hdg );
+
+ // nothing happening in this test for now
+ expect( true ).toBe( true );
+
+ } );
+
+ it( 'should join 2 road nodes correctly', () => {
+
+ // start |---------------->
+
+ // start |---------------->
+
+ let roadA, roadB, joiningRoad;
+
+ [ roadA, roadB, joiningRoad ] = createRoads();
+
+ expect( !joiningRoad ).toBe( false );
+
+ expect( joiningRoad.spline.controlPoints.length ).toBe( 6 );
+
+ } );
+
+ function createRoads () {
+
+ const roadA = openDrive.addDefaultRoad();
+
+ roadA.spline.addControlPoint( new RoadControlPoint( roadA, new Vector3( 0, 0, 0 ) ) );
+ roadA.spline.addControlPoint( new RoadControlPoint( roadA, new Vector3( 50, 5, 0 ) ) );
+ roadA.spline.addControlPoint( new RoadControlPoint( roadA, new Vector3( 100, 0, 0 ) ) );
+
+ roadA.updateGeometryFromSpline();
+
+ NodeFactoryService.updateRoadNodes( roadA );
+
+ const roadB = openDrive.addDefaultRoad();
+
+ roadB.spline.addControlPoint( new RoadControlPoint( roadB, new Vector3( 0, 50, 0 ) ) );
+ roadB.spline.addControlPoint( new RoadControlPoint( roadB, new Vector3( 55, 50, 0 ) ) );
+ roadB.spline.addControlPoint( new RoadControlPoint( roadB, new Vector3( 100, 50, 0 ) ) );
+
+ roadB.updateGeometryFromSpline();
+
+ NodeFactoryService.updateRoadNodes( roadB );
+
+ const joiningRoad = RoadFactory.joinRoadNodes( roadA, roadA.endNode, roadB, roadB.endNode );
+
+ return [ roadA, roadB, joiningRoad ];
+ }
+
+ function suc_pre () {
+ const road1 = this.openDrive.addDefaultRoadWithType( TvRoadType.TOWN );
+
+ road1.addControlPointAt( new Vector3( 0, 0, 0 ) );
+ road1.addControlPointAt( new Vector3( 0, 50, 0 ) );
+ road1.addControlPointAt( new Vector3( 80, 50, 0 ) );
+
+ road1.updateGeometryFromSpline();
+
+ RoadFactory.rebuildRoad( road1 );
+
+ const road2 = this.openDrive.addDefaultRoadWithType( TvRoadType.TOWN );
+
+ road2.addControlPointAt( new Vector3( 80, 50, 0 ) );
+ road2.addControlPointAt( new Vector3( 160, 50, 0 ) );
+ road2.addControlPointAt( new Vector3( 160, 0, 0 ) );
+
+ road2.updateGeometryFromSpline();
+
+ RoadFactory.rebuildRoad( road2 );
+
+ road1.setSuccessor( "road", road2.id, TvContactPoint.START );
+ road2.setPredecessor( "road", road1.id, TvContactPoint.END );
+ }
+
+ function suc_suc () {
+ const road1 = this.openDrive.addDefaultRoadWithType( TvRoadType.TOWN );
+
+ road1.addControlPointAt( new Vector3( 0, 0, 0 ) );
+ road1.addControlPointAt( new Vector3( 0, 50, 0 ) );
+ road1.addControlPointAt( new Vector3( 80, 50, 0 ) );
+
+ road1.updateGeometryFromSpline();
+
+ RoadFactory.rebuildRoad( road1 );
+
+ const road2 = this.openDrive.addDefaultRoadWithType( TvRoadType.TOWN );
+
+ road2.addControlPointAt( new Vector3( 160, 0, 0 ) );
+ road2.addControlPointAt( new Vector3( 160, 50, 0 ) );
+ road2.addControlPointAt( new Vector3( 80, 50, 0 ) );
+
+ road2.updateGeometryFromSpline();
+
+ RoadFactory.rebuildRoad( road2 );
+
+ road1.setSuccessor( "road", road2.id, TvContactPoint.END );
+ road2.setSuccessor( "road", road1.id, TvContactPoint.END );
+ }
+
+ function pre_pre () {
+ const road1 = this.openDrive.addDefaultRoadWithType( TvRoadType.TOWN );
+
+ road1.addControlPointAt( new Vector3( 80, 50, 0 ) );
+ road1.addControlPointAt( new Vector3( 0, 50, 0 ) );
+ road1.addControlPointAt( new Vector3( 0, 0, 0 ) );
+
+ road1.updateGeometryFromSpline();
+
+ RoadFactory.rebuildRoad( road1 );
+
+ const road2 = this.openDrive.addDefaultRoadWithType( TvRoadType.TOWN );
+
+ road2.addControlPointAt( new Vector3( 80, 50, 0 ) );
+ road2.addControlPointAt( new Vector3( 160, 50, 0 ) );
+ road2.addControlPointAt( new Vector3( 160, 0, 0 ) );
+
+ road2.updateGeometryFromSpline();
+
+ RoadFactory.rebuildRoad( road2 );
+
+ road1.setPredecessor( "road", road2.id, TvContactPoint.START );
+ road2.setPredecessor( "road", road1.id, TvContactPoint.START );
+ }
+
+ function pre_suc () {
+ const road1 = this.openDrive.addDefaultRoadWithType( TvRoadType.TOWN );
+
+ road1.addControlPointAt( new Vector3( 80, 50, 0 ) );
+ road1.addControlPointAt( new Vector3( 0, 50, 0 ) );
+ road1.addControlPointAt( new Vector3( 0, 0, 0 ) );
+
+ road1.updateGeometryFromSpline();
+
+ RoadFactory.rebuildRoad( road1 );
+
+ const road2 = this.openDrive.addDefaultRoadWithType( TvRoadType.TOWN );
+
+ road2.addControlPointAt( new Vector3( 160, 0, 0 ) );
+ road2.addControlPointAt( new Vector3( 160, 50, 0 ) );
+ road2.addControlPointAt( new Vector3( 80, 50, 0 ) );
+
+ road2.updateGeometryFromSpline();
+
+ RoadFactory.rebuildRoad( road2 );
+
+ road1.setPredecessor( "road", road2.id, TvContactPoint.END );
+ road2.setSuccessor( "road", road1.id, TvContactPoint.START );
+ }
+
+
+} );
diff --git a/src/app/core/tools/road-tool.ts b/src/app/core/tools/road-tool.ts
new file mode 100644
index 00000000..796941c4
--- /dev/null
+++ b/src/app/core/tools/road-tool.ts
@@ -0,0 +1,616 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseTool } from './base-tool';
+import { MouseButton, PointerEventData } from 'app/events/pointer-event-data';
+import { ObjectTypes, TvContactPoint } from 'app/modules/tv-map/models/tv-common';
+import { TvLane } from 'app/modules/tv-map/models/tv-lane';
+import { TvRoad } from 'app/modules/tv-map/models/tv-road.model';
+import { RoadInspector } from 'app/views/inspectors/road-inspector/road-inspector.component';
+import { Vector3 } from 'three';
+import { KeyboardInput } from '../input';
+import { PickingHelper } from '../services/picking-helper.service';
+import { RoadNode } from 'app/modules/three-js/objects/road-node';
+import { TvMapBuilder } from 'app/modules/tv-map/builders/od-builder.service';
+import { NodeFactoryService } from '../factories/node-factory.service';
+import { RoadFactory } from '../factories/road-factory.service';
+import { CommandHistory } from 'app/services/command-history';
+import { AddRoadPointCommand } from 'app/core/commands/add-road-point-command';
+import { SetValueCommand } from 'app/modules/three-js/commands/set-value-command';
+import { MultiCmdsCommand } from 'app/core/commands/multi-cmds-command';
+import { AddRoadCommand } from 'app/core/commands/add-road-command';
+import { SetInspectorCommand } from '../commands/set-inspector-command';
+import { UpdateRoadPointCommand } from 'app/core/commands/update-road-point-command';
+import { JoinRoadNodeCommand } from '../commands/join-road-node-command';
+import { RoadControlPoint } from 'app/modules/three-js/objects/road-control-point';
+
+export class RoadTool extends BaseTool {
+
+ public name: string = 'RoadTool';
+
+ private road: TvRoad;
+ private controlPoint: RoadControlPoint;
+ private node: RoadNode;
+
+ private roadChanged: boolean = false;
+ private pointerDown: boolean = false;
+ private pointerDownAt: Vector3;
+
+ constructor () {
+
+ super();
+
+ }
+
+ init () {
+
+
+ }
+
+ enable () {
+
+ super.enable();
+
+ this.openDrive.roads.forEach( road => {
+
+ if ( !road.isJunction ) {
+
+ NodeFactoryService.updateRoadNodes( road );
+
+ road.showNodes();
+
+ }
+
+ } );
+
+ }
+
+ disable () {
+
+ super.disable();
+
+ this.openDrive.roads.forEach( road => road.hideNodes() );
+
+ if ( this.road ) this.hideRoad( this.road );
+
+ if ( this.controlPoint ) this.controlPoint.unselect();
+
+ if ( this.node ) this.node.unselected();
+ }
+
+ onPointerDown ( e: PointerEventData ) {
+
+ if ( e.button == MouseButton.RIGHT || e.button == MouseButton.MIDDLE ) return;
+
+ this.pointerDown = true;
+ this.pointerDownAt = e.point.clone();
+
+ const shiftKeyDown = KeyboardInput.isShiftKeyDown;
+
+ let hasInteracted = false;
+
+ if ( !hasInteracted ) hasInteracted = this.checkControlPointInteraction( e );
+
+ if ( !hasInteracted ) hasInteracted = this.checkRoadNodeInteraction( e );
+
+ if ( !hasInteracted && !shiftKeyDown ) hasInteracted = this.checkLaneObjectInteraction( e );
+
+ if ( !hasInteracted ) {
+
+ if ( e.button === MouseButton.LEFT && shiftKeyDown && e.point != null ) {
+
+ this.addControlPoint( e.point );
+
+ } else {
+
+ if ( this.road || this.controlPoint || this.node ) {
+
+ const setInspectorCommand = new SetInspectorCommand( null, null );
+
+ const setRoadCommand = new SetValueCommand( this, 'road', null );
+
+ const setPointCommand = new SetValueCommand( this, 'controlPoint', null );
+
+ const setNodeCommand = new SetValueCommand( this, 'node', null );
+
+ CommandHistory.execute( new MultiCmdsCommand( [
+ setInspectorCommand,
+ setRoadCommand,
+ setPointCommand,
+ setNodeCommand
+ ] ) );
+
+ }
+
+ }
+
+ // // no interaction with line, lane, points etc
+ // if ( this.road && !this.controlPoint && !shiftKeyDown ) {
+
+ // AppInspector.clear();
+
+ // this.hideRoad( this.road );
+
+ // this.road = null;
+ // }
+
+ // if ( this.controlPoint && !shiftKeyDown ) {
+
+ // this.controlPoint.unselect();
+
+ // this.controlPoint = null;
+
+ // }
+
+ }
+
+ // AppInspector.setInspector( RoadInspector, {
+ // road: this.road,
+ // controlPoint: this.controlPoint
+ // } );
+ }
+
+ onPointerClicked ( e: PointerEventData ) {
+
+ // no need to do chck creation logic here, as it caused bugs, moved it in onPointerDown
+
+ // if ( e.button === MouseButton.LEFT && KeyboardInput.isShiftKeyDown && e.point != null ) {
+
+ // this.checkRoadNodeInteraction( e );
+
+ // this.addControlPoint( e.point );
+
+ // }
+ }
+
+ onPointerUp ( e: PointerEventData ) {
+
+ if ( this.roadChanged && this.road && this.road.spline.controlPoints.length >= 2 ) {
+
+ const updateRoadPointCommand = new UpdateRoadPointCommand(
+ this.road, this.controlPoint, this.controlPoint.position, this.pointerDownAt
+ );
+
+ CommandHistory.execute( updateRoadPointCommand );
+
+ }
+
+ this.pointerDown = false;
+
+ this.pointerDownAt = null;
+
+ this.roadChanged = false;
+ }
+
+ onPointerMoved ( e: PointerEventData ) {
+
+ if ( this.pointerDown && this.controlPoint && this.controlPoint.isSelected && this.road ) {
+
+ this.controlPoint.copyPosition( e.point );
+
+ this.road.spline.update();
+
+ this.roadChanged = true;
+
+ this.updateSuccessor( this.road, this.controlPoint );
+
+ this.updatePredecessor( this.road, this.controlPoint );
+ }
+
+ }
+
+ private updateSuccessor ( road: TvRoad, currentPoint: RoadControlPoint ) {
+
+ const P1 = road.spline.getSecondLastPoint() as RoadControlPoint;
+ const P2 = road.spline.getLastPoint() as RoadControlPoint;
+
+ if ( road.successor && road.successor.elementType !== "junction"
+ && ( P1.id === currentPoint.id || P2.id === currentPoint.id ) ) {
+
+ const successor = this.openDrive.getRoadById( road.successor.elementId );
+
+ if ( !successor ) return;
+
+ successor.spline.show();
+
+ let P3: RoadControlPoint;
+
+ let P4: RoadControlPoint;
+
+ let newP4: RoadControlPoint;
+
+ let distance: number;
+
+ if ( road.successor.contactPoint === TvContactPoint.START ) {
+
+ P3 = successor.spline.controlPoints[ 0 ] as RoadControlPoint;
+
+ P4 = successor.spline.controlPoints[ 1 ] as RoadControlPoint;
+
+ distance = P3.position.distanceTo( P4.position );
+
+ P3.copyPosition( P2.position );
+
+ P2.hdg = P1.hdg;
+
+ P3.hdg = P2.hdg + Math.PI;
+
+ newP4 = P2.moveForward( distance );
+
+ P4.copyPosition( newP4.position );
+
+ successor.spline.update();
+
+ } else {
+
+ P3 = successor.spline.getLastPoint() as RoadControlPoint;
+
+ P4 = successor.spline.getSecondLastPoint() as RoadControlPoint;
+
+ distance = P3.position.distanceTo( P4.position );
+
+ P3.copyPosition( P2.position );
+
+ P2.hdg = P1.hdg;
+
+ P3.hdg = P2.hdg + Math.PI;
+
+ newP4 = P2.moveForward( distance );
+
+ P4.copyPosition( newP4.position );
+
+ successor.spline.update();
+ }
+ }
+ }
+
+ private updatePredecessor ( road: TvRoad, currentPoint: RoadControlPoint ) {
+
+ const P1 = road.spline.controlPoints[ 1 ] as RoadControlPoint;
+ const P2 = road.spline.controlPoints[ 0 ] as RoadControlPoint;
+
+ if ( road.predecessor && road.predecessor.elementType !== "junction"
+ && ( P1.id === currentPoint.id || P2.id === currentPoint.id ) ) {
+
+ const predecessor = this.openDrive.getRoadById( road.predecessor.elementId );
+
+ if ( !predecessor ) return;
+
+ predecessor.spline.show();
+
+ let P3: RoadControlPoint;
+
+ let P4: RoadControlPoint;
+
+ let newP4: RoadControlPoint;
+
+ let distance: number;
+
+ if ( road.predecessor.contactPoint === TvContactPoint.START ) {
+
+ P3 = predecessor.spline.controlPoints[ 0 ] as RoadControlPoint;
+
+ P4 = predecessor.spline.controlPoints[ 1 ] as RoadControlPoint;
+
+ distance = P3.position.distanceTo( P4.position );
+
+ P3.copyPosition( P2.position );
+
+ P3.hdg = P4.hdg = P2.hdg + Math.PI;
+
+ newP4 = P3.moveForward( distance );
+
+ P4.copyPosition( newP4.position );
+
+ predecessor.spline.update();
+
+ } else {
+
+ P3 = predecessor.spline.getLastPoint() as RoadControlPoint;
+
+ P4 = predecessor.spline.getSecondLastPoint() as RoadControlPoint;
+
+ distance = P3.position.distanceTo( P4.position );
+
+ P3.copyPosition( P2.position );
+
+ P3.hdg = P4.hdg = P2.hdg + Math.PI;
+
+ newP4 = P3.moveForward( distance );
+
+ P4.copyPosition( newP4.position );
+
+ predecessor.spline.update();
+
+ }
+ }
+ }
+
+ private checkControlPointInteraction ( e: PointerEventData ): boolean {
+
+ if ( !this.road || !this.road.spline ) return;
+
+ if ( !e.point ) return;
+
+ // // first chceck for control point interactions
+ // // doing in 2 loop to prioritise control points
+ // let controlPoint = PickingHelper.checkControlPointInteraction( e, ControlPoint.roadTag, 1 );
+
+ const maxDistance = Math.max( 0.5, e.approxCameraDistance * 0.01 );
+
+ const controlPoints = [];
+
+ this.road.spline.controlPoints.forEach( ( cp: RoadControlPoint ) => {
+
+ controlPoints.push( cp );
+
+ if ( cp.frontTangent ) controlPoints.push( cp.frontTangent );
+
+ if ( cp.backTangent ) controlPoints.push( cp.backTangent );
+
+ } );
+
+ const controlPoint = PickingHelper.findNearest( e.point, controlPoints, maxDistance );
+
+ if ( controlPoint ) {
+
+ CommandHistory.executeAll( [
+
+ new SetInspectorCommand( RoadInspector, { road: this.road, controlPoint } ),
+
+ new SetValueCommand( this, 'controlPoint', controlPoint ),
+
+ new SetValueCommand( this, 'node', null )
+
+ ] );
+
+ } else if ( this.controlPoint ) {
+
+ CommandHistory.executeAll( [
+
+ new SetValueCommand( this, 'controlPoint', null ),
+
+ new SetInspectorCommand( null, null ),
+
+ ] );
+
+ }
+
+ return controlPoint != null;
+ }
+
+ private checkLaneObjectInteraction ( e: PointerEventData ): boolean {
+
+ let hasInteracted = false;
+
+ for ( let i = 0; i < e.intersections.length; i++ ) {
+
+ const intersection = e.intersections[ i ];
+
+ // tslint:disable-next-line: no-string-literal
+ if ( intersection.object && intersection.object[ 'tag' ] === ObjectTypes.LANE ) {
+
+ hasInteracted = true;
+
+ if ( intersection.object.userData.lane ) {
+
+ const lane = intersection.object.userData.lane as TvLane;
+
+ const road = this.openDrive.getRoadById( lane.roadId );
+
+ if ( road.isJunction ) continue;
+
+ // check if old or a new lane is selected
+ if ( !this.road || this.road.id !== road.id ) {
+
+ const commands = [];
+
+ commands.push( new SetInspectorCommand( RoadInspector, { road } ) );
+
+ commands.push( new SetValueCommand( this, 'road', road ) );
+
+ commands.push( new SetValueCommand( this, 'controlPoint', null ) );
+
+ commands.push( new SetValueCommand( this, 'node', null ) );
+
+ CommandHistory.execute( new MultiCmdsCommand( commands ) );
+ }
+ }
+
+ break;
+ }
+ }
+
+ return hasInteracted;
+ }
+
+ private checkRoadNodeInteraction ( e: PointerEventData ): boolean {
+
+ let hasInteracted = false;
+
+ // for now ignore the shift key
+ const shiftKeyDown = true; //KeyboardInput.isShiftKeyDown;
+
+ for ( let i = 0; i < e.intersections.length; i++ ) {
+
+ const intersection = e.intersections[ i ];
+
+ if ( intersection.object && intersection.object[ 'tag' ] === RoadNode.lineTag ) {
+
+ hasInteracted = true;
+
+ const node = intersection.object.parent as RoadNode;
+
+ if ( shiftKeyDown && this.node && this.node.roadId !== node.roadId ) {
+
+ // node with node then
+
+ // two roads need to joined
+ // we take both nodes and use them as start and end points
+ // for a new road
+ // new road will have 4 more points, so total 6 points
+
+ this.joinNodeWithNode( this.node, node );
+
+ } else if ( shiftKeyDown && this.controlPoint ) {
+
+ // control point with node
+ // modify the control point road and join it the the node road
+ // console.log( "only join roads", this.road.id, node.roadId );
+
+
+ // another scenario of first node selected then controlpoint
+ // in this
+ // create new road and normally but with node as the first point
+ // and second point will be forward distance of x distance
+ // then 3rd point will be created wherever the point was selected
+
+ } else {
+
+ // this only selects the node
+
+ const road = this.openDrive.getRoadById( node.roadId );
+
+ CommandHistory.executeAll( [
+
+ new SetInspectorCommand( RoadInspector, { road, node } ),
+
+ new SetValueCommand( this, 'node', node ),
+
+ new SetValueCommand( this, 'road', road ),
+
+ new SetValueCommand( this, 'conrolPoint', null ),
+
+ ] );
+
+ }
+
+
+ break;
+ }
+ }
+
+ if ( !hasInteracted && this.node ) {
+
+ this.node.unselected();
+
+ this.node = null;
+
+ }
+
+ return hasInteracted;
+ }
+
+ private updateRoadGeometry ( road: TvRoad ): void {
+
+ road.spline.update();
+
+ this.openDrive.gameObject.remove( road.gameObject );
+
+ // remove old geometries
+ road.clearGeometries();
+
+ // TODO: can be improved
+ road.spline.exportGeometries().forEach( geometry => {
+
+ road.addGeometry( geometry );
+
+ } );
+
+ TvMapBuilder.buildRoad( this.openDrive.gameObject, road );
+
+ NodeFactoryService.updateRoadNodes( this.road );
+ }
+
+ private hideRoad ( road: TvRoad ): void {
+
+ road.spline.hide();
+
+ }
+
+ private showRoad ( road: TvRoad ): void {
+
+ road.spline.show();
+
+ }
+
+ private addControlPoint ( position: Vector3 ) {
+
+ // // unselect old if exists
+ // if ( this.controlPoint ) this.controlPoint.unselect();
+
+ if ( !this.road ) {
+
+ const result = RoadFactory.createRoad( position );
+
+ const addRoadCommand = new AddRoadCommand( result.road, result.point );
+
+ const setRoadCommand = new SetValueCommand( this, 'road', result.road );
+
+ const setPointCommand = new SetValueCommand( this, 'controlPoint', result.point );
+
+ CommandHistory.execute( new MultiCmdsCommand( [ addRoadCommand, setRoadCommand, setPointCommand ] ) );
+
+ } else {
+
+ const controlPoint = RoadFactory.addControlPoint( this.road, position );
+
+ const setPointCommand = new SetValueCommand( this, 'controlPoint', controlPoint );
+
+ const addPointCommand = new AddRoadPointCommand( this.road, controlPoint, this.controlPoint );
+
+ CommandHistory.execute( new MultiCmdsCommand( [ addPointCommand, setPointCommand ] ) );
+ }
+
+ // if ( !this.road ) this.road = this.openDrive.addDefaultRoad();
+
+
+ // // set new
+ // this.controlPoint = ControlPoint.create( "", position );
+
+ // // TODO: spline should take this responsibility
+ // SceneService.add( this.controlPoint );
+
+ // this.controlPoint.mainObject = this.controlPoint.userData.road = this.road;
+
+ // this.road.spline.addControlPoint( this.controlPoint );
+
+ // this.controlPoint.onSelected();
+
+ // if ( this.road.spline.controlPoints.length < 2 ) return;
+
+ // this.updateRoadGeometry( this.road );
+
+ // AppInspector.setInspector( RoadInspector, {
+ // road: this.road,
+ // controlPoint: this.controlPoint
+ // } );
+ }
+
+ private joinNodeWithNode ( firstNode: RoadNode, secondNode: RoadNode ) {
+
+ // console.log( "crate new road to join ", firstNode.roadId, secondNode.roadId );
+
+ const commands = [];
+
+ commands.push( new SetValueCommand( this, 'node', null ) );
+
+ commands.push( new SetValueCommand( this, 'road', null ) );
+
+ commands.push( new SetValueCommand( this, 'conrolPoint', null ) );
+
+ commands.push( new JoinRoadNodeCommand( firstNode, secondNode ) );
+
+ CommandHistory.execute( new MultiCmdsCommand( commands ) );
+
+ }
+
+ private joinPointWithNode () {
+
+ }
+
+ private joinNodeWithPoint () {
+
+ }
+}
\ No newline at end of file
diff --git a/src/app/core/tools/surface-tool.ts b/src/app/core/tools/surface-tool.ts
new file mode 100644
index 00000000..b46427ba
--- /dev/null
+++ b/src/app/core/tools/surface-tool.ts
@@ -0,0 +1,190 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseTool } from './base-tool';
+import { PointEditor } from '../editors/point-editor';
+import { AnyControlPoint } from 'app/modules/three-js/objects/control-point';
+import { Subscription } from 'rxjs';
+import { PointerEventData } from 'app/events/pointer-event-data';
+import { CatmullRomSpline } from '../shapes/catmull-rom-spline';
+import { TvSurface } from 'app/modules/tv-map/models/tv-surface.model';
+import { KeyboardInput } from '../input';
+
+export class SurfaceTool extends BaseTool {
+
+ public name: string = 'SurfaceTool';
+
+ public shapeEditor: PointEditor;
+
+ private cpSubscriptions: Subscription[] = [];
+
+ private cpAddedSub: Subscription;
+ private cpMovedSub: Subscription;
+ private cpUpdatedSub: Subscription;
+ private cpSelectedSub: Subscription;
+ private cpUnselectedSub: Subscription;
+ private keyDownSub: Subscription;
+
+ private surface: TvSurface;
+
+ constructor () {
+
+ super();
+
+ }
+
+ public init () {
+
+ super.init();
+
+ this.shapeEditor = new PointEditor( 100 );
+ }
+
+ public enable () {
+
+ super.enable();
+
+ this.openDrive.surfaces.forEach( surface => {
+
+ surface.update();
+
+ surface.showControlPoints();
+
+ surface.showCurve();
+
+ surface.spline.controlPoints.forEach( cp => {
+
+ cp.mainObject = surface;
+
+ this.shapeEditor.controlPoints.push( cp )
+
+ } )
+ } );
+
+ this.keyDownSub = KeyboardInput.keyDown
+ .subscribe( e => this.onDeletePressed( e ) );
+
+ this.cpAddedSub = this.shapeEditor.controlPointAdded
+ .subscribe( ( cp: AnyControlPoint ) => this.onControlPointAdded( cp ) );
+
+ this.cpMovedSub = this.shapeEditor.controlPointMoved
+ .subscribe( () => this.onControlPointMoved() );
+
+ this.cpUpdatedSub = this.shapeEditor.controlPointUpdated
+ .subscribe( () => this.onControlPointUpdated() );
+
+ this.cpSelectedSub = this.shapeEditor.controlPointSelected
+ .subscribe( ( cp: AnyControlPoint ) => this.onControlPointSelected( cp ) );
+
+ this.cpUnselectedSub = this.shapeEditor.controlPointUnselected
+ .subscribe( () => this.onControlPointUnselected() );
+
+ }
+
+ public disable (): void {
+
+ super.disable();
+
+ this.openDrive.surfaces.forEach( surface => {
+
+ surface.hideCurve();
+ surface.hideControlPoints();
+
+ } );
+
+ this.keyDownSub.unsubscribe();
+ this.cpAddedSub.unsubscribe();
+ this.cpMovedSub.unsubscribe();
+ this.cpUpdatedSub.unsubscribe();
+ this.cpSelectedSub.unsubscribe();
+ this.cpUnselectedSub.unsubscribe();
+
+ this.shapeEditor.destroy();
+ }
+
+ public onPointerClicked ( e: PointerEventData ) {
+
+ for ( let i = 0; i < e.intersections.length; i++ ) {
+
+ const intersection = e.intersections[ i ];
+
+ if ( intersection.object && intersection.object[ 'tag' ] === TvSurface.tag ) {
+
+ this.surface = intersection.object.userData.surface;
+
+ this.surface.showControlPoints();
+
+ break;
+ }
+ }
+ }
+
+ private onControlPointSelected ( cp: AnyControlPoint ) {
+
+ this.surface = cp.mainObject;
+
+ this.surface.showControlPoints();
+
+ }
+
+ private onControlPointUnselected () {
+
+ this.surface = null;
+
+ }
+
+ private onControlPointAdded ( cp: AnyControlPoint ) {
+
+ if ( !this.surface ) {
+
+ this.surface = new TvSurface( 'grass', new CatmullRomSpline() )
+
+ this.openDrive.surfaces.push( this.surface );
+
+ }
+
+ cp.mainObject = this.surface;
+
+ this.surface.spline.addControlPoint( cp );
+
+ this.surface.update();
+
+ }
+
+ private onControlPointUpdated () {
+
+ this.surface.spline.update();
+
+ this.surface.update();
+
+ }
+
+ private onControlPointMoved () {
+
+ this.surface.spline.update();
+
+ }
+
+ private onDeletePressed ( e: KeyboardEvent ) {
+
+ if ( e.key === "Delete" && this.surface ) {
+
+ this.surface.delete();
+
+ const index = this.openDrive.surfaces.findIndex( s => s.id == this.surface.id );
+
+ if ( index > -1 ) {
+
+ this.openDrive.surfaces.splice( index, 1 );
+
+ }
+
+ this.surface = null;
+
+ delete this.surface;
+ }
+
+ }
+
+}
diff --git a/src/app/core/tools/tool-manager.ts b/src/app/core/tools/tool-manager.ts
new file mode 100644
index 00000000..355a0d95
--- /dev/null
+++ b/src/app/core/tools/tool-manager.ts
@@ -0,0 +1,70 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { EventEmitter } from '@angular/core';
+import { BaseTool } from './base-tool';
+
+export class ToolManager {
+
+ public static toolChanged = new EventEmitter();
+
+ private static tool: BaseTool;
+
+ static get currentTool (): BaseTool {
+
+ return this.tool;
+
+ }
+
+ static set currentTool ( value: BaseTool ) {
+
+ if ( !value ) {
+
+ this.clear();
+
+ } else {
+
+ // dont do anything if the same tool is already being used
+ if ( this.tool && this.tool.name === value.name ) return;
+
+ this.destroyPreviousState();
+
+ this.tool = value;
+
+ this.tool.init();
+
+ this.tool.enable();
+
+ this.toolChanged.emit( value );
+
+ }
+
+ }
+
+ static clear () {
+
+ this.destroyPreviousState();
+
+ this.toolChanged.emit( null );
+ }
+
+ static disable () {
+
+ if ( this.tool != null ) this.tool.disable();
+
+ }
+
+ static enable () {
+
+ if ( this.tool != null ) this.tool.enable();
+
+ }
+
+ private static destroyPreviousState () {
+
+ this.disable();
+
+ delete this.tool;
+ }
+}
diff --git a/src/app/core/utils/console.ts b/src/app/core/utils/console.ts
new file mode 100644
index 00000000..e621eba6
--- /dev/null
+++ b/src/app/core/utils/console.ts
@@ -0,0 +1,74 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { environment } from '../../../environments/environment';
+
+enum ConsoleMessageType {
+ info,
+ warn,
+ error,
+}
+
+class ConsoleMessage {
+ constructor ( public type: ConsoleMessageType, public message: any, public counter = 0 ) { }
+}
+
+export class TvConsole {
+
+ static messages: ConsoleMessage[] = [];
+
+ static get lastMessage () { return this.messages[ this.messages.length - 1 ]; }
+
+ static clear () {
+
+ this.messages.splice( 0, this.messages.length );
+
+ }
+
+ static info ( message: string ) {
+
+ // if same message is being printed then just increase the counter
+ if ( this.lastMessage.type === ConsoleMessageType.info && this.lastMessage.message === message ) {
+
+ this.lastMessage.counter += 1;
+
+ } else {
+
+ this.messages.push( new ConsoleMessage( ConsoleMessageType.info, message ) );
+
+ }
+ }
+
+
+ static warn ( message: string ) {
+
+ // if same message is being printed then just increase the counter
+ if ( this.lastMessage.type === ConsoleMessageType.warn && this.lastMessage.message === message ) {
+
+ this.lastMessage.counter += 1;
+
+ } else {
+
+ this.messages.push( new ConsoleMessage( ConsoleMessageType.warn, message ) );
+
+ }
+
+ }
+
+
+ static error ( message: string ) {
+
+ // if same message is being printed then just increase the counter
+ if ( this.lastMessage.type === ConsoleMessageType.error && this.lastMessage.message === message ) {
+
+ this.lastMessage.counter += 1;
+
+ } else {
+
+ this.messages.push( new ConsoleMessage( ConsoleMessageType.error, message ) );
+
+ }
+
+ }
+}
diff --git a/src/app/core/utils/debug.ts b/src/app/core/utils/debug.ts
new file mode 100644
index 00000000..0e3ef526
--- /dev/null
+++ b/src/app/core/utils/debug.ts
@@ -0,0 +1,41 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { environment } from '../../../environments/environment';
+import { Mesh, MeshBasicMaterial, SphereBufferGeometry, Vector2, Vector3 } from 'three';
+import { SceneService } from '../services/scene.service';
+
+export class Debug {
+
+ private static sphereAdded: boolean;
+ private static sphere: Mesh;
+
+ static log ( message?: any, ...optionalParams: any[] ) {
+
+ if ( !environment.production ) {
+
+ console.log( message, optionalParams );
+
+ }
+ }
+
+ static drawSphere ( position: Vector3 | Vector2 ) {
+
+ if ( !this.sphereAdded ) {
+
+ const geometry = new SphereBufferGeometry();
+ const material = new MeshBasicMaterial();
+
+ this.sphere = new Mesh( geometry, material );
+
+ SceneService.add( this.sphere );
+
+ this.sphereAdded = true;
+ }
+
+ this.sphere.position.setX( position.x );
+ this.sphere.position.setY( position.y );
+
+ }
+}
diff --git a/src/app/core/utils/environment.ts b/src/app/core/utils/environment.ts
new file mode 100644
index 00000000..1d1c67b6
--- /dev/null
+++ b/src/app/core/utils/environment.ts
@@ -0,0 +1,27 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { environment } from '../../../environments/environment';
+
+export class Environment {
+
+ static get production (): boolean {
+
+ return environment.production;
+
+ }
+
+ static get oscEnabled (): boolean {
+
+ return environment.osc_enabled;
+
+ }
+
+ static get websiteUrl (): string {
+
+ return environment.web_url;
+
+ }
+
+}
diff --git a/src/app/core/utils/threejs-utils.ts b/src/app/core/utils/threejs-utils.ts
new file mode 100644
index 00000000..7d1bf478
--- /dev/null
+++ b/src/app/core/utils/threejs-utils.ts
@@ -0,0 +1,12 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import * as THREE from 'three';
+import { Mesh } from 'three';
+
+export class ThreeJsUtils {
+
+
+
+}
diff --git a/src/app/events/event-system-handler.ts b/src/app/events/event-system-handler.ts
new file mode 100644
index 00000000..a4c9fd19
--- /dev/null
+++ b/src/app/events/event-system-handler.ts
@@ -0,0 +1,12 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+export interface IEventSystemHandler {
+
+}
+
+export class EventSystemHandler implements IEventSystemHandler {
+
+
+}
diff --git a/src/app/events/event-system.service.ts b/src/app/events/event-system.service.ts
new file mode 100644
index 00000000..53c91075
--- /dev/null
+++ b/src/app/events/event-system.service.ts
@@ -0,0 +1,43 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { EventEmitter, Injectable, Output } from '@angular/core';
+import { BaseEventData, PointerEventData, PointerMoveData } from './pointer-event-data';
+
+@Injectable( {
+ providedIn: 'root'
+} )
+export class EventSystem {
+
+ @Output() pointerClicked = new EventEmitter();
+ @Output() pointerMoved = new EventEmitter();
+ @Output() pointerUp = new EventEmitter();
+ @Output() pointerDown = new EventEmitter();
+ @Output() pointerEnter = new EventEmitter();
+ @Output() pointerExit = new EventEmitter();
+ @Output() pointerOut = new EventEmitter();
+ @Output() pointerLeave = new EventEmitter();
+ @Output() beginDrag = new EventEmitter();
+ @Output() endDrag = new EventEmitter();
+ @Output() drag = new EventEmitter();
+ @Output() drop = new EventEmitter();
+ @Output() select = new EventEmitter();
+ @Output() deSelect = new EventEmitter();
+
+
+ constructor () {
+
+ // this.pointerClicked.subscribe( e => Debug.log( 'pointerClicked', e ) );
+ // this.pointerMoved.subscribe( e => Debug.log( 'pointerMoved', e ) );
+ // this.pointerUp.subscribe( e => Debug.log( 'pointerUp', e ) );
+ // this.pointerDown.subscribe( e => Debug.log( 'pointerDown', e ) );
+ // this.pointerEnter.subscribe( e => Debug.log( 'pointerEnter', e ) );
+ // this.pointerExit.subscribe( e => Debug.log( 'pointerExit', e ) );
+ // this.select.subscribe( e => Debug.log( 'select', e ) );
+ // this.deSelect.subscribe( e => Debug.log( 'deSelect', e ) );
+ // this.beginDrag.subscribe( e => Debug.log( 'beginDrag', e ) );
+ // this.drag.subscribe( e => Debug.log( 'drag', e ) );
+
+ }
+}
diff --git a/src/app/events/pointer-event-data.ts b/src/app/events/pointer-event-data.ts
new file mode 100644
index 00000000..b33a7506
--- /dev/null
+++ b/src/app/events/pointer-event-data.ts
@@ -0,0 +1,33 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import {Face3, Intersection, Object3D, Vector2, Vector3} from 'three/three-core';
+
+export class BaseEventData {
+ object: Object3D;
+}
+
+export enum MouseButton {
+ LEFT = 0,
+ MIDDLE = 1,
+ RIGHT = 2
+}
+
+export class PointerMoveData {
+ point: Vector3;
+}
+
+export class PointerEventData extends BaseEventData {
+ distance: number;
+ distanceToRay?: number;
+ point: Vector3;
+ index?: number;
+ face?: Face3 | null;
+ faceIndex?: number;
+ uv?: Vector2;
+ button: MouseButton;
+ intersections?: Intersection[];
+ approxCameraDistance?: number
+}
+
diff --git a/src/app/modules/three-js/EnableThreeExamples.js b/src/app/modules/three-js/EnableThreeExamples.js
new file mode 100644
index 00000000..3f704223
--- /dev/null
+++ b/src/app/modules/three-js/EnableThreeExamples.js
@@ -0,0 +1,5 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+THREE = require( 'three' );
diff --git a/src/app/modules/three-js/commands/set-material-map-command.ts b/src/app/modules/three-js/commands/set-material-map-command.ts
new file mode 100644
index 00000000..5924e013
--- /dev/null
+++ b/src/app/modules/three-js/commands/set-material-map-command.ts
@@ -0,0 +1,40 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseCommand } from 'app/core/commands/base-command';
+import { Material, Texture } from 'three';
+
+export class SetMaterialMapCommand extends BaseCommand {
+
+ private oldMap: Texture | null;
+
+ constructor ( private material: Material, private mapName, private newMap: Texture, private materialSlot ) {
+
+ super();
+
+ this.oldMap = this.material[ mapName ];
+ }
+
+ execute (): void {
+
+ this.material[ this.mapName ] = this.newMap;
+ this.material[ 'needsUpdate' ] = true;
+
+ }
+
+ undo (): void {
+
+ this.material[ this.mapName ] = this.oldMap;
+ this.material[ 'needsUpdate' ] = true;
+
+ }
+
+ redo (): void {
+
+ this.execute();
+
+ }
+
+
+}
diff --git a/src/app/modules/three-js/commands/set-position-command.ts b/src/app/modules/three-js/commands/set-position-command.ts
new file mode 100644
index 00000000..77fa3ed9
--- /dev/null
+++ b/src/app/modules/three-js/commands/set-position-command.ts
@@ -0,0 +1,58 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseCommand } from 'app/core/commands/base-command';
+
+export class SetPositionCommand extends BaseCommand {
+
+ private oldPosition: THREE.Vector3;
+
+ constructor (
+ private object: THREE.Object3D,
+ private newPosition: THREE.Vector3,
+ private optionalOldPosition: THREE.Vector3 = null
+ ) {
+
+ super();
+
+ if ( object !== null && newPosition !== null ) {
+
+ this.oldPosition = object.position.clone();
+ this.newPosition = newPosition.clone();
+
+ }
+
+ if ( optionalOldPosition !== null ) {
+
+ this.oldPosition = optionalOldPosition.clone();
+
+ }
+
+ }
+
+ execute (): void {
+
+ this.object.position.copy( this.newPosition );
+ this.object.updateMatrixWorld( true );
+
+ // this.editor.signals.objectChanged.dispatch( this.object );
+
+ }
+
+ undo (): void {
+
+ this.object.position.copy( this.oldPosition );
+ this.object.updateMatrixWorld( true );
+
+ // this.editor.signals.objectChanged.dispatch( this.object );
+
+ }
+
+ redo (): void {
+
+ this.object.position.copy( this.newPosition );
+ this.object.updateMatrixWorld( true );
+
+ }
+}
diff --git a/src/app/modules/three-js/commands/set-rotation-command.ts b/src/app/modules/three-js/commands/set-rotation-command.ts
new file mode 100644
index 00000000..00857fd8
--- /dev/null
+++ b/src/app/modules/three-js/commands/set-rotation-command.ts
@@ -0,0 +1,55 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseCommand } from '../../../core/commands/base-command';
+import { Euler, Object3D } from 'three';
+
+export class SetRotationCommand extends BaseCommand {
+
+ private readonly oldRotation: Euler;
+
+ constructor (
+ private object: Object3D,
+ private newRotation: Euler,
+ private optionalOldRotation: Euler = null
+ ) {
+
+ super();
+
+ if ( object !== null && newRotation !== null ) {
+
+ this.oldRotation = object.rotation.clone();
+ this.newRotation = newRotation.clone();
+
+ }
+
+ if ( optionalOldRotation !== null ) {
+
+ this.oldRotation = optionalOldRotation.clone();
+
+ }
+
+ }
+
+ execute (): void {
+
+ this.object.rotation.copy( this.newRotation );
+ this.object.updateMatrixWorld( true );
+
+ }
+
+ undo (): void {
+
+ this.object.rotation.copy( this.oldRotation );
+ this.object.updateMatrixWorld( true );
+
+ }
+
+ redo (): void {
+
+ this.execute();
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/app/modules/three-js/commands/set-scale-command.ts b/src/app/modules/three-js/commands/set-scale-command.ts
new file mode 100644
index 00000000..c66929d2
--- /dev/null
+++ b/src/app/modules/three-js/commands/set-scale-command.ts
@@ -0,0 +1,55 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseCommand } from '../../../core/commands/base-command';
+import { Object3D, Vector3 } from 'three';
+
+export class SetScaleCommand extends BaseCommand {
+
+ private readonly oldScale: Vector3;
+
+ constructor (
+ private object: Object3D,
+ private newScale: Vector3,
+ private optionalOldScale: Vector3 = null
+ ) {
+
+ super();
+
+ if ( object !== null && newScale !== null ) {
+
+ this.oldScale = object.scale.clone();
+ this.newScale = newScale.clone();
+
+ }
+
+ if ( optionalOldScale !== null ) {
+
+ this.oldScale = optionalOldScale.clone();
+
+ }
+
+ }
+
+ execute (): void {
+
+ this.object.scale.copy( this.newScale );
+ this.object.updateMatrixWorld( true );
+
+ }
+
+ undo (): void {
+
+ this.object.scale.copy( this.oldScale );
+ this.object.updateMatrixWorld( true );
+
+ }
+
+ redo (): void {
+
+ this.execute();
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/app/modules/three-js/commands/set-value-command.ts b/src/app/modules/three-js/commands/set-value-command.ts
new file mode 100644
index 00000000..ef701dd9
--- /dev/null
+++ b/src/app/modules/three-js/commands/set-value-command.ts
@@ -0,0 +1,36 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseCommand } from 'app/core/commands/base-command';
+
+export class SetValueCommand extends BaseCommand {
+
+ private oldValue: any;
+
+ constructor ( private object: Object, private attributeName, private newValue: any ) {
+
+ super();
+
+ // store the old value
+ this.oldValue = ( object !== undefined ) ? object[ attributeName ] : undefined;
+ }
+
+ execute (): void {
+
+ this.object[ this.attributeName ] = this.newValue;
+
+ }
+
+ undo (): void {
+
+ this.object[ this.attributeName ] = this.oldValue;
+
+ }
+
+ redo (): void {
+
+ this.execute();
+
+ }
+}
diff --git a/src/app/modules/three-js/inspectors/material-inspector/_material-inspector.component.html b/src/app/modules/three-js/inspectors/material-inspector/_material-inspector.component.html
new file mode 100644
index 00000000..ac4d6be7
--- /dev/null
+++ b/src/app/modules/three-js/inspectors/material-inspector/_material-inspector.component.html
@@ -0,0 +1,17 @@
+
+
+
\ No newline at end of file
diff --git a/src/app/modules/three-js/inspectors/material-inspector/_material-inspector.component.ts b/src/app/modules/three-js/inspectors/material-inspector/_material-inspector.component.ts
new file mode 100644
index 00000000..ea6cfe37
--- /dev/null
+++ b/src/app/modules/three-js/inspectors/material-inspector/_material-inspector.component.ts
@@ -0,0 +1,67 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+// import { Component, Input, OnInit } from '@angular/core';
+// import { IComponent } from '../../../../core/game-object';
+// import * as THREE from 'three';
+// import { Material, MeshBasicMaterial, Texture } from 'three';
+// import { CommandHistory } from '../../../../services/command-history';
+// import { SetMaterialMapCommand } from '../../commands/set-material-map-command';
+
+// @Component( {
+// selector: 'app-material-inspector',
+// templateUrl: './material-inspector.component.html',
+// } )
+// /**
+// * @deprecated
+// */
+// export class MaterialInspectorComponent implements OnInit, IComponent {
+
+// @Input() data: Material | Material[];
+
+// /**
+// * Color Picker position
+// */
+// @Input() cpPosition: string = 'top';
+
+
+// get material (): MeshBasicMaterial {
+// return this.data as MeshBasicMaterial;
+// }
+
+// get color (): any {
+// return '#' + this.material.color.getHexString();
+// }
+
+// set color ( value: any ) {
+// this.material.color.setStyle( value );
+// }
+
+// ngOnInit (): void {
+// }
+
+// onEventLog ( event: string, data: string ) {
+
+// // console.log( event, data );
+
+// // this.data.color.setStyle( data[ 'color' ] );
+
+// // console.log( '#' + this.data.color.getHexString() );
+// // console.log( '#' + this.data.color.getHex() );
+// // console.log( '#' + this.data.color.getStyle() );
+
+// }
+
+// onAlbedoChanged ( texture: Texture ) {
+
+// texture.wrapS = THREE.RepeatWrapping;
+// texture.wrapT = THREE.RepeatWrapping;
+// texture.mapping = THREE.UVMapping;
+// texture.repeat.set( 1, 1 );
+// texture.anisotropy = 5;
+
+// CommandHistory.execute( new SetMaterialMapCommand( this.material, 'map', texture, 1 ) );
+
+// }
+// }
diff --git a/src/app/modules/three-js/inspectors/texture-field/texture-field.component.css b/src/app/modules/three-js/inspectors/texture-field/texture-field.component.css
new file mode 100644
index 00000000..707523f7
--- /dev/null
+++ b/src/app/modules/three-js/inspectors/texture-field/texture-field.component.css
@@ -0,0 +1,4 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
diff --git a/src/app/modules/three-js/inspectors/texture-field/texture-field.component.html b/src/app/modules/three-js/inspectors/texture-field/texture-field.component.html
new file mode 100644
index 00000000..23b8d0cd
--- /dev/null
+++ b/src/app/modules/three-js/inspectors/texture-field/texture-field.component.html
@@ -0,0 +1,8 @@
+
+
+
+{{ label }}
diff --git a/src/app/modules/three-js/inspectors/texture-field/texture-field.component.ts b/src/app/modules/three-js/inspectors/texture-field/texture-field.component.ts
new file mode 100644
index 00000000..8f0383e4
--- /dev/null
+++ b/src/app/modules/three-js/inspectors/texture-field/texture-field.component.ts
@@ -0,0 +1,163 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
+import * as THREE from 'three';
+import { Texture } from 'three';
+
+@Component( {
+ selector: 'app-texture-field',
+ templateUrl: './texture-field.component.html',
+ styleUrls: [ './texture-field.component.css' ]
+} )
+/**
+ * @deprecated
+ */
+export class TextureFieldComponent implements OnInit {
+
+ @Input() data: Texture | null;
+ @Input() label: string = 'Texture';
+
+ @Output() changed = new EventEmitter();
+
+ @ViewChild( 'canvas' ) canvas: ElementRef;
+
+ private input: any;
+
+ constructor () {
+ }
+
+ get canvasEl (): HTMLCanvasElement {
+ return this.canvas.nativeElement;
+ }
+
+ get texture () {
+ return this.data;
+ }
+
+ set texture ( value ) {
+ this.data = value;
+ }
+
+ ngOnInit (): void {
+
+ const dom = document.createElement( 'span' );
+ const form = document.createElement( 'form' );
+
+ this.input = document.createElement( 'input' );
+ this.input.type = 'file';
+ this.input.addEventListener( 'change', ( event: any ) => {
+
+ this.loadFile( event.target.files[ 0 ] );
+
+ } );
+
+ form.appendChild( this.input );
+
+ if ( this.data != null ) this.setValue( this.texture );
+ }
+
+ loadFile ( file ) {
+
+ if ( file.type.match( 'image.*' ) ) {
+
+ const reader = new FileReader();
+
+ if ( file.type === 'image/targa' ) {
+
+ // reader.addEventListener( 'load', function ( event ) {
+ //
+ // // var canvas = new THREE.TGALoader().parse( event.target.result );
+ // //
+ // // var texture = new THREE.CanvasTexture( canvas, mapping );
+ // // texture.sourceFile = file.name;
+ // //
+ // // scope.setValue( texture );
+ // //
+ // // if ( scope.onChangeCallback ) scope.onChangeCallback( texture );
+ //
+ // }, false );
+ //
+ // reader.readAsArrayBuffer( file );
+ //
+ } else {
+
+ reader.addEventListener( 'load', ( event: any ) => {
+
+ const image = document.createElement( 'img' );
+
+ image.addEventListener( 'load', ( event: any ) => {
+
+ const texture = new Texture( image, THREE.UVMapping );
+ texture.sourceFile = file.name;
+ texture.format = file.type === 'image/jpeg' ? THREE.RGBFormat : THREE.RGBAFormat;
+ texture.needsUpdate = true;
+
+ this.setValue( texture );
+
+ this.changed.emit( texture );
+
+ }, false );
+
+ image.src = event.target.result;
+
+ }, false );
+
+ reader.readAsDataURL( file );
+
+ }
+
+ }
+
+ // form.reset();
+
+ }
+
+ onClick () {
+
+ this.input.click();
+
+ }
+
+ private setValue ( texture: Texture ) {
+
+ const canvas = this.canvasEl;
+ const context = canvas.getContext( '2d' );
+
+ if ( texture !== null ) {
+
+ const image = texture.image;
+
+ if ( image !== undefined && image.width > 0 ) {
+
+ canvas.title = texture.sourceFile;
+
+ const scale = canvas.width / image.width;
+ context.drawImage( image, 0, 0, image.width * scale, image.height * scale );
+
+ } else {
+
+ canvas.title = texture.sourceFile + ' (error)';
+ context.clearRect( 0, 0, canvas.width, canvas.height );
+
+ }
+
+ } else {
+
+ canvas.title = 'empty';
+
+ if ( context !== null ) {
+
+ // Seems like context can be null if the canvas is not visible
+
+ context.clearRect( 0, 0, canvas.width, canvas.height );
+
+ }
+
+ }
+
+ this.texture = texture;
+
+ }
+}
diff --git a/src/app/modules/three-js/objects/control-point.ts b/src/app/modules/three-js/objects/control-point.ts
new file mode 100644
index 00000000..498a5d8d
--- /dev/null
+++ b/src/app/modules/three-js/objects/control-point.ts
@@ -0,0 +1,213 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BufferGeometry, Color, Geometry, Group, LineSegments, Material, Points, PointsMaterial, Vector3 } from 'three';
+import { EventEmitter } from '@angular/core';
+import { COLOR } from 'app/shared/utils/colors.service';
+import { OdTextures } from 'app/modules/tv-map/builders/od.textures';
+import { TvLaneWidth } from 'app/modules/tv-map/models/tv-lane-width';
+import { TvRoadLaneOffset } from 'app/modules/tv-map/models/tv-road-lane-offset';
+import { TvLaneRoadMark } from 'app/modules/tv-map/models/tv-lane-road-mark';
+import { TvRoad } from 'app/modules/tv-map/models/tv-road.model';
+import { TvLane } from '../../tv-map/models/tv-lane';
+
+export abstract class BaseControlPoint extends Points {
+
+ public mainObject: any;
+
+ public tag: string;
+ public tagindex: number;
+
+ public updated = new EventEmitter();
+
+ protected DEFAULT_CONTROL_POINT_COLOR = COLOR.BLUE;
+ protected HOVERED_CONTROL_POINT_COLOR = COLOR.YELLOW;
+ protected SELECTED_CONTROL_POINT_COLOR = COLOR.RED;
+
+ public isSelected: boolean;
+
+ constructor ( geometry?: Geometry | BufferGeometry, material?: Material | Material[] ) {
+
+ super( geometry, material );
+
+ }
+
+ setPosition ( position: Vector3 ) {
+
+ this.position.copy( position );
+
+ this.updated.emit( this );
+ }
+
+ copyPosition ( position: Vector3 ) {
+
+ this.setPosition( position.clone() );
+
+ }
+
+ show (): void {
+
+ this.visible = true;
+
+ }
+
+ hide (): void {
+
+ this.visible = false;
+
+ }
+
+ onMouseOver () {
+
+ ( this.material as PointsMaterial ).color = new Color( this.HOVERED_CONTROL_POINT_COLOR );
+ ( this.material as PointsMaterial ).needsUpdate = true;
+
+ }
+
+ onMouseOut () {
+
+ ( this.material as PointsMaterial ).color = new Color( this.DEFAULT_CONTROL_POINT_COLOR );
+ ( this.material as PointsMaterial ).needsUpdate = true;
+
+ }
+
+ select () {
+
+ this.isSelected = true;
+
+ ( this.material as PointsMaterial ).color = new Color( this.SELECTED_CONTROL_POINT_COLOR );
+ ( this.material as PointsMaterial ).needsUpdate = true;
+
+ }
+
+ unselect () {
+
+ this.isSelected = false;
+
+ ( this.material as PointsMaterial ).color = new Color( this.DEFAULT_CONTROL_POINT_COLOR );
+ ( this.material as PointsMaterial ).needsUpdate = true;
+
+ }
+
+}
+
+export class DistanceNode extends BaseControlPoint {
+
+ constructor ( public s: number, geometry?: Geometry | BufferGeometry, material?: Material ) {
+ super( geometry, material );
+ }
+
+}
+export class NewDistanceNode extends BaseControlPoint {
+
+ constructor ( public roadId, public laneId, public s: number, public t: number, geometry?: Geometry | BufferGeometry, material?: Material ) {
+ super( geometry, material );
+ }
+
+}
+
+export class LaneWidthNode extends Group {
+
+ public static readonly tag = 'width-node';
+ public static readonly pointTag = 'width-point';
+ public static readonly lineTag = 'width-line';
+
+ public line: LineSegments;
+ public point: AnyControlPoint;
+
+ constructor ( public road: TvRoad, public lane: TvLane, public s: number, public laneWidth: TvLaneWidth ) {
+
+ super();
+
+ }
+
+ get roadId () { return this.road.id }
+
+ get laneId () { return this.lane.id }
+
+ updateLaneWidthValues () {
+
+ this.road.getLaneSectionAt( this.s ).updateLaneWidthValues( this.lane );
+
+ }
+
+}
+
+export class LaneOffsetNode extends Group {
+
+ public static readonly tag = 'offset-node';
+ public static readonly pointTag = 'offset-point';
+ public static readonly lineTag = 'offset-line';
+
+ public line: LineSegments;
+ public point: AnyControlPoint;
+
+ constructor ( public road: TvRoad, public laneOffset: TvRoadLaneOffset ) {
+
+ super();
+
+ }
+
+ get roadId () { return this.road.id }
+}
+
+export class LaneRoadMarkNode extends Group {
+
+ public static readonly tag = 'roadmark-node';
+ public static readonly pointTag = 'roadmark-point';
+ public static readonly lineTag = 'roadmark-line';
+
+ public line: LineSegments;
+ public point: AnyControlPoint;
+
+ constructor ( public lane: TvLane, public roadmark: TvLaneRoadMark ) {
+
+ super();
+
+ }
+}
+
+
+/**
+ * @deprecated avoid using this use BaseControlPoint or use an exact implementation
+ */
+export class AnyControlPoint extends BaseControlPoint {
+
+ static roadTag = 'road';
+
+ static create ( name = "", position?: Vector3 ) {
+
+ const dotGeometry = new Geometry();
+
+ dotGeometry.vertices.push( new Vector3( 0, 0, 0 ) );
+
+ const texture = OdTextures.point;
+
+ const dotMaterial = new PointsMaterial( {
+ size: 10,
+ sizeAttenuation: false,
+ map: texture,
+ alphaTest: 0.5,
+ transparent: true,
+ color: COLOR.BLUE,
+ depthTest: false
+ } );
+
+ const cp = new AnyControlPoint( dotGeometry, dotMaterial );
+
+ if ( position ) cp.copyPosition( position );
+
+ cp.userData.is_button = true;
+ cp.userData.is_control_point = true;
+ cp.userData.is_selectable = true;
+
+ cp.tag = this.roadTag;
+
+ cp.renderOrder = 3;
+
+ return cp;
+ }
+
+}
+
diff --git a/src/app/modules/three-js/objects/editor-controls.ts b/src/app/modules/three-js/objects/editor-controls.ts
new file mode 100644
index 00000000..0b2f5c96
--- /dev/null
+++ b/src/app/modules/three-js/objects/editor-controls.ts
@@ -0,0 +1,52 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import * as THREE from 'three';
+import { Camera, Vector3 } from 'three';
+import { IViewportController } from './i-viewport-controller';
+
+import 'three/examples/js/controls/EditorControls';
+
+export class EditorControls extends THREE.EditorControls implements IViewportController {
+
+
+ static getNew ( camera: Camera, canvas: HTMLCanvasElement ) {
+
+ return new EditorControls( camera, canvas );
+ }
+
+ setTarget ( position: Vector3 ): void {
+ this.center = position;
+ }
+
+ getTarget (): Vector3 {
+ return undefined;
+ }
+
+ setCamera ( camera: Camera ): void {
+ this[ 'object' ] = camera;
+ }
+
+ setEnabled ( enabled: boolean ): void {
+ this.enabled = enabled;
+ }
+
+ setPanEnabled ( enabled: boolean ): void {
+ }
+
+ setZoomEnabled ( enabled: boolean ): void {
+ }
+
+ setRotateEnabled ( enabled: boolean ): void {
+ }
+
+ setScreenSpaceEnabled ( enabled: boolean ): void {
+ }
+
+ update (): void {
+ }
+
+ reset (): void {
+ }
+}
diff --git a/src/app/modules/three-js/objects/i-viewport-controller.ts b/src/app/modules/three-js/objects/i-viewport-controller.ts
new file mode 100644
index 00000000..fe65109e
--- /dev/null
+++ b/src/app/modules/three-js/objects/i-viewport-controller.ts
@@ -0,0 +1,30 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Camera, Vector3 } from 'three';
+
+export interface IViewportController {
+
+ enabled: boolean;
+
+ setTarget ( position: Vector3 ): void;
+
+ getTarget (): Vector3;
+
+ setCamera ( camera: Camera ): void;
+
+ setEnabled ( enabled: boolean ): void;
+
+ setPanEnabled ( enabled: boolean ): void;
+
+ setZoomEnabled ( enabled: boolean ): void;
+
+ setRotateEnabled ( enabled: boolean ): void;
+
+ setScreenSpaceEnabled ( enabled: boolean ): void;
+
+ update (): void;
+
+ reset (): void;
+}
\ No newline at end of file
diff --git a/src/app/modules/three-js/objects/junction-entry.object.ts b/src/app/modules/three-js/objects/junction-entry.object.ts
new file mode 100644
index 00000000..dbf92b39
--- /dev/null
+++ b/src/app/modules/three-js/objects/junction-entry.object.ts
@@ -0,0 +1,76 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Color, Geometry, PointsMaterial, Vector3 } from 'three';
+import { COLOR } from 'app/shared/utils/colors.service';
+import { OdTextures } from 'app/modules/tv-map/builders/od.textures';
+import { TvRoad } from 'app/modules/tv-map/models/tv-road.model';
+import { BaseControlPoint } from './control-point';
+import { TvContactPoint } from 'app/modules/tv-map/models/tv-common';
+import { TvLane } from '../../tv-map/models/tv-lane';
+
+export class JunctionEntryObject extends BaseControlPoint {
+
+ public static tag = 'junction-dot';
+
+ public contact: TvContactPoint;
+
+ public road: TvRoad;
+
+ public lane: TvLane;
+
+ constructor ( name: string, position: Vector3, contact: TvContactPoint, road: TvRoad, lane?: TvLane ) {
+
+ const geometry = new Geometry();
+
+ geometry.vertices.push( new Vector3( 0, 0, 0 ) );
+
+ const texture = OdTextures.point;
+
+ const material = new PointsMaterial( {
+ size: 20,
+ sizeAttenuation: false,
+ map: texture,
+ alphaTest: 0.5,
+ transparent: true,
+ color: COLOR.SKYBLUE,
+ depthTest: true
+ } );
+
+ super( geometry, material );
+
+ this.contact = contact;
+
+ this.road = road;
+
+ this.lane = lane;
+
+ this.name = name;
+
+ if ( position ) this.copyPosition( position );
+
+ this.tag = JunctionEntryObject.tag;
+
+ this.renderOrder = 3;
+
+ }
+
+ select () {
+
+ this.isSelected = true;
+
+ ( this.material as PointsMaterial ).color = new Color( COLOR.RED );
+ ( this.material as PointsMaterial ).needsUpdate = true;
+
+ }
+
+ unselect () {
+
+ this.isSelected = false;
+
+ ( this.material as PointsMaterial ).color = new Color( COLOR.SKYBLUE );
+ ( this.material as PointsMaterial ).needsUpdate = true;
+
+ }
+}
diff --git a/src/app/modules/three-js/objects/orbit-controls.ts b/src/app/modules/three-js/objects/orbit-controls.ts
new file mode 100644
index 00000000..4643b24c
--- /dev/null
+++ b/src/app/modules/three-js/objects/orbit-controls.ts
@@ -0,0 +1,55 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import * as THREE from 'three';
+import { Camera, Vector3 } from 'three';
+import { IViewportController } from './i-viewport-controller';
+
+import 'three/examples/js/controls/OrbitControls';
+
+export class OrbitControls extends THREE.OrbitControls implements IViewportController {
+
+ static getNew ( camera, canvas ): OrbitControls {
+
+ const controls = new OrbitControls( camera, canvas );
+
+ controls.screenSpacePanning = true;
+ controls.enableRotate = false;
+ controls.enableKeys = false;
+
+ return controls;
+ }
+
+ setTarget ( position: Vector3 ): void {
+ this.target = position;
+ }
+
+ getTarget (): Vector3 {
+ return this.target;
+ }
+
+ setCamera ( camera: Camera ): void {
+ this.object = camera;
+ }
+
+ setEnabled ( enabled: boolean ): void {
+ this.enabled = enabled;
+ }
+
+ setPanEnabled ( enabled: boolean ): void {
+ this.enablePan = enabled;
+ }
+
+ setZoomEnabled ( enabled: boolean ): void {
+ this.enableZoom = enabled;
+ }
+
+ setRotateEnabled ( enabled: boolean ): void {
+ this.enableRotate = enabled;
+ }
+
+ setScreenSpaceEnabled ( enabled: boolean ): void {
+ this.screenSpacePanning = enabled;
+ }
+}
diff --git a/src/app/modules/three-js/objects/road-control-point.ts b/src/app/modules/three-js/objects/road-control-point.ts
new file mode 100644
index 00000000..af96efe6
--- /dev/null
+++ b/src/app/modules/three-js/objects/road-control-point.ts
@@ -0,0 +1,207 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseControlPoint } from './control-point';
+import { TvRoad } from 'app/modules/tv-map/models/tv-road.model';
+import { BufferAttribute, BufferGeometry, Geometry, Line, LineBasicMaterial, PointsMaterial, Vector3 } from 'three';
+import { OdTextures } from 'app/modules/tv-map/builders/od.textures';
+import { COLOR } from 'app/shared/utils/colors.service';
+import { RoadTangentPoint } from './road-tangent-point';
+import { CURVE_Y } from 'app/core/shapes/spline-config';
+import { SceneService } from 'app/core/services/scene.service';
+import { TvGeometryType } from 'app/modules/tv-map/models/tv-common';
+
+export class RoadControlPoint extends BaseControlPoint {
+
+ public static readonly tag = 'road-control-point';
+
+ public frontTangent: RoadTangentPoint;
+ public backTangent: RoadTangentPoint;
+
+ public tangentLine: Line;
+ public tangentLineGeometry: BufferGeometry;
+ public tangentLineMaterial = new LineBasicMaterial( {
+ color: 0x0000ff,
+ linewidth: 2
+ } );
+
+ public hdg: number = 0;
+ public segmentType: TvGeometryType;
+
+ public allowChange: boolean = true;
+
+ // tag, tagindex, cpobjidx are not used anywhere in new fixed workflow
+ // can add hdg here and
+ // remove segmentType from here to spline directly
+ constructor (
+ public road: TvRoad,
+ position: Vector3,
+ tag = 'cp',
+ tagindex?: number,
+ cpobjidx?: number,
+ ) {
+
+ super( null, null );
+
+ this.geometry = new Geometry();
+
+ this.geometry.vertices.push( new Vector3( 0, 0, 0 ) );
+
+ const texture = OdTextures.point;
+
+ this.material = new PointsMaterial( {
+ size: 10,
+ sizeAttenuation: false,
+ map: texture,
+ alphaTest: 0.5,
+ transparent: true,
+ color: COLOR.BLUE,
+ depthTest: false
+ } );
+
+ if ( position ) this.copyPosition( position );
+
+ this.userData.is_button = true;
+ this.userData.is_control_point = true;
+ this.userData.is_selectable = true;
+
+ this.tag = tag;
+ this.tagindex = tagindex;
+
+ this.renderOrder = 3;
+
+ }
+
+ copyPosition ( position: Vector3 ) {
+
+ if ( !this.allowChange ) return;
+
+ super.copyPosition( position );
+
+ if ( this.frontTangent ) {
+
+ this.segmentType = TvGeometryType.SPIRAL;
+
+ const frontPosition = new Vector3( Math.cos( this.hdg ), Math.sin( this.hdg ), CURVE_Y )
+ .multiplyScalar( this.frontTangent.length )
+ .add( this.position );
+
+ this.frontTangent.copyPosition( frontPosition );
+
+ }
+
+ if ( this.backTangent ) {
+
+ this.segmentType = TvGeometryType.SPIRAL;
+
+ const backPosition = new Vector3( Math.cos( this.hdg ), Math.sin( this.hdg ), CURVE_Y )
+ .multiplyScalar( -this.backTangent.length )
+ .add( this.position );
+
+ this.backTangent.copyPosition( backPosition );
+
+ }
+
+ if ( this.frontTangent || this.backTangent ) {
+
+ this.updateTangentLine();
+
+ }
+
+ }
+
+ show () {
+
+ super.show();
+
+ if ( this.frontTangent ) this.frontTangent.show();
+
+ if ( this.backTangent ) this.backTangent.show();
+
+ if ( this.tangentLine ) this.tangentLine.visible = true;
+ }
+
+ hide () {
+
+ super.hide();
+
+ if ( this.frontTangent ) this.frontTangent.hide();
+
+ if ( this.backTangent ) this.backTangent.hide();
+
+ if ( this.tangentLine ) this.tangentLine.visible = false;
+
+ }
+
+ addDefaultTangents ( hdg: number, frontLength: number, backLength: number ) {
+
+ const frontPosition = new Vector3( Math.cos( hdg ), Math.sin( hdg ), CURVE_Y )
+ .multiplyScalar( frontLength )
+ .add( this.position )
+
+ const backPosition = new Vector3( Math.cos( hdg ), Math.sin( hdg ), CURVE_Y )
+ .multiplyScalar( -backLength )
+ .add( this.position );
+
+ this.frontTangent = new RoadTangentPoint(
+ this.road,
+ frontPosition,
+ "tpf",
+ this.tagindex,
+ this.tagindex + 1 + this.tagindex * 2,
+ this,
+ );
+
+ this.backTangent = new RoadTangentPoint(
+ this.road,
+ backPosition,
+ "tpb",
+ this.tagindex,
+ this.tagindex + 1 + this.tagindex * 2 + 1,
+ this,
+ );
+
+ // TODO: move this maybe somewhere else
+
+ SceneService.add( this.frontTangent );
+
+ SceneService.add( this.backTangent );
+
+ this.tangentLineGeometry = new BufferGeometry().setFromPoints( [ this.frontTangent.position, this.backTangent.position ] );
+
+ this.tangentLine = new Line( this.tangentLineGeometry, this.tangentLineMaterial );
+
+ this.tangentLine.castShadow = true;
+
+ this.tangentLine.renderOrder = 3;
+
+ this.tangentLine.frustumCulled = false;
+
+ SceneService.add( this.tangentLine );
+ }
+
+ updateTangentLine () {
+
+ if ( this.tangentLine && this.backTangent ) {
+
+ const buffer = this.tangentLineGeometry.attributes.position as BufferAttribute;
+
+ buffer.setXYZ( 0, this.frontTangent.position.x, this.frontTangent.position.y, this.frontTangent.position.z );
+
+ buffer.setXYZ( 1, this.backTangent.position.x, this.backTangent.position.y, this.backTangent.position.z );
+
+ buffer.needsUpdate = true;
+ }
+
+ }
+
+ moveForward ( s: number ): RoadControlPoint {
+
+ const x = this.position.x + Math.cos( this.hdg ) * s;
+ const y = this.position.y + Math.sin( this.hdg ) * s;
+
+ return new RoadControlPoint( this.road, new Vector3( x, y, 0 ), this.tag, this.tagindex, this.tagindex );
+ }
+
+}
diff --git a/src/app/modules/three-js/objects/road-node.ts b/src/app/modules/three-js/objects/road-node.ts
new file mode 100644
index 00000000..64d04ff5
--- /dev/null
+++ b/src/app/modules/three-js/objects/road-node.ts
@@ -0,0 +1,45 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Color, Group, LineSegments, LineBasicMaterial } from 'three';
+import { COLOR } from 'app/shared/utils/colors.service';
+
+export class RoadNode extends Group {
+
+ public static readonly tag = 'road-node';
+ public static readonly lineTag = 'road-node-line';
+
+ public static defaultColor = COLOR.MAGENTA;
+ public static defaultOpacity = 0.35;
+ public static defaultWidth = 5;
+
+ public line: LineSegments;
+ public isSelected = false;
+
+ constructor ( public roadId: number, public distance: 'start' | 'end', public s?: number ) {
+
+ super();
+
+ }
+
+ get material () { return this.line.material as LineBasicMaterial; }
+
+ selected () {
+
+ this.isSelected = true;
+
+ this.material.color = new Color( COLOR.RED );
+
+ this.renderOrder = 5;
+ }
+
+ unselected () {
+
+ this.isSelected = false;
+
+ this.material.color = new Color( RoadNode.defaultColor );
+
+ this.renderOrder = 3;
+ }
+}
diff --git a/src/app/modules/three-js/objects/road-tangent-point.ts b/src/app/modules/three-js/objects/road-tangent-point.ts
new file mode 100644
index 00000000..48efaaaa
--- /dev/null
+++ b/src/app/modules/three-js/objects/road-tangent-point.ts
@@ -0,0 +1,118 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { BaseControlPoint } from './control-point';
+import { TvRoad } from 'app/modules/tv-map/models/tv-road.model';
+import { Geometry, PointsMaterial, Vector3 } from 'three';
+import { OdTextures } from 'app/modules/tv-map/builders/od.textures';
+import { COLOR } from 'app/shared/utils/colors.service';
+import { RoadControlPoint } from './road-control-point';
+import { CURVE_Y } from 'app/core/shapes/spline-config';
+import { TvGeometryType } from 'app/modules/tv-map/models/tv-common';
+
+export class RoadTangentPoint extends BaseControlPoint {
+
+ // public static readonly tag = 'road-tangent-point';
+
+ public length = 1;
+
+ // tag, tagindex, cpobjidx are not used anywhere in new fixed workflow
+ constructor (
+ public road: TvRoad,
+ position: Vector3,
+ public tag: 'tpf' | 'tpb',
+ public tagindex: number,
+ public cpobjidx: number,
+ public controlPoint: RoadControlPoint
+ ) {
+
+ super( null, null );
+
+ this.geometry = new Geometry();
+
+ this.geometry.vertices.push( new Vector3( 0, 0, 0 ) );
+
+ const texture = OdTextures.point;
+
+ this.material = new PointsMaterial( {
+ size: 10,
+ sizeAttenuation: false,
+ map: texture,
+ alphaTest: 0.5,
+ transparent: true,
+ color: COLOR.BLUE,
+ depthTest: false
+ } );
+
+ if ( position ) this.position.copy( position.clone() );
+
+ this.userData.is_button = true;
+ this.userData.is_control_point = true;
+ this.userData.is_selectable = true;
+
+ this.tag = tag;
+
+ this.tagindex = tagindex;
+
+ this.renderOrder = 3;
+
+ }
+
+ copyPosition ( position: Vector3 ) {
+
+ super.copyPosition( position );
+
+ this.controlPoint.segmentType = TvGeometryType.SPIRAL;
+
+ if ( this.tag === "tpf" && this.controlPoint.frontTangent ) {
+
+ const delta = new Vector3().subVectors(
+ this.controlPoint.frontTangent.position,
+ this.controlPoint.position
+ );
+
+ this.controlPoint.hdg = Math.atan2( delta.y, delta.x );
+
+ this.length = delta.length();
+
+ } else if ( this.tag === "tpb" && this.controlPoint.backTangent ) {
+
+ const delta = new Vector3().subVectors(
+ this.controlPoint.backTangent.position,
+ this.controlPoint.position
+ );
+
+ this.controlPoint.hdg = Math.PI + Math.atan2( delta.y, delta.x );
+
+ this.length = delta.length();
+
+ }
+
+ if ( this.controlPoint.frontTangent ) {
+
+ this.controlPoint.frontTangent.position.set(
+ Math.cos( this.controlPoint.hdg ),
+ Math.sin( this.controlPoint.hdg ),
+ CURVE_Y
+ ).multiplyScalar( this.controlPoint.frontTangent.length )
+ .add( this.controlPoint.position );
+
+ }
+
+ if ( this.controlPoint.backTangent ) {
+
+ this.controlPoint.backTangent.position.set(
+ Math.cos( this.controlPoint.hdg ),
+ Math.sin( this.controlPoint.hdg ),
+ CURVE_Y
+ ).multiplyScalar( -this.controlPoint.backTangent.length )
+ .add( this.controlPoint.position );
+
+ }
+
+ this.controlPoint.updateTangentLine();
+
+ }
+
+}
diff --git a/src/app/modules/three-js/objects/tv-material.model.ts b/src/app/modules/three-js/objects/tv-material.model.ts
new file mode 100644
index 00000000..6a90ca67
--- /dev/null
+++ b/src/app/modules/three-js/objects/tv-material.model.ts
@@ -0,0 +1,337 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { MeshStandardMaterial, MeshStandardMaterialParameters, Math, Texture } from 'three';
+import { AppService } from 'app/core/services/app.service';
+
+export class TvMaterial extends MeshStandardMaterial {
+
+ public mapGuid: string;
+ public roughnessMapGuid: string;
+ public normalMapGuid: string;
+ public aoMapGuid: string;
+ public displacementMapGuid: string;
+
+
+ constructor ( public guid: string, parameters?: MeshStandardMaterialParameters ) {
+ super( parameters )
+ }
+
+ static new ( name = 'NewMaterial' ) {
+
+ return new TvMaterial( Math.generateUUID(), { name: name } );
+
+ }
+
+ static parseString ( value: string ): TvMaterial {
+
+ return this.parseJSON( JSON.parse( value ) );
+
+ }
+
+ static parseJSON ( json: any ): TvMaterial {
+
+ function getTexture ( guid ) {
+
+ return AppService.assets.getInstance( guid ) as Texture;
+
+ }
+
+ var material = new TvMaterial( Math.generateUUID() );
+
+ if ( json.uuid !== undefined ) material.uuid = json.uuid;
+ if ( json.name !== undefined ) material.name = json.name;
+ if ( json.color !== undefined ) material.color.setHex( json.color );
+ if ( json.roughness !== undefined ) material.roughness = json.roughness;
+ if ( json.metalness !== undefined ) material.metalness = json.metalness;
+ if ( json.emissive !== undefined ) material.emissive.setHex( json.emissive );
+ // if ( json.specular !== undefined ) material.specular.setHex( json.specular );
+ // if ( json.shininess !== undefined ) material.shininess = json.shininess;
+ // if ( json.clearCoat !== undefined ) material.clearCoat = json.clearCoat;
+ // if ( json.clearCoatRoughness !== undefined ) material.clearCoatRoughness = json.clearCoatRoughness;
+ if ( json.vertexColors !== undefined ) material.vertexColors = json.vertexColors;
+ if ( json.fog !== undefined ) material.fog = json.fog;
+ if ( json.flatShading !== undefined ) material.flatShading = json.flatShading;
+ if ( json.blending !== undefined ) material.blending = json.blending;
+ // if ( json.combine !== undefined ) material.combine = json.combine;
+ if ( json.side !== undefined ) material.side = json.side;
+ if ( json.opacity !== undefined ) material.opacity = json.opacity;
+ if ( json.transparent !== undefined ) material.transparent = json.transparent;
+ if ( json.alphaTest !== undefined ) material.alphaTest = json.alphaTest;
+ if ( json.depthTest !== undefined ) material.depthTest = json.depthTest;
+ if ( json.depthWrite !== undefined ) material.depthWrite = json.depthWrite;
+ if ( json.colorWrite !== undefined ) material.colorWrite = json.colorWrite;
+ if ( json.wireframe !== undefined ) material.wireframe = json.wireframe;
+ if ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth;
+ // if ( json.wireframeLinecap !== undefined ) material.wireframeLinecap = json.wireframeLinecap;
+ // if ( json.wireframeLinejoin !== undefined ) material.wireframeLinejoin = json.wireframeLinejoin;
+
+ // if ( json.rotation !== undefined ) material.rotation = json.rotation;
+
+ // if ( json.linewidth !== 1 ) material.linewidth = json.linewidth;
+ // if ( json.dashSize !== undefined ) material.dashSize = json.dashSize;
+ // if ( json.gapSize !== undefined ) material.gapSize = json.gapSize;
+ // if ( json.scale !== undefined ) material.scale = json.scale;
+
+ if ( json.polygonOffset !== undefined ) material.polygonOffset = json.polygonOffset;
+ if ( json.polygonOffsetFactor !== undefined ) material.polygonOffsetFactor = json.polygonOffsetFactor;
+ if ( json.polygonOffsetUnits !== undefined ) material.polygonOffsetUnits = json.polygonOffsetUnits;
+
+ if ( json.skinning !== undefined ) material.skinning = json.skinning;
+ if ( json.morphTargets !== undefined ) material.morphTargets = json.morphTargets;
+ if ( json.dithering !== undefined ) material.dithering = json.dithering;
+
+ if ( json.visible !== undefined ) material.visible = json.visible;
+ if ( json.userData !== undefined ) material.userData = json.userData;
+
+ // maps
+ if ( json.mapGuid !== undefined ) {
+ material.mapGuid = json.mapGuid;
+ material.map = getTexture( json.mapGuid );
+ }
+
+ if ( json.roughnessMapGuid !== undefined ) {
+ material.roughnessMapGuid = json.roughnessMapGuid;
+ material.roughnessMap = getTexture( json.roughnessMapGuid );
+ }
+
+ if ( json.normalMapGuid !== undefined ) {
+ material.normalMapGuid = json.normalMapGuid;
+ material.normalMap = getTexture( json.normalMapGuid );
+ }
+
+ if ( json.aoMapGuid !== undefined ) {
+ material.aoMapGuid = json.aoMapGuid;
+ material.aoMap = getTexture( json.aoMapGuid );
+ }
+
+ if ( json.displacementMapGuid !== undefined ) {
+ material.displacementMapGuid = json.displacementMapGuid;
+ material.displacementMap = getTexture( json.displacementMapGuid );
+ }
+
+
+ // if ( json.alphaMap !== undefined ) {
+
+ // material.alphaMap = getTexture( json.alphaMap );
+ // material.transparent = true;
+
+ // }
+
+ // if ( json.bumpMap !== undefined ) material.bumpMap = getTexture( json.bumpMap );
+ // if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale;
+
+ // if ( json.normalMap !== undefined ) material.normalMap = getTexture( json.normalMap );
+
+ // if ( json.displacementMap !== undefined ) material.displacementMap = getTexture( json.displacementMap );
+ // if ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale;
+ // if ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias;
+
+ // if ( json.roughnessMap !== undefined ) material.roughnessMap = getTexture( json.roughnessMap );
+ // if ( json.metalnessMap !== undefined ) material.metalnessMap = getTexture( json.metalnessMap );
+
+ // if ( json.emissiveMap !== undefined ) material.emissiveMap = getTexture( json.emissiveMap );
+ // if ( json.emissiveIntensity !== undefined ) material.emissiveIntensity = json.emissiveIntensity;
+
+ // // if ( json.specularMap !== undefined ) material.specularMap = getTexture( json.specularMap );
+
+ // if ( json.envMap !== undefined ) material.envMap = getTexture( json.envMap );
+ // if ( json.envMapIntensity !== undefined ) material.envMapIntensity = json.envMapIntensity;
+
+ // // if ( json.reflectivity !== undefined ) material.reflectivity = json.reflectivity;
+
+ // if ( json.lightMap !== undefined ) material.lightMap = getTexture( json.lightMap );
+ // if ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity;
+
+ // if ( json.aoMap !== undefined ) material.aoMap = getTexture( json.aoMap );
+ // if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity;
+
+ // // if ( json.gradientMap !== undefined ) material.gradientMap = getTexture( json.gradientMap );
+
+ return material;
+ }
+
+ toJSON ( meta?: any ): any {
+
+ var isRoot = ( meta === undefined || typeof meta === 'string' );
+
+ if ( isRoot ) {
+
+ meta = {
+ textures: {},
+ images: {}
+ };
+
+ }
+
+ var data: any = {
+ version: 0.1,
+ };
+
+ // standard Material serialization
+ data.uuid = this.uuid;
+ data.type = this.type;
+
+ if ( this.name !== '' ) data.name = this.name;
+
+ if ( this.color ) data.color = this.color.getHex();
+
+ if ( this.roughness !== undefined ) data.roughness = this.roughness;
+ if ( this.metalness !== undefined ) data.metalness = this.metalness;
+
+ if ( this.emissive ) data.emissive = this.emissive.getHex();
+ if ( this.emissiveIntensity !== 1 ) data.emissiveIntensity = this.emissiveIntensity;
+
+ // if ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex();
+ // if ( this.shininess !== undefined ) data.shininess = this.shininess;
+ // if ( this.clearCoat !== undefined ) data.clearCoat = this.clearCoat;
+ // if ( this.clearCoatRoughness !== undefined ) data.clearCoatRoughness = this.clearCoatRoughness;
+
+ if ( this.mapGuid ) data.mapGuid = this.mapGuid;
+
+ if ( this.roughnessMapGuid ) data.roughnessMapGuid = this.roughnessMapGuid;
+
+ if ( this.normalMapGuid ) data.normalMapGuid = this.normalMapGuid;
+
+ if ( this.aoMapGuid ) data.aoMapGuid = this.aoMapGuid;
+
+ if ( this.displacementMapGuid ) data.displacementMapGuid = this.displacementMapGuid;
+
+ // if ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid;
+ // if ( this.lightMap && this.lightMap.isTexture ) data.lightMap = this.lightMap.toJSON( meta ).uuid;
+
+ // if ( this.aoMap && this.aoMap.isTexture ) {
+
+ // data.aoMap = this.aoMap.toJSON( meta ).uuid;
+ // data.aoMapIntensity = this.aoMapIntensity;
+
+ // }
+
+ // if ( this.bumpMap && this.bumpMap.isTexture ) {
+
+ // data.bumpMap = this.bumpMap.toJSON( meta ).uuid;
+ // data.bumpScale = this.bumpScale;
+
+ // }
+
+ // if ( this.normalMap && this.normalMap.isTexture ) {
+
+ // data.normalMap = this.normalMap.toJSON( meta ).uuid;
+ // data.normalMapType = this.normalMapType;
+ // data.normalScale = this.normalScale.toArray();
+
+ // }
+
+ // if ( this.displacementMap && this.displacementMap.isTexture ) {
+
+ // data.displacementMap = this.displacementMap.toJSON( meta ).uuid;
+ // data.displacementScale = this.displacementScale;
+ // data.displacementBias = this.displacementBias;
+
+ // }
+
+ // if ( this.roughnessMap && this.roughnessMap.isTexture ) data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid;
+ // if ( this.metalnessMap && this.metalnessMap.isTexture ) data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid;
+
+ // if ( this.emissiveMap && this.emissiveMap.isTexture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid;
+ // if ( this.specularMap && this.specularMap.isTexture ) data.specularMap = this.specularMap.toJSON( meta ).uuid;
+
+ // if ( this.envMap && this.envMap.isTexture ) {
+
+ // data.envMap = this.envMap.toJSON( meta ).uuid;
+ // data.reflectivity = this.reflectivity; // Scale behind envMap
+
+ // if ( this.combine !== undefined ) data.combine = this.combine;
+ // if ( this.envMapIntensity !== undefined ) data.envMapIntensity = this.envMapIntensity;
+
+ // }
+
+ // if ( this.gradientMap && this.gradientMap.isTexture ) {
+
+ // data.gradientMap = this.gradientMap.toJSON( meta ).uuid;
+
+ // }
+
+ // if ( this.size !== undefined ) data.size = this.size;
+ // if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation;
+
+ // if ( this.blending !== NormalBlending ) data.blending = this.blending;
+ // if ( this.flatShading === true ) data.flatShading = this.flatShading;
+ // if ( this.side !== FrontSide ) data.side = this.side;
+ // if ( this.vertexColors !== NoColors ) data.vertexColors = this.vertexColors;
+
+ // if ( this.opacity < 1 ) data.opacity = this.opacity;
+ // if ( this.transparent === true ) data.transparent = this.transparent;
+
+ data.depthFunc = this.depthFunc;
+ data.depthTest = this.depthTest;
+ data.depthWrite = this.depthWrite;
+
+ // // rotation (SpriteMaterial)
+ // if ( this.rotation !== 0 ) data.rotation = this.rotation;
+
+ // if ( this.polygonOffset === true ) data.polygonOffset = true;
+ // if ( this.polygonOffsetFactor !== 0 ) data.polygonOffsetFactor = this.polygonOffsetFactor;
+ // if ( this.polygonOffsetUnits !== 0 ) data.polygonOffsetUnits = this.polygonOffsetUnits;
+
+ // if ( this.linewidth !== 1 ) data.linewidth = this.linewidth;
+ // if ( this.dashSize !== undefined ) data.dashSize = this.dashSize;
+ // if ( this.gapSize !== undefined ) data.gapSize = this.gapSize;
+ // if ( this.scale !== undefined ) data.scale = this.scale;
+
+ // if ( this.dithering === true ) data.dithering = true;
+
+ // if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest;
+ // if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha;
+
+ // if ( this.wireframe === true ) data.wireframe = this.wireframe;
+ // if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth;
+ // if ( this.wireframeLinecap !== 'round' ) data.wireframeLinecap = this.wireframeLinecap;
+ // if ( this.wireframeLinejoin !== 'round' ) data.wireframeLinejoin = this.wireframeLinejoin;
+
+ // if ( this.morphTargets === true ) data.morphTargets = true;
+ // if ( this.skinning === true ) data.skinning = true;
+
+ // if ( this.visible === false ) data.visible = false;
+
+ if ( JSON.stringify( this.userData ) !== '{}' ) data.userData = this.userData;
+
+ // TODO: Copied from Object3D.toJSON
+
+ function extractFromCache ( cache ) {
+
+ var values = [];
+
+ for ( var key in cache ) {
+
+ var data = cache[ key ];
+ delete data.metadata;
+ values.push( data );
+
+ }
+
+ return values;
+
+ }
+
+ if ( isRoot ) {
+
+ var textures = extractFromCache( meta.textures );
+ var images = extractFromCache( meta.images );
+
+ if ( textures.length > 0 ) data.textures = textures;
+ if ( images.length > 0 ) data.images = images;
+
+ }
+
+ return data;
+
+ }
+
+ toJSONString (): string {
+
+ return JSON.stringify( this.toJSON(), null, 2 );
+
+ }
+}
\ No newline at end of file
diff --git a/src/app/modules/three-js/pointer-guides/pointer-guides.component.html b/src/app/modules/three-js/pointer-guides/pointer-guides.component.html
new file mode 100644
index 00000000..4b7973b3
--- /dev/null
+++ b/src/app/modules/three-js/pointer-guides/pointer-guides.component.html
@@ -0,0 +1,4 @@
+
+
diff --git a/src/app/modules/three-js/pointer-guides/pointer-guides.component.ts b/src/app/modules/three-js/pointer-guides/pointer-guides.component.ts
new file mode 100644
index 00000000..4831fd71
--- /dev/null
+++ b/src/app/modules/three-js/pointer-guides/pointer-guides.component.ts
@@ -0,0 +1,96 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Component, OnInit } from '@angular/core';
+import * as THREE from 'three';
+import { ThreeService } from 'app/modules/three-js/three.service';
+import { AppService } from 'app/core/services/app.service';
+import { PointerMoveData } from 'app/events/pointer-event-data';
+
+@Component( {
+ selector: 'app-pointer-guides',
+ templateUrl: './pointer-guides.component.html'
+} )
+export class PointerGuidesComponent implements OnInit {
+
+ draw: boolean = false;
+
+ private CROSSHAIR_COLOR = 0x000000;
+
+ private lineMaterial = new THREE.LineBasicMaterial( {
+ color: this.CROSSHAIR_COLOR,
+ linewidth: 10,
+ } );
+
+ private lineGeometry = new THREE.BufferGeometry();
+ private verticalLine = new THREE.Line( this.lineGeometry, this.lineMaterial );
+ private horizontalLine = new THREE.Line( this.lineGeometry, this.lineMaterial );
+
+ constructor (
+ private engineService: ThreeService,
+ ) {
+ }
+
+ ngOnInit () {
+
+ if ( this.draw ) {
+
+ AppService.eventSystem.pointerMoved.subscribe( e => this.onPointerMoved( e ) );
+
+ }
+
+ }
+
+ onPointerMoved ( e: PointerMoveData ): any {
+
+ this.updateLineHelpers( e );
+
+ }
+
+ updateLineHelpers ( data: PointerMoveData ): void {
+
+ this.updateCursorType();
+
+ this.engineService.remove( this.verticalLine );
+ this.engineService.remove( this.horizontalLine );
+
+ // DRAW IF CURSOR IS ON IMAGE
+ if ( data ) {
+
+ const point = data.point;
+
+ var geometry = new THREE.Geometry();
+ geometry.vertices.push( new THREE.Vector3( point.x, -10000, 1 ) );
+ geometry.vertices.push( new THREE.Vector3( point.x, 10000, 1 ) );
+ this.horizontalLine = new THREE.Line( geometry, this.lineMaterial );
+ this.horizontalLine.computeLineDistances();
+
+ var geometry = new THREE.Geometry();
+ geometry.vertices.push( new THREE.Vector3( -10000, point.y, 1 ) );
+ geometry.vertices.push( new THREE.Vector3( 10000, point.y, 1 ) );
+ this.verticalLine = new THREE.Line( geometry, this.lineMaterial );
+ this.verticalLine.computeLineDistances();
+
+ this.engineService.add( this.verticalLine );
+ this.engineService.add( this.horizontalLine );
+
+ }
+
+ }
+
+ updateCursorType (): any {
+
+ // this.engineService.canvas.style.cursor = 'crosshair';
+
+ // if ( this.inputService.isShiftKeyDown ) {
+ // // this.engineService.canvas.style.cursor = 'grab';
+ // } else if ( this.engineService.cursorOnBox && !this.drawing ) {
+ // this.engineService.canvas.style.cursor = 'move';
+ // } else {
+ // this.engineService.canvas.style.cursor = 'crosshair';
+ // }
+
+ }
+
+}
diff --git a/src/app/modules/three-js/primitives.ts b/src/app/modules/three-js/primitives.ts
new file mode 100644
index 00000000..9c58e0a5
--- /dev/null
+++ b/src/app/modules/three-js/primitives.ts
@@ -0,0 +1,124 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { COLOR } from '../../shared/utils/colors.service';
+import * as THREE from 'three';
+import { Mesh, Object3D, PlaneBufferGeometry, Vector2, Vector3 } from 'three';
+import { SceneService } from '../../core/services/scene.service';
+
+export enum PrimitiveType {
+ CUBE = 1,
+ PLANE = 2,
+}
+
+export class BoundingBoxObject {
+
+ public width: number = 10;
+ public height: number = 10;
+ public geometry = new PlaneBufferGeometry( this.width, this.height );
+ private color = COLOR.DARKGRAY;
+ private opacity = 0.4;
+ private material = new THREE.MeshBasicMaterial( {
+ color: this.color,
+ opacity: this.opacity,
+ transparent: true
+ } );
+ public mesh = new Mesh( this.geometry, this.material );
+
+ // BORDER
+ private borderGeometry = new THREE.EdgesGeometry( this.geometry );
+ private borderMaterial = new THREE.LineBasicMaterial( { color: this.color } );
+ private border = new THREE.LineSegments( this.borderGeometry, this.borderMaterial );
+ private borderAdded = false;
+
+ constructor ( start?: Vector3, end?: Vector3 ) {
+
+ this.mesh.userData.is_annotation = true;
+ this.mesh.userData.uuid = this.mesh.uuid;
+
+ SceneService.add( this.mesh, true );
+
+ if ( start && end ) this.updateGeometry( start, end );
+ }
+
+ static addBoxInfo ( box: Object3D, start: Vector3, end: THREE.Vector3, size: Vector2 ): any {
+
+ box.userData.is_annotation = true;
+ box.userData.type = 'box';
+
+ box.userData.width = size.x;
+ box.userData.height = size.y;
+
+ box.userData.startX = start.x;
+ box.userData.startY = start.y;
+
+ box.userData.endX = end.x;
+ box.userData.endY = end.y;
+ }
+
+ static getDimensions ( p1: Vector3, p2: Vector3 ): Vector2 {
+
+ let width = Math.abs( p1.x - p2.x );
+ let height = Math.abs( p1.y - p2.y );
+
+ width = Math.round( Math.max( width, 10 ) );
+ height = Math.round( Math.max( height, 10 ) );
+
+ return new Vector2(
+ width,
+ height
+ );
+ }
+
+ setColor ( hexColorString: string ): any {
+ this.material.color.set( hexColorString );
+ this.borderMaterial.color.set( hexColorString );
+ }
+
+ updateGeometry ( start: Vector3, end: Vector3 ) {
+
+ let size = BoundingBoxObject.getDimensions( start, end );
+
+ this.width = size.x;
+ this.height = size.y;
+
+ this.geometry = new PlaneBufferGeometry( size.x, size.y, 1, 1 );
+
+ this.mesh.geometry.dispose();
+
+ this.mesh.geometry = this.geometry;
+
+ this.setPosition( this.mesh, start, end, size );
+
+ this.updateBorderGeometry( start, end, size );
+
+ BoundingBoxObject.addBoxInfo( this.mesh, start, end, size );
+ }
+
+ updateBorderGeometry ( start, end, size ) {
+
+ this.borderGeometry = new THREE.EdgesGeometry( this.geometry );
+
+ this.border.geometry.dispose();
+ this.border.geometry = this.borderGeometry;
+
+ if ( !this.borderAdded ) {
+ this.mesh.add( this.border );
+ this.borderAdded = true;
+ }
+ }
+
+ setPosition ( object, start: Vector3, end: Vector3, dimensions: Vector2 ) {
+
+ if ( end.x < start.x ) dimensions.x *= -1;
+ if ( end.y < start.y ) dimensions.y *= -1;
+
+ object.position.set( start.x + (dimensions.x / 2), start.y + (dimensions.y / 2), 0.1 );
+ }
+
+ destroy () {
+ SceneService.remove( this.mesh, true );
+ }
+
+}
diff --git a/src/app/modules/three-js/three-js.module.ts b/src/app/modules/three-js/three-js.module.ts
new file mode 100644
index 00000000..4ac23733
--- /dev/null
+++ b/src/app/modules/three-js/three-js.module.ts
@@ -0,0 +1,27 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { ViewportComponent } from './viewport/viewport.component';
+import { PointerGuidesComponent } from './pointer-guides/pointer-guides.component';
+import { SharedModule } from 'app/shared/shared.module';
+import { ColorPickerModule } from 'ngx-color-picker';
+import { MatInputModule } from '@angular/material';
+import { TextureFieldComponent } from './inspectors/texture-field/texture-field.component';
+
+@NgModule( {
+ declarations: [ ViewportComponent, PointerGuidesComponent, TextureFieldComponent ],
+ imports: [
+ CommonModule,
+ SharedModule,
+ ColorPickerModule,
+ MatInputModule
+ ],
+ exports: [
+ ViewportComponent,
+ ]
+} )
+export class ThreeJsModule {
+}
diff --git a/src/app/modules/three-js/three.service.ts b/src/app/modules/three-js/three.service.ts
new file mode 100644
index 00000000..cb40054f
--- /dev/null
+++ b/src/app/modules/three-js/three.service.ts
@@ -0,0 +1,567 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Injectable } from '@angular/core';
+
+import * as THREE from 'three';
+import { Material, Object3D, OrthographicCamera, PerspectiveCamera, WebGLRenderer } from 'three';
+import './EnableThreeExamples';
+import 'three/examples/js/controls/DragControls';
+import 'three/examples/js/controls/TransformControls';
+
+import 'three/examples/js/postprocessing/EffectComposer';
+import 'three/examples/js/postprocessing/RenderPass';
+import 'three/examples/js/postprocessing/ShaderPass';
+import 'three/examples/js/postprocessing/OutlinePass';
+import 'three/examples/js/shaders/CopyShader';
+
+import { Maths } from 'app/utils/maths';
+import { SetPositionCommand } from './commands/set-position-command';
+import { CommandHistory } from 'app/services/command-history';
+import { IEngine } from '../../core/services/IEngine';
+import { SceneService } from '../../core/services/scene.service';
+import { IViewportController } from './objects/i-viewport-controller';
+import { OrbitControls } from './objects/orbit-controls';
+import { COLOR } from 'app/shared/utils/colors.service';
+
+@Injectable( {
+ providedIn: 'root'
+} )
+export class ThreeService implements IEngine {
+
+
+ public canvas: HTMLCanvasElement;
+ public renderer: THREE.WebGLRenderer;
+ public mouse: THREE.Vector2 = new THREE.Vector2;
+ public image: THREE.Mesh;
+
+ public static controls: IViewportController;
+
+ public canvasWidth: number;
+ public canvasHeight: number;
+ public leftOffset: number;
+ public topOffset: number;
+ public ORTHO_DRIVER = 4;
+
+ private currentCameraIndex = 0;
+ private cameras: THREE.Camera[] = [];
+ private composer: THREE.EffectComposer;
+ private transformControls: THREE.TransformControls;
+ private light: THREE.AmbientLight;
+ private objectPositionOnDown: THREE.Vector3 = null;
+ static bgForClicks: THREE.Mesh;
+
+ constructor () {
+
+ }
+
+ public get camera () {
+ return this.cameras[ this.currentCameraIndex ];
+ }
+
+ setupScene ( canvas: HTMLCanvasElement, renderer: WebGLRenderer ): void {
+
+ this.canvas = canvas;
+ this.renderer = renderer;
+
+ // const box = this.getCanvasBounds();
+ // this.CANVAS_WIDTH = box.width === 0 ? this.CANVAS_WIDTH : box.width;
+ // this.CANVAS_HEIGHT = box.height === 0 ? this.CANVAS_HEIGHT : box.height;CO
+ SceneService.scene.background = new THREE.Color( COLOR.BLACK );// new THREE.Color( 0x2e2e2e );
+
+ this.createCameras();
+
+ this.createSceneHelpers();
+ }
+
+ addDirectionalLight () {
+
+ const directionaLight = new THREE.DirectionalLight( '0xffffff', 1 );
+
+ directionaLight.position.set( 5, 10, 7.5 );
+
+ SceneService.addHelper( directionaLight );
+
+ SceneService.addHelper( directionaLight.target );
+
+ const ambientLight = new THREE.AmbientLight( 0xE6E6E6, 1 );
+
+ SceneService.addHelper( ambientLight );
+
+ }
+
+ createControls (): void {
+
+ ThreeService.controls = OrbitControls.getNew( this.camera, this.canvas );
+ // ThreeService.controls = EditorControls.getNew( this.camera, this.canvas );
+
+ }
+
+ createTransformControls (): void {
+
+ const self: ThreeService = this;
+
+ this.transformControls = new THREE.TransformControls( this.camera, this.canvas );
+
+ this.transformControls.addEventListener( 'dragging-changed', function ( event ) {
+
+ ThreeService.controls.enabled = !event.value;
+
+ } );
+
+ this.transformControls.addEventListener( 'mouseDown', () => {
+
+ var object = self.transformControls.object;
+
+
+ this.objectPositionOnDown = object.position.clone();
+ // this.objectRotationOnDown = object.rotation.clone();
+ // this.objectScaleOnDown = object.scale.clone();
+
+ ThreeService.controls.enabled = false;
+
+ } );
+
+ this.transformControls.addEventListener( 'mouseUp', function () {
+
+ var object = self.transformControls.object;
+
+ if ( object !== undefined ) {
+
+ switch ( self.transformControls.getMode() ) {
+
+ case 'translate':
+
+ if ( !self.objectPositionOnDown.equals( object.position ) ) {
+
+ CommandHistory.execute( new SetPositionCommand( object, object.position, self.objectPositionOnDown ) );
+
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ }
+
+ ThreeService.controls.enabled = true;
+
+ } );
+
+
+ SceneService.addHelper( this.transformControls );
+
+ }
+
+ createGridHelper (): void {
+
+ var gridHelper = new THREE.GridHelper( 1000, 100 );
+
+ ( gridHelper.material as Material ).transparent = true;
+ ( gridHelper.material as Material ).opacity = 0.2;
+ ( gridHelper.material as Material ).needsUpdate = false;
+
+ // to adjust with up Z
+ gridHelper.rotateX( Maths.Deg2Rad * 90 );
+
+ SceneService.addHelper( gridHelper );
+
+ }
+
+ createSceneHelpers (): any {
+
+ this.addDirectionalLight();
+
+ this.setupPostProcessing();
+
+ this.createControls();
+
+ this.createDragControls();
+
+ this.createGridHelper();
+
+ this.createTransformControls();
+
+ this.createBackgroundPlaneForClicks();
+
+ this.addAxesHelper();
+ }
+
+ createBackgroundPlaneForClicks () {
+
+ ThreeService.bgForClicks = new THREE.Mesh( new THREE.PlaneBufferGeometry( 10000, 10000 ), new THREE.MeshBasicMaterial( {
+ color: 0xFFFFFF,
+ transparent: true,
+ opacity: 0
+ } ) );
+
+ ThreeService.bgForClicks.name = 'bgForClicks';
+
+ SceneService.add( ThreeService.bgForClicks, true );
+ }
+
+ setupPostProcessing (): any {
+
+ // // TODO : Move this to component, listen to resize to fix blurriness
+ //
+ // // postprocessing
+ // this.composer = new THREE.EffectComposer( this.renderer );
+ //
+ // let renderPass = new THREE.RenderPass( this.scene, this.camera );
+ //
+ //
+ // let copyPass = new THREE.ShaderPass( THREE.CopyShader );
+ // copyPass.renderToScreen = false;
+ //
+ // let res = new Vector2( this.CANVAS_WIDTH, this.CANVAS_HEIGHT );
+ // let outlinePass = new OutlinePass( res, this.scene, this.camera );
+ //
+ // this.composer.addPass( renderPass );
+ // // this.composer.addPass( copyPass );
+ // // this.composer.addPass( outlinePass );
+
+ }
+
+ createDragControls (): any {
+
+
+ }
+
+ // addImageSprite (): any {
+
+ // const self: ThreeService = this;
+
+ // // const image = "assets/checkered.png";
+ // const image = 'assets/buildings.jpeg';
+
+ // var map = new THREE.TextureLoader().load( image, function ( texture ) {
+
+ // var material = new THREE.SpriteMaterial( { map: texture, color: 0xffffff } );
+ // var sprite = new THREE.Sprite( material );
+
+ // sprite.scale.set( texture.image.width, 2622, 1 );
+
+ // sprite.position.set( texture.image.width / 2, texture.image.height / 2, 0 );
+ // // self.camera.position.set( texture.image.width / 2, texture.image.height / 2, 20 );
+ // // self.camera.lookAt( new THREE.Vector3( texture.image.width / 2, texture.image.height / 2, 0 ) );
+ // // self.camera.up.set( 0, 1, 0 );
+ // // self.camera.lookAt( 1311, 1311, 0 );
+ // // self.camera.position.set( 600, 0, 0 );
+ // // self.camera.position.z = 20;
+
+ // self.camera.updateProjectionMatrix();
+
+ // self.add( sprite, true );
+
+ // } );
+
+ // }
+
+ // addImageScreen (): any {
+ //
+ // const self: ThreeService = this;
+ //
+ // // const url = "http://true-label.test/images/checkered.png";
+ // // const url = "https://picsum.photos/1200/900";
+ // // const url = "assets/highway.jpg";
+ // const url = 'assets/pedestrians/Frame_ (1).png';
+ // // const url = "assets/buildings.jpeg";
+ // // const url = "assets/sample.png";
+ //
+ // // load a texture, set wrap mode to repeat
+ // const textureLoader = new THREE.TextureLoader();
+ //
+ // textureLoader.crossOrigin = '';
+ //
+ // textureLoader.load( url, function ( texture ) {
+ //
+ // texture.wrapS = THREE.RepeatWrapping;
+ // texture.wrapT = THREE.RepeatWrapping;
+ //
+ // var geometry = new THREE.PlaneGeometry( texture.image.width, texture.image.height );
+ // var material = new THREE.MeshBasicMaterial( { map: texture, side: THREE.DoubleSide } );
+ //
+ // self.image = new THREE.Mesh( geometry, material );
+ //
+ // self.image.userData.is_image = true;
+ //
+ // self.image.position.set( texture.image.width / 2, texture.image.height / 2, 0 );
+ //
+ // self.camera.position.set( texture.image.width / 2, texture.image.height / 2, 20 );
+ //
+ // self.orbitControls.target.set( texture.image.width / 2, texture.image.height / 2, 0 );
+ //
+ // self.camera.updateProjectionMatrix();
+ //
+ // self.add( self.image, true );
+ //
+ // } );
+ // }
+
+ animate (): void {
+ // window.addEventListener( 'DOMContentLoaded', () => {
+ // this.render();
+ // } );
+
+ // window.addEventListener( 'resize', () => {
+ // this.resize();
+ // } );
+ }
+
+ // render () {
+ // requestAnimationFrame( () => {
+ // this.render();
+ // } );
+
+ // this.orbitControls.update();
+
+ // // update the picking ray with the camera and mouse position
+ // this.raycaster.setFromCamera( this.mouse, this.camera );
+
+ // // calculate objects intersecting the picking ray
+ // let intersects = this.raycaster.intersectObjects( this.raycastableObjects, false );
+
+ // // intersects = this.raycaster.intersectObjects( this.unclickableObjects, false );
+
+ // // Debug.log( intersects.length );
+
+ // // this.cursorOnBox = false;
+
+ // if ( intersects.length > 0 ) {
+
+ // // Debug.log( intersects.length );
+
+ // // this.sphereInter.visible = true;
+ // // this.sphereInter.position.copy( intersects[0].point );
+
+ // this.pointerPosition = intersects[0].point;
+
+ // this.cursorOnImage = true;
+ // this.pixelPosition.x = intersects[0].point.x;
+ // this.pixelPosition.y = intersects[0].point.y;
+
+ // if ( intersects[0].object.userData.is_annotation ) {
+ // // this.dragControls['enabled'] = true;
+ // this.cursorOnBox = true;
+ // this.objectInFocus = intersects[0].object;
+ // this.editorService.mouseOverAnnotationObject.emit( intersects[0].object.id );
+
+ // } else if ( intersects[0].object.userData.is_button ) {
+
+ // this.cursorOnBox = false;
+
+ // // this.dragControls['enabled'] = false;
+
+ // if ( intersects[0].object.userData.is_delete_button ) this.editorService.mouseOverDeleteButton.emit( 1 );
+
+ // } else {
+
+ // // this.dragControls['enabled'] = false;
+ // this.cursorOnBox = false;
+ // this.objectInFocus = null;
+
+ // }
+
+ // // Debug.log( this.cursorOnBox );
+
+ // } else {
+
+ // // this.sphereInter.visible = false;
+
+ // this.cursorOnImage = false;
+ // this.pixelPosition.x = 0;
+ // this.pixelPosition.y = 0;
+
+ // }
+
+ // // for ( var i = 0; i < intersects.length; i++ ) {
+ // // intersects[i].object.material.color.set( 0xff0000 );
+ // // }
+
+ // // this.renderer.render( this.scene, this.camera );
+
+ // }
+
+
+ /**
+ *
+ * @param object
+ * @param raycasting
+ * @deprecated use SceneService.add instead
+ */
+ add ( object: THREE.Object3D, raycasting = false ): any {
+
+ SceneService.add( object, raycasting );
+ }
+
+ remove ( object: THREE.Object3D, raycasting = false ): any {
+
+ SceneService.remove( object, raycasting );
+
+ }
+
+ public focus ( obj: THREE.Object3D ) {
+
+ // move the camera on top of the object
+ this.camera.position.setX( obj.position.x );
+ this.camera.position.setY( obj.position.y );
+
+ // focus the camera on the object
+ this.camera.lookAt( obj.position );
+
+ // change the target position for controls
+ ThreeService.controls.setTarget( obj.position );
+ ThreeService.controls.update();
+
+ ( this.camera as any ).updateProjectionMatrix();
+ }
+
+ reset () {
+
+ SceneService.reset();
+
+ }
+
+ /**
+ * Public methods
+ */
+
+ public select ( obj: Object3D ) {
+
+ this.transformControls.attach( obj );
+
+ }
+
+ public deselect () {
+
+ this.transformControls.detach();
+
+ }
+
+ public changeCamera () {
+
+ if ( this.currentCameraIndex + 1 >= this.cameras.length ) {
+
+ this.currentCameraIndex = 0;
+
+ } else {
+
+ this.currentCameraIndex++;
+
+ }
+
+ this.transformControls.detach();
+ this.transformControls.object = this.camera;
+
+ ThreeService.controls.setCamera( this.camera );
+
+ if ( this.camera[ 'isOrthographicCamera' ] ) {
+
+ ThreeService.controls.setScreenSpaceEnabled( true );
+ ThreeService.controls.setRotateEnabled( false );
+
+ } else if ( this.camera[ 'isPerspectiveCamera' ] ) {
+
+ ThreeService.controls.setScreenSpaceEnabled( false );
+ ThreeService.controls.setRotateEnabled( true );
+
+ }
+
+ ThreeService.controls.reset();
+ }
+
+ onWindowResized () {
+
+ const width = this.canvasWidth;
+ const height = this.canvasHeight;
+
+ this.cameras.forEach( camera => {
+
+ if ( camera[ 'isOrthographicCamera' ] ) {
+
+ ( camera as OrthographicCamera ).left = width / -this.ORTHO_DRIVER;
+ ( camera as OrthographicCamera ).right = width / this.ORTHO_DRIVER;
+ ( camera as OrthographicCamera ).top = height / this.ORTHO_DRIVER;
+ ( camera as OrthographicCamera ).bottom = height / -this.ORTHO_DRIVER;
+
+ ( camera as OrthographicCamera ).updateProjectionMatrix();
+
+ } else if ( camera[ 'isPerspectiveCamera' ] ) {
+
+ ( camera as PerspectiveCamera ).aspect = width / height;
+
+ ( camera as PerspectiveCamera ).updateProjectionMatrix();
+ }
+
+
+ } );
+
+ }
+
+ enableControls () {
+
+ ThreeService.controls.enabled = true;
+
+ }
+
+ disableControls () {
+
+ ThreeService.controls.enabled = false;
+
+ }
+
+ private createCameras () {
+
+ // higher near value >= 10 reduces the z fighting that
+ // happens in rendering road markings
+ const near = 1;
+ const far = 100000;
+
+ const width = 791.88;
+ const height = 606;
+ const otherDivider = 4;
+
+ const left = width / -otherDivider;
+ const right = width / otherDivider;
+ const top = height / otherDivider;
+ const bottom = height / -otherDivider;
+
+ const orthographicCamera = new THREE.OrthographicCamera( left, right, top, bottom, near, far );
+ orthographicCamera.position.set( 0, 0, 50 );
+ orthographicCamera.up.set( 0, 0, 1 );
+
+ const perspectiveCamera = new THREE.PerspectiveCamera( 50, width / height, near, far );
+ perspectiveCamera.position.set( 0, 5, 10 );
+ perspectiveCamera.up.set( 0, 0, 1 );
+
+ this.cameras.push( orthographicCamera );
+ this.cameras.push( perspectiveCamera );
+
+ for ( let i = 0; i < this.cameras.length; i++ ) {
+
+ this.cameras[ i ].lookAt( 0, 0, 0 );
+
+ SceneService.addHelper( this.cameras[ i ] );
+ }
+
+
+ if ( this.camera[ 'isOrthographicCamera' ] ) {
+
+ ( this.camera as OrthographicCamera ).updateProjectionMatrix();
+
+ } else if ( this.camera[ 'isPerspectiveCamera' ] ) {
+
+ ( this.camera as PerspectiveCamera ).updateProjectionMatrix();
+
+ }
+ }
+
+ private addAxesHelper () {
+
+ SceneService.addHelper( new THREE.AxesHelper( 3000 ) );
+
+ }
+}
diff --git a/src/app/modules/three-js/viewport/viewport.component.html b/src/app/modules/three-js/viewport/viewport.component.html
new file mode 100644
index 00000000..b46577b9
--- /dev/null
+++ b/src/app/modules/three-js/viewport/viewport.component.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+ {{ fps }} FPS
+
+
\ No newline at end of file
diff --git a/src/app/modules/three-js/viewport/viewport.component.ts b/src/app/modules/three-js/viewport/viewport.component.ts
new file mode 100644
index 00000000..8f4f3cc3
--- /dev/null
+++ b/src/app/modules/three-js/viewport/viewport.component.ts
@@ -0,0 +1,635 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { AfterViewInit, Component, ElementRef, HostListener, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
+import * as THREE from 'three';
+import { Intersection, Object3D, WebGLRenderer, OrthographicCamera, PerspectiveCamera } from 'three';
+import { ThreeService } from 'app/modules/three-js/three.service';
+import { InputService } from 'app/core/services/input.service';
+import { EventSystem } from 'app/events/event-system.service';
+import { MouseButton, PointerEventData } from 'app/events/pointer-event-data';
+import { SceneService } from '../../../core/services/scene.service';
+import { ImporterService } from 'app/services/importer.service';
+
+import * as Stats from 'stats.js';
+
+@Component( {
+ selector: 'app-viewport',
+ templateUrl: './viewport.component.html',
+ styles: [
+ `.app-viewport-stats {
+ top: 12px;
+ right: 10px;
+ color: white;
+ height: 38px;
+ padding: 0px 8px !important;
+ position: absolute;
+ width: auto !important;
+ }`
+ ]
+} )
+export class ViewportComponent implements OnInit, AfterViewInit, OnDestroy {
+
+ @Input( 'directionalLight' ) directionalLightEnabled: boolean = false;
+ @Input( 'showPointerCoordinates' ) showPointerCoordinates: boolean = false;
+
+ public beginTime;
+ public prevTime;
+ public frames = 0;
+ public fps;
+
+ CANVAS_WIDTH = 600;
+ CANVAS_HEIGHT = 600;
+
+ OFFSET_LEFT: number = 0;
+ OFFSET_TOP: number = 0;
+
+ selected: Object3D;
+ intersections: THREE.Intersection[] = [];
+ lastIntersection: THREE.Intersection;
+
+ @ViewChild( 'viewport' ) elementRef: ElementRef;
+
+ raycaster = new THREE.Raycaster;
+ mouse: THREE.Vector2 = new THREE.Vector2();
+
+ private animationId;
+ private renderer: WebGLRenderer;
+ private ORTHO_DRIVER = 4;
+ private stats: Stats;
+
+
+ constructor (
+ private threeService: ThreeService,
+ private inputService: InputService,
+ private eventSystem: EventSystem,
+ private importer: ImporterService,
+ ) {
+ this.render = this.render.bind( this );
+ }
+
+ get canvas (): HTMLCanvasElement {
+ return this.elementRef.nativeElement;
+ }
+
+ ngOnInit () {
+
+
+ }
+
+ // was used when multiple canvas instances were used
+ ngOnDestroy (): void {
+
+ this.canvas.remove();
+
+ this.renderer.dispose();
+
+ cancelAnimationFrame( this.animationId );
+ }
+
+ ngAfterViewInit (): void {
+
+ this.prevTime = ( performance || Date ).now();
+ this.beginTime = ( performance || Date ).now()
+
+ if ( this.detectWebgl() ) {
+
+ this.setupRenderer();
+
+ setTimeout( () => {
+
+ this.setCanvasSize();
+
+ }, 300 );
+
+ } else {
+
+ alert( 'Your Browser does not support WebGL. Please visit from a browser which supports WebGL.' );
+ }
+
+ }
+
+ setupRenderer () {
+
+ this.renderer = new THREE.WebGLRenderer( { alpha: false, antialias: true, precision: 'highp', stencil: false } );
+ this.renderer.setPixelRatio( window.devicePixelRatio );
+ this.renderer.setClearColor( 0xffffff, 1 );
+ this.renderer.autoClear = true;
+
+ this.raycaster = new THREE.Raycaster();
+ this.raycaster.linePrecision = 0.25;
+ this.raycaster.far = 10000;
+
+ ////////////////////////////////////
+
+ this.renderer.setSize( this.CANVAS_WIDTH, this.CANVAS_HEIGHT );
+
+ this.canvas.appendChild( this.renderer.domElement );
+
+ ////////////////////////////////////
+
+ this.threeService.setupScene( this.canvas, this.renderer );
+
+ this.render();
+
+ // const self: ViewportComponent = this;
+
+ /**
+ * Commented the below as this was causing slow rendering
+ */
+ // ( function render () {
+
+ // self.animationId = requestAnimationFrame( render );
+
+ // self.render();
+
+ // }() );
+
+ // self.render();
+
+ this.handleEditorEvents();
+ this.handlePointerEvents();
+
+ if ( this.directionalLightEnabled ) this.threeService.addDirectionalLight();
+
+ }
+
+ handleEditorEvents (): any {
+
+ // this.editorService.onZoomIn.subscribe( e => {
+ // if ( this.threeService.camera.zoom >= 2.0 ) return;
+ // this.threeService.camera.zoom += 0.1;
+ // this.threeService.camera.updateProjectionMatrix();
+ // } );
+ //
+ // this.editorService.onZoomOut.subscribe( e => {
+ // if ( this.threeService.camera.zoom <= 0.20 ) return;
+ // this.threeService.camera.zoom -= 0.1;
+ // this.threeService.camera.updateProjectionMatrix();
+ // } );
+ //
+ // this.editorService.onZoomReset.subscribe( e => {
+ // this.threeService.camera.zoom = 1;
+ // this.threeService.camera.updateProjectionMatrix();
+ // } );
+
+ }
+
+ render () {
+
+ // this seems a faster want to call render function
+ requestAnimationFrame( this.render );
+
+ this.frameBegin();
+
+ this.renderer.render( SceneService.scene, this.threeService.camera );
+
+ ThreeService.controls.update();
+
+ this.frameEnd();
+ }
+
+ frameBegin () {
+
+ this.beginTime = ( performance || Date ).now();
+
+ }
+
+ frameEnd () {
+
+ this.frames++;
+
+ const time = ( performance || Date ).now();
+
+ if ( time >= this.prevTime + 1000 ) {
+
+ this.fps = Math.round( ( this.frames * 1000 ) / ( time - this.prevTime ) );
+
+ this.prevTime = time;
+
+ this.frames = 0;
+ }
+
+ }
+
+ findIntersections ( recursive: boolean = true ): void {
+
+ this.raycaster.setFromCamera( this.mouse, this.threeService.camera );
+
+ this.intersections = this.raycaster.intersectObjects( SceneService.objects, recursive );
+
+ if ( this.intersections.length > 0 ) {
+
+ // if new object then fire enter event
+ if (
+ this.lastIntersection != null &&
+ this.lastIntersection.object.id != this.intersections[ 0 ].object.id &&
+ this.intersections[ 0 ].object[ 'detectRaycast' ] == true
+ ) {
+
+ this.eventSystem.pointerExit.emit( this.convertToPointerData( 0, this.lastIntersection ) );
+ this.eventSystem.pointerEnter.emit( this.convertToPointerData( 0, this.intersections[ 0 ] ) );
+
+ }
+
+ if ( this.intersections[ 0 ].object[ 'detectRaycast' ] == true ) {
+
+ this.lastIntersection = this.intersections[ 0 ];
+
+ }
+
+ // if ( this.threeIntersection.object.userData.is_annotation ) {
+ // this.editorService.mouseOverAnnotationObject.emit( this.threeIntersection );
+ // }
+
+ // if ( this.threeIntersection.object.userData.is_button ) {
+ // this.editorService.mouseOverButton.emit( this.threeIntersection );
+ // }
+
+
+ } else {
+
+ if ( this.lastIntersection != null ) {
+
+ this.eventSystem.pointerExit.emit( this.convertToPointerData( 0, this.lastIntersection ) );
+
+ }
+
+ this.lastIntersection = null;
+
+ }
+ }
+
+ private lastTime: number = Date.now();
+ private minTime: number = 100;
+
+ onMouseMove ( event: MouseEvent ) {
+
+ // TODO: implement GPU picking
+ // https://threejs.org/examples/webgl_interactive_cubes_gpu.html
+ // https://stackoverflow.com/questions/48691642/three-js-raycaster-find-intersections-as-mouse-moves
+ // https://github.com/brianxu/GPUPicker
+
+ this.mouse.x = ( ( event.clientX - this.OFFSET_LEFT ) / this.CANVAS_WIDTH ) * 2 - 1;
+ this.mouse.y = -( ( event.clientY - this.OFFSET_TOP ) / this.CANVAS_HEIGHT ) * 2 + 1;
+
+ this.raycaster.setFromCamera( this.mouse, this.threeService.camera );
+
+ // logic to limit the number of time raycasting will be done
+ // return if less time has passed
+ if ( ( Date.now() - this.lastTime ) < this.minTime ) return;
+
+ this.lastTime = Date.now();
+
+
+ // this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
+ // this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
+
+ // this.editorService.move.emit( this.mouse );
+
+ // this.eventSystem.pointerMoved.emit( { this.INTERSECTIONS[0] } );
+
+ this.intersections = this.raycaster.intersectObjects( [ ThreeService.bgForClicks ], false );
+
+ if ( this.intersections.length > 0 ) {
+ this.eventSystem.pointerMoved.emit( this.convertToPointerData( 0, this.intersections[ 0 ] ) );
+ }
+
+ // TODO: no need to find intersections on mouse move
+ return;
+
+ this.findIntersections( false );
+
+ if ( this.intersections.length > 0 ) {
+ this.eventSystem.pointerMoved.emit( this.convertToPointerData( 0, this.intersections[ 0 ] ) );
+ } else {
+ // this.eventSystem.pointerMoved.emit( { point: new THREE.Vector3 } );
+ }
+
+ }
+
+ onMouseClick ( event: MouseEvent ) {
+
+ // this.findIntersections();
+
+ switch ( event.button ) {
+
+ // left
+ case 0:
+ this.fireSelectionEvents();
+ this.fireClickedEvent( event.button );
+ break;
+
+ // middle click
+ case 1:
+ break;
+
+ // right
+ case 2:
+ break;
+
+ }
+
+ }
+
+ onMouseDown ( event: MouseEvent ) {
+
+ this.findIntersections( true );
+
+ event.preventDefault();
+
+ // this.eventSystem.pointerDown.emit( new PointerEventData );
+
+ switch ( event.button ) {
+
+ // left
+ case 0:
+ this.fireSelectionEvents();
+ if ( this.intersections.length > 0 ) {
+ this.eventSystem.pointerDown.emit(
+ this.convertToPointerData( event.button, this.intersections[ 0 ] )
+ );
+ } else {
+ this.eventSystem.pointerDown.emit( this.convertToPointerData( event.button, null ) );
+ }
+ break;
+
+ // middle
+ case 1:
+ // this.eventSystem.pointerDown.emit( this.convertToPointerData( event.button, null ) );
+ break;
+
+ // right
+ case 2:
+ this.eventSystem.pointerDown.emit( this.convertToPointerData( event.button, null ) );
+ break;
+
+ }
+
+ }
+
+ onMouseUp ( event: MouseEvent ) {
+
+ this.eventSystem.pointerUp.emit( this.convertToPointerData( event.button, null ) );
+
+ }
+
+ onMouseEnter ( event: Event ) {
+
+ this.eventSystem.pointerEnter.emit( new PointerEventData );
+
+ }
+
+ onMouseExit ( event: MouseEvent ) {
+
+ this.eventSystem.pointerExit.emit( new PointerEventData );
+
+ }
+
+ onMouseLeave ( $event: Event ) {
+
+ this.eventSystem.pointerLeave.emit( new PointerEventData );
+
+ }
+
+ onMouseOut ( e: MouseEvent ) {
+
+ this.eventSystem.pointerOut.emit( new PointerEventData );
+
+ }
+
+ //Dragover listener
+ @HostListener( 'dragover', [ '$event' ] )
+ onDragOver ( evt ) {
+ evt.preventDefault();
+ evt.stopPropagation();
+ }
+
+ //Dragleave listener
+ @HostListener( 'dragleave', [ '$event' ] )
+ onDragLeave ( evt ) {
+ evt.preventDefault();
+ evt.stopPropagation();
+ }
+
+ //Drop listener
+ @HostListener( 'drop', [ '$event' ] )
+ onDrop ( $event: DragEvent ) {
+
+ $event.preventDefault();
+ $event.stopPropagation();
+
+ this.mouse.x = ( ( $event.clientX - this.OFFSET_LEFT ) / this.CANVAS_WIDTH ) * 2 - 1;
+ this.mouse.y = -( ( $event.clientY - this.OFFSET_TOP ) / this.CANVAS_HEIGHT ) * 2 + 1;
+
+ this.findIntersections();
+
+ this.eventSystem.drop.emit( this.convertToPointerData( 0, this.intersections[ 0 ] ) );
+
+ let position = null;
+
+ if ( this.intersections.length > 0 ) {
+
+ this.eventSystem.pointerMoved.emit( this.convertToPointerData( 0, this.intersections[ 0 ] ) );
+
+ position = this.intersections[ 0 ].point;
+
+ }
+
+
+ this.importer.importViaPath( $event.dataTransfer.getData( "path" ), "", position );
+ }
+
+ @HostListener( 'window: resize', [ '$event' ] )
+ resize () {
+
+ this.setCanvasSize();
+
+ // let width = this.CANVAS_WIDTH;
+ // let height = this.CANVAS_HEIGHT;
+ //
+ // // const box = this.threeService.getCanvasBounds();
+ //
+ // // this.CANVAS_WIDTH = width = box.width;
+ // // this.CANVAS_HEIGHT = height = box.height;
+ //
+ // this.renderingService.renderer.setSize( width, height );
+ //
+ // // this.renderer.setViewport( box.left, -box.top, width, height );
+ // this.renderingService.renderer.setViewport( 0, -64, width, height );
+ //
+ // this.threeService.oCamera.left = width / -2;
+ // this.threeService.oCamera.right = width / 2;
+ // this.threeService.oCamera.top = height / 2;
+ // this.threeService.oCamera.bottom = height / -2;
+ //
+ // this.threeService.oCamera.updateProjectionMatrix();
+
+ }
+
+ handlePointerEvents (): any {
+
+
+ }
+
+ fireSelectionEvents () {
+
+ if ( this.intersections.length > 0 ) {
+
+ let object = null;
+
+ this.intersections.forEach( ( i: Intersection ) => {
+
+ if ( i.object != null && i.object.userData.is_selectable == true ) {
+
+ object = i.object;
+
+ return false;
+ }
+
+ } );
+
+ // NO SELECTION EVENTS ON OUR ANNOTATION IMAGE
+ // if ( object.userData.is_image ) return;
+
+ // new object is selected, so first deselect
+ if ( this.selected != null && object != this.selected ) {
+
+ this.eventSystem.deSelect.emit( { object: this.selected } );
+
+ this.eventSystem.select.emit( { object: object } );
+
+ this.selected = object;
+
+ } else if ( this.selected == null ) {
+
+ this.eventSystem.select.emit( { object: object } );
+
+ this.selected = object;
+
+ } else if ( object == this.selected ) {
+
+ // do nothing
+
+ }
+
+ // no object is selected
+ } else {
+
+ this.eventSystem.deSelect.emit( { object: this.selected } );
+
+ }
+
+ }
+
+ fireClickedEvent ( button: number ): any {
+
+ if ( this.intersections.length > 0 ) {
+
+ this.eventSystem.pointerClicked.emit(
+ this.convertToPointerData( button, this.intersections[ 0 ] )
+ );
+
+ }
+
+ }
+
+ convertToPointerData ( button: number, i: THREE.Intersection ): PointerEventData {
+
+ let p = new PointerEventData();
+
+ if ( i != null ) {
+
+ p.distance = i.distance;
+ p.distanceToRay = i.distanceToRay;
+ p.index = i.index;
+ p.face = i.face;
+ p.faceIndex = i.faceIndex;
+ p.object = i.object;
+ p.point = i.point;
+ p.uv = i.uv;
+ p.intersections = this.intersections;
+
+ if ( this.threeService.camera instanceof OrthographicCamera ) {
+
+ // approximation, not accurate
+ p.approxCameraDistance = ( 1 / this.threeService.camera.zoom ) * this.ORTHO_DRIVER * this.threeService.camera.position.z;
+
+ } else if ( this.threeService.camera instanceof PerspectiveCamera ) {
+
+ p.approxCameraDistance = i.distance;
+
+ }
+
+ } else {
+
+ p.intersections = [];
+
+ }
+
+ // ADDITIONAL
+ if ( button == 0 ) {
+ p.button = MouseButton.LEFT;
+ } else if ( button == 1 ) {
+ p.button = MouseButton.MIDDLE;
+ } else if ( button == 2 ) {
+ p.button = MouseButton.RIGHT;
+ }
+
+ return p;
+ }
+
+ detectWebgl () {
+
+ try {
+
+ // Create canvas element. The canvas is not added to the
+ // document itself, so it is never displayed in the
+ // browser window.
+ const canvas = document.createElement( 'canvas' );
+
+ // Get WebGLRenderingContext from canvas element.
+ const gl = canvas.getContext( 'webgl' ) || canvas.getContext( 'experimental-webgl' );
+
+ // Report the result.
+ if ( gl && gl instanceof WebGLRenderingContext ) {
+
+ return true;
+
+ } else {
+
+ return false;
+ }
+
+ } catch ( e ) {
+
+ return false;
+
+ }
+ }
+
+ onContextMenu ( $event: MouseEvent ) {
+
+
+ }
+
+ private setCanvasSize () {
+
+ const container = this.renderer.domElement.parentElement.parentElement;
+
+ const box = container.getBoundingClientRect();
+
+ const width = this.threeService.canvasWidth = this.CANVAS_WIDTH = container.clientWidth;
+ const height = this.threeService.canvasHeight = this.CANVAS_HEIGHT = container.clientHeight;
+
+ this.OFFSET_LEFT = this.threeService.leftOffset = box.left;
+ this.OFFSET_TOP = this.threeService.topOffset = box.top;
+
+ this.renderer.setViewport( -box.left, -box.top, width, height );
+ this.renderer.setSize( width, height );
+
+ this.threeService.onWindowResized();
+
+ }
+}
diff --git a/src/app/modules/tv-map/builders/od-builder-config.ts b/src/app/modules/tv-map/builders/od-builder-config.ts
new file mode 100644
index 00000000..bf4c00a3
--- /dev/null
+++ b/src/app/modules/tv-map/builders/od-builder-config.ts
@@ -0,0 +1,36 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+export class OdBuilderConfig {
+
+ public static ROAD_STEP = 1.0;
+
+ public static JUNCTION_STEP = 3.0;
+
+ // Junction elevation shift, so that the junction tracks are drawn above the road
+ public static JUNCTION_ELEVATION_SHIFT = 0.05;
+
+ // Epsilon constant for length and s related precision
+ public static LENGTH_EPS = 0.0000001;
+
+ // Epsilon constant for Height related precision
+ public static HEIGHT_EPS = 1.0e-6;
+
+ // Texture scaling along the road (along s)
+ // S is divided by this constant to define the number of tiling of the texture
+ public static TEXTURE_LENGTH_SCALING = 4.0;
+
+ // widths for the two types of weight values
+ public static STD_ROADMARK_WIDTH = 0.15;
+ public static BOLD_ROADMARK_WIDTH = 0.3;
+
+ // elevation shift, so that the road mark is drawn above the road
+ // 0.001 increases the z-fighting, and requires camera near value of 10
+ // 0.01 works well with camera near value of 1
+ public static ROADMARK_ELEVATION_SHIFT = 0.01;
+
+ // broken mark tiling
+ private static ROADMARK_BROKEN_TILING = 3.0;
+
+}
\ No newline at end of file
diff --git a/src/app/modules/tv-map/builders/od-builder.service.ts b/src/app/modules/tv-map/builders/od-builder.service.ts
new file mode 100644
index 00000000..45c3baa4
--- /dev/null
+++ b/src/app/modules/tv-map/builders/od-builder.service.ts
@@ -0,0 +1,318 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvMap } from '../models/tv-map.model';
+import { GameObject } from 'app/core/game-object';
+import { TvRoad } from '../models/tv-road.model';
+import { TvLaneSection } from '../models/tv-lane-section';
+import { TvRoadSignal } from '../models/tv-road-signal.model';
+import { TvRoadObject } from '../models/tv-road-object';
+import * as THREE from 'three';
+import { BufferGeometry, Material, MeshBasicMaterial, Vector2, Vector3 } from 'three';
+import { TvObjectType } from '../interfaces/i-tv-object';
+import { TvPosTheta } from '../models/tv-pos-theta';
+import { Maths } from 'app/utils/maths';
+import { TvLane } from '../models/tv-lane';
+import { ObjectTypes, TvLaneSide, TvLaneType } from '../models/tv-common';
+import { OdBuilderConfig } from './od-builder-config';
+import { TvMapSourceFile } from '../services/tv-map-source-file';
+import { SceneService } from '../../../core/services/scene.service';
+import { TvSignalHelper } from '../services/tv-signal-helper';
+import { OdRoadMarkBuilder } from './od-road-mark-builder';
+import { MeshGeometryData } from '../models/mesh-geometry.data';
+import { Vertex } from '../models/vertex';
+import { OdSignalBuilder } from './od-signal-builder';
+import { AssetDatabase } from 'app/services/asset-database';
+import { OdMaterials } from './od-materials.service';
+
+export class TvMapBuilder {
+
+ private static signalFactory = new OdSignalBuilder;
+ private static roadMarkBuilder = new OdRoadMarkBuilder( null );
+
+ private static JUNCTION_ELEVATION_SHIFT = 0.005;
+
+ constructor ( public openDrive?: TvMap ) {
+
+ }
+
+ public static buildMap ( openDrive?: TvMap ): GameObject {
+
+ TvMapSourceFile.clearOpenDrive();
+
+ SceneService.remove( openDrive.gameObject );
+
+ openDrive.gameObject = null;
+ openDrive.gameObject = new GameObject( 'OpenDrive' );
+
+ openDrive.roads.forEach( road => {
+
+ this.buildRoad( openDrive.gameObject, road );
+
+ } );
+
+ SceneService.add( openDrive.gameObject );
+
+ return openDrive.gameObject;
+
+ }
+
+ static buildRoad ( parent: GameObject, road: TvRoad ): any {
+
+ road.gameObject = null;
+ road.gameObject = new GameObject( 'Road:' + road.id );
+ road.gameObject.Tag = ObjectTypes.ROAD;
+ road.gameObject.userData.road = road;
+
+ road.lanes.computeLaneSectionEnd( road );
+
+ // ( new OdRoadReferenceLineHelper( road ) ).create();
+ // ( new OdLaneReferenceLineHelper( road ) ).create();
+
+ // OdBuilder.makeRoadReferenceLine( road );
+
+ // const offset = road.lanes.getLaneOffset();
+ const laneSections = road.lanes.getLaneSections();
+
+ for ( let i = 0; i < laneSections.length; i++ ) {
+
+ TvMapBuilder.buildLaneSection( road, laneSections[ i ] );
+
+ }
+
+ this.roadMarkBuilder.buildRoad( road );
+
+ ( new TvSignalHelper( road ) ).create();
+
+ parent.add( road.gameObject );
+
+ }
+
+ static buildLaneSection ( road: TvRoad, laneSection: TvLaneSection ): void {
+
+ laneSection.gameObject = null;
+ laneSection.gameObject = new GameObject( 'LaneSection' );
+
+ road.gameObject.add( laneSection.gameObject );
+
+ TvMapBuilder.buildLanes( laneSection.getLeftLanes().reverse(), road, laneSection );
+
+ TvMapBuilder.createCenterLane( laneSection.getLastCenterLane(), laneSection, road );
+
+ TvMapBuilder.buildLanes( laneSection.getRightLanes(), road, laneSection );
+
+ // console.timeEnd( 'lane-build-time' );
+ }
+
+ static createCenterLane ( lane: TvLane, laneSection: TvLaneSection, road: TvRoad ) {
+
+ this.createLaneGameObject( lane, new BufferGeometry(), new MeshBasicMaterial(), laneSection );
+
+ }
+
+ static buildLanes ( lanes: TvLane[], road: TvRoad, laneSection: TvLaneSection ): any {
+
+ // console.time( 'lane-build-time' );
+
+ for ( let i = 0; i < lanes.length; i++ ) {
+
+ const lane = lanes[ i ];
+
+ TvMapBuilder.buildLane( lane, laneSection, road );
+
+ }
+
+ }
+
+ static buildLane ( lane: TvLane, laneSection: TvLaneSection, road: TvRoad ): any {
+
+ let roadStep = OdBuilderConfig.ROAD_STEP;
+ let posTheta = new TvPosTheta;
+
+ let cumulativeWidth = 0;
+
+ lane.meshData = null;
+ lane.meshData = new MeshGeometryData;
+
+ lane.markMeshData = null;
+ lane.markMeshData = new MeshGeometryData;
+
+ const laneSectionLength = laneSection.lastSCoordinate - laneSection.s;
+
+ let step = 0;
+
+ for ( let sCoordinate = laneSection.s; sCoordinate < laneSection.lastSCoordinate; sCoordinate += roadStep ) {
+
+ step += roadStep;
+
+ cumulativeWidth = laneSection.getWidthUptoStart( lane, step );
+
+ road.getGeometryCoords( sCoordinate, posTheta );
+
+ this.makeLaneVertices( sCoordinate, posTheta, lane, road, cumulativeWidth, step );
+
+ }
+
+ // add last s geometry to close any gaps
+ let lastSCoordinate = laneSection.lastSCoordinate - Maths.Epsilon;
+
+ cumulativeWidth = laneSection.getWidthUptoStart( lane, laneSectionLength );
+
+ road.getGeometryCoords( lastSCoordinate, posTheta );
+
+ this.makeLaneVertices( lastSCoordinate, posTheta, lane, road, cumulativeWidth, laneSectionLength );
+
+ this.createLaneMeshFromGeometry( road, lane, laneSection );
+
+ }
+
+ static makeLaneVertices ( sCoordinate: number, pos: TvPosTheta, lane: TvLane, road: TvRoad, cumulativeWidth: number, laneSectionS: number ) {
+
+ const width = lane.getWidthValue( laneSectionS );
+ const height = lane.getHeightValue( laneSectionS );
+ const elevation = road.getElevationValue( laneSectionS );
+
+ const cosHdgPlusPiO2 = Maths.cosHdgPlusPiO2( lane.side, pos.hdg );
+ const sinHdgPlusPiO2 = Maths.sinHdgPlusPiO2( lane.side, pos.hdg );
+
+ const v1 = new Vertex();
+ const p1X = cosHdgPlusPiO2 * cumulativeWidth;
+ const p1Y = sinHdgPlusPiO2 * cumulativeWidth;
+ v1.Position = new Vector3( pos.x + p1X, pos.y + p1Y, elevation );
+ v1.TexCoord = new Vector2( 0, sCoordinate );
+
+ const v2 = new Vertex();
+ const p2X = cosHdgPlusPiO2 * ( cumulativeWidth + width );
+ const p2Y = sinHdgPlusPiO2 * ( cumulativeWidth + width );
+ v2.Position = new Vector3( pos.x + p2X, pos.y + p2Y, elevation + height.getOuter() );
+ v2.TexCoord = new Vector2( width + height.getOuter(), sCoordinate );
+
+ if ( lane.side == TvLaneSide.RIGHT ) {
+
+ this.addVertex( lane.meshData, v1 );
+ this.addVertex( lane.meshData, v2 );
+
+ } else {
+
+ this.addVertex( lane.meshData, v2 );
+ this.addVertex( lane.meshData, v1 );
+
+ }
+
+ }
+
+ static addVertex ( meshData: MeshGeometryData, v1: Vertex ) {
+
+ meshData.vertices.push( v1.Position.x, v1.Position.y, v1.Position.z );
+ meshData.normals.push( v1.Normal.x, v1.Normal.y, v1.Normal.z );
+ meshData.texCoords.push( v1.TexCoord.x, v1.TexCoord.y );
+ meshData.indices.push( meshData.currentIndex++ );
+
+ }
+
+
+ static createMeshIndices ( geom: MeshGeometryData ): void {
+
+ let index = 0;
+
+ for ( let i = 0; i < ( geom.indices.length / 2 ) - 1; i++ ) {
+
+ geom.triangles.push( index );
+ geom.triangles.push( index + 1 );
+ geom.triangles.push( index + 2 );
+
+ geom.triangles.push( index + 1 );
+ geom.triangles.push( index + 3 );
+ geom.triangles.push( index + 2 );
+
+ index += 2;
+ }
+ }
+
+ static makeRoadSignal ( road: TvRoad, signal: TvRoadSignal ): any {
+
+ TvMapBuilder.signalFactory.createSignalGameObject( road, signal );
+
+ }
+
+ static makeObject ( road: TvRoad, object: TvRoadObject ): any {
+
+ var gameObject = new GameObject( 'Object:' + object.attr_id );
+
+ road.gameObject.add( gameObject );
+
+ }
+
+ private static createLaneMeshFromGeometry ( road: TvRoad, lane: TvLane, laneSection: TvLaneSection ) {
+
+ this.createMeshIndices( lane.meshData );
+
+ const geometry = new THREE.BufferGeometry();
+ const vertices = new Float32Array( lane.meshData.vertices );
+ const normals = new Float32Array( lane.meshData.normals );
+ const faces = new Float32Array( lane.meshData.texCoords );
+
+ geometry.setIndex( lane.meshData.triangles );
+ geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
+ geometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
+ geometry.addAttribute( 'uv', new THREE.Float32BufferAttribute( faces, 2 ) );
+
+ geometry.computeBoundingBox();
+ geometry.computeVertexNormals();
+
+ // const material = OdMaterials.getLaneMaterial( lane );
+ const material = this.getLaneMaterial( road, lane );
+
+ TvMapBuilder.createLaneGameObject( lane, geometry, material, laneSection );
+
+ }
+
+ public static getLaneMaterial ( road: TvRoad, lane: TvLane ): Material {
+
+ let material: Material;
+ let guid: string;
+
+ if ( lane.type == TvLaneType.driving ) {
+
+ guid = road.drivingMaterialGuid;
+
+ } else if ( lane.type == TvLaneType.border ) {
+
+ guid = road.borderMaterialGuid;
+
+ } else if ( lane.type == TvLaneType.sidewalk ) {
+
+ guid = road.sidewalkMaterialGuid;
+
+ } else if ( lane.type == TvLaneType.shoulder ) {
+
+ guid = road.shoulderMaterialGuid;
+
+ }
+
+ // find by guid
+ if ( guid ) material = AssetDatabase.getInstance( guid );
+
+ // if no material found then use in built
+ if ( !material ) material = OdMaterials.getLaneMaterial( lane ) as Material;
+
+ return material;
+ }
+
+ private static createLaneGameObject (
+ lane: TvLane,
+ geometry: THREE.BufferGeometry,
+ material: THREE.Material | THREE.Material[],
+ laneSection: TvLaneSection
+ ) {
+
+ lane.gameObject = new GameObject( 'Lane:' + lane.id, geometry, material );
+ lane.gameObject.Tag = TvObjectType[ TvObjectType.LANE ];
+ lane.gameObject.OpenDriveType = TvObjectType.LANE;
+ lane.gameObject.userData.data = lane;
+ lane.gameObject.userData.lane = lane;
+
+ laneSection.gameObject.add( lane.gameObject );
+ }
+}
diff --git a/src/app/modules/tv-map/builders/od-lane-direction-builder.ts b/src/app/modules/tv-map/builders/od-lane-direction-builder.ts
new file mode 100644
index 00000000..80f875ac
--- /dev/null
+++ b/src/app/modules/tv-map/builders/od-lane-direction-builder.ts
@@ -0,0 +1,133 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvRoad } from '../models/tv-road.model';
+import { ArrowHelper, Object3D, Vector3 } from 'three';
+import { TvLane } from '../models/tv-lane';
+import { TvLaneSection } from '../models/tv-lane-section';
+import { TvPosTheta } from '../models/tv-pos-theta';
+import { TvLaneSide, TvLaneType } from '../models/tv-common';
+import { Maths } from '../../../utils/maths';
+import { SceneService } from 'app/core/services/scene.service';
+
+export class OdLaneDirectionBuilder {
+
+ private stepValue = 5;
+ private arrows: Object3D[] = [];
+
+ constructor ( private road: TvRoad ) {
+
+ }
+
+ setRoad ( value: TvRoad ): void {
+
+ this.clear();
+
+ this.road = value;
+
+ }
+
+ create () {
+
+ const container = this.road.getLanes();
+
+ container.computeLaneSectionEnd( this.road );
+
+ for ( let i = 0; i < this.road.lanes.laneSections.length; i++ ) {
+
+ const laneSection = this.road.lanes.laneSections[ i ];
+
+ laneSection.getLeftLanes().forEach( lane => this.drawLane( lane, laneSection ) );
+
+ laneSection.getRightLanes().forEach( lane => this.drawLane( lane, laneSection ) );
+ }
+
+ }
+
+ clear () {
+
+ this.arrows.forEach( arrow => SceneService.removeHelper( arrow ) );
+
+ }
+
+ drawSingleLane ( road: TvRoad, lane: TvLane ) {
+
+ this.road = road;
+
+ for ( let i = 0; i < this.road.lanes.laneSections.length; i++ ) {
+
+ const laneSection = this.road.lanes.laneSections[ i ];
+
+ this.drawLane( lane, laneSection );
+ }
+ }
+
+ private createArrow ( origin: Vector3, direction: Vector3 ) {
+
+ // var dir = new Vector3( 0, 1, 0 );
+
+ // normalize the direction vector (convert to vector of length 1)
+ direction.normalize();
+
+ const length = 2.5;
+ const hex = 0xffff00;
+
+ const headLength = 0.2 * length;
+ const headWidth = 0.75 * headLength;
+
+ origin.setZ( origin.z + 0.1 );
+
+ const arrowHelper = new ArrowHelper( direction, origin, length, hex, headLength, headWidth );
+
+ arrowHelper.renderOrder = 3;
+
+ this.arrows.push( arrowHelper );
+
+ // add to helper to avoid raycasting
+ SceneService.addHelper( arrowHelper );
+ }
+
+ private drawLane ( lane: TvLane, laneSection: TvLaneSection ) {
+
+ if ( lane.type !== TvLaneType.driving ) return;
+
+ let s = laneSection.s;
+
+ const posTheta = new TvPosTheta();
+
+ let laneOffset = 0;
+
+ let width = 0;
+
+ while ( s <= laneSection.lastSCoordinate ) {
+
+ laneOffset = this.road.lanes.getLaneOffsetValue( s );
+
+ width = laneSection.getWidthUptoCenter( lane, s );
+
+ this.road.getGeometryCoords( s, posTheta );
+
+ posTheta.addLateralOffset( laneOffset );
+
+ if ( lane.side === TvLaneSide.RIGHT ) {
+
+ width *= -1;
+
+ } else if ( lane.side === TvLaneSide.LEFT ) {
+
+ // to show arrow in traffic direction
+ // TODO: Make traffic direction editable from editor
+ posTheta.hdg += Maths.M_PI;
+ width *= -1;
+ }
+
+ posTheta.addLateralOffset( width );
+
+ this.createArrow( posTheta.toVector3(), posTheta.toDirectionVector() );
+
+ s += this.stepValue;
+ }
+
+ }
+}
diff --git a/src/app/modules/tv-map/builders/od-lane-reference-line-builder.ts b/src/app/modules/tv-map/builders/od-lane-reference-line-builder.ts
new file mode 100644
index 00000000..a04b7a49
--- /dev/null
+++ b/src/app/modules/tv-map/builders/od-lane-reference-line-builder.ts
@@ -0,0 +1,346 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvRoad } from '../models/tv-road.model';
+import * as THREE from 'three';
+import { Color, Line, LineBasicMaterial, LineDashedMaterial, Material, Object3D, Vector3 } from 'three';
+import { TvPosTheta } from '../models/tv-pos-theta';
+import { TvLaneSection } from '../models/tv-lane-section';
+import { TvLane } from '../models/tv-lane';
+import { TvLaneSide } from '../models/tv-common';
+import { Maths } from 'app/utils/maths';
+
+export enum LineType {
+ SOLID = 'solid',
+ DASHED = 'dashed',
+ BOTH = 'both',
+}
+
+const DEFAULT_LINE_COLOR = new Color( 0, 0, 1 );
+const HIGHLIGHT_LINE_COLOR = new Color( 0, 1, 0 );
+const SELECTED_LINE_COLOR = new Color( 1, 0, 0 );
+
+const DEFAULT_SOLID_WIDTH = 1;
+const HIGHLIGHT_SOLID_WIDTH = 2;
+
+const DEFAULT_DASHED_WIDTH = 2;
+const HIGHLIGHT_DASHED_WIDTH = 3;
+
+
+export class OdLaneReferenceLineBuilder {
+
+ public lines: Object3D[] = [];
+
+ public readonly tag: string = 'lane-reference-line';
+
+ private basicMaterial = new THREE.LineBasicMaterial( {
+ color: DEFAULT_LINE_COLOR,
+ linewidth: DEFAULT_SOLID_WIDTH
+ } );
+
+ private dashedMaterial = new LineDashedMaterial( {
+ color: DEFAULT_LINE_COLOR,
+ opacity: 0.35,
+ linewidth: DEFAULT_DASHED_WIDTH,
+ scale: 1,
+ dashSize: 0.2,
+ gapSize: 0.1,
+ } );
+
+ private mouseOverLine: Line;
+
+ private selectedLine: Line;
+
+ constructor ( private road?: TvRoad, private lineType: LineType = LineType.SOLID, private color?: number ) {
+
+ }
+
+ public setType ( lineType: LineType ) {
+
+ this.lineType = lineType;
+
+ }
+
+ public create () {
+
+ const container = this.road.getLanes();
+
+ container.computeLaneSectionEnd( this.road );
+
+ this.drawRoad( this.road );
+ }
+
+ public redraw ( type: LineType = LineType.SOLID ): void {
+
+ const road = this.road;
+
+ this.clear();
+
+ this.drawRoad( road, type );
+ }
+
+
+ public drawRoad ( road: TvRoad, type: LineType = LineType.SOLID, redraw = false ) {
+
+ if ( road == null ) return;
+
+ // TODO: remove this logic as the responsibility should not be here
+ // simply return if the road is already selected
+ if ( !redraw && this.road && this.road.id === road.id ) return;
+
+ this.clear();
+
+ this.road = road;
+
+ for ( let i = 0; i < road.lanes.laneSections.length; i++ ) {
+
+ const laneSection = road.lanes.laneSections[ i ];
+
+ laneSection.lanes.forEach( lane => {
+
+ const points: TvPosTheta[] = [];
+
+ this.makeLanePoints( laneSection, lane, points );
+
+ this.drawLine( lane, this.convertToVector3List( points ), type );
+
+ } )
+
+ }
+ }
+
+ public clear () {
+
+ if ( this.road && this.road.gameObject ) {
+
+ this.lines.forEach( line => {
+
+ this.road.gameObject.remove( line );
+
+ } );
+
+ this.road = null;
+ }
+
+ }
+
+ onMouseOverLine ( line: Line ) {
+
+ if ( this.selectedLine && line.id === this.selectedLine.id ) return;
+
+ // // not reqquired it seems
+ // // dont higlight if this line is already selected
+ // if ( this.mouseOverLine && this.selectedLine && line.id === this.selectedLine.id ) return;
+
+ // if same line is being asked to highlight then simple return and do nothing
+ if ( this.mouseOverLine && this.mouseOverLine.id == line.id ) return;
+
+ // // not reqquired it seems
+ // simpley return if mouse is over the selected line
+ // if ( this.mouseOverLine && this.selectedLine && this.selectedLine.id == this.mouseOverLine.id ) return;
+
+ // else remove the previously highlighted line if present
+ this.onMouseOutLine();
+
+ // make this the new highlighted line
+ this.mouseOverLine = line;
+
+ // set the current material property to highlighted color
+ ( line.computeLineDistances() );
+
+ ( line.material as LineBasicMaterial ).color.set( HIGHLIGHT_LINE_COLOR );
+
+ if ( this.lineType == LineType.SOLID ) {
+
+ ( line.material as LineBasicMaterial ).linewidth = HIGHLIGHT_SOLID_WIDTH;
+
+ } else {
+
+ ( line.material as LineBasicMaterial ).linewidth = HIGHLIGHT_DASHED_WIDTH;
+ }
+
+ ( line.material as LineBasicMaterial ).needsUpdate = true;
+ }
+
+ onMouseOutLine () {
+
+ if ( !this.mouseOverLine ) return;
+
+ // simpley return if mouse was over the selected line
+ if ( this.mouseOverLine && this.selectedLine && this.mouseOverLine.id == this.selectedLine.id ) return;
+
+ // set the current material property to highlighted color
+ ( this.mouseOverLine.computeLineDistances() );
+
+ ( this.mouseOverLine.material as LineBasicMaterial ).color.set( DEFAULT_LINE_COLOR );
+
+ if ( this.lineType == LineType.SOLID ) {
+
+ ( this.mouseOverLine.material as LineBasicMaterial ).linewidth = DEFAULT_SOLID_WIDTH;
+
+ } else {
+
+ ( this.mouseOverLine.material as LineBasicMaterial ).linewidth = DEFAULT_DASHED_WIDTH;
+ }
+
+ ( this.mouseOverLine.material as LineBasicMaterial ).needsUpdate = true;
+
+ this.mouseOverLine = null;
+ }
+
+ onLineSelected ( line: Line ) {
+
+ if ( this.selectedLine && this.selectedLine.id == line.id ) return;
+
+ this.onLineUnselected();
+
+ this.selectedLine = line;
+
+ ( line.computeLineDistances() );
+
+ ( line.material as LineBasicMaterial ).color.set( SELECTED_LINE_COLOR );
+
+ if ( this.lineType == LineType.SOLID ) {
+
+ ( line.material as LineBasicMaterial ).linewidth = HIGHLIGHT_SOLID_WIDTH;
+
+ } else {
+
+ ( line.material as LineBasicMaterial ).linewidth = HIGHLIGHT_DASHED_WIDTH;
+ }
+
+ ( line.material as LineBasicMaterial ).needsUpdate = true;
+
+ }
+
+ onLineUnselected () {
+
+ if ( !this.selectedLine ) return;
+
+ ( this.selectedLine.computeLineDistances() );
+
+ ( this.selectedLine.material as LineBasicMaterial ).color.set( DEFAULT_LINE_COLOR );
+
+ if ( this.lineType == LineType.SOLID ) {
+
+ ( this.selectedLine.material as LineBasicMaterial ).linewidth = DEFAULT_SOLID_WIDTH;
+
+ } else {
+
+ ( this.selectedLine.material as LineBasicMaterial ).linewidth = DEFAULT_DASHED_WIDTH;
+ }
+
+ ( this.selectedLine.material as LineBasicMaterial ).needsUpdate = true;
+
+ this.selectedLine = null;
+
+ }
+
+ private drawLine ( lane: TvLane, points: Vector3[], type: LineType = LineType.SOLID ) {
+
+ const geometry = new THREE.BufferGeometry().setFromPoints( points );
+
+ const material = this.getLineMaterial( type );
+
+ if ( this.color ) material.color.set( this.color );
+
+ const line = new THREE.Line( geometry, material );
+
+ ( line.material as Material ).depthTest = false;
+
+ line.computeLineDistances();
+
+ line.name = 'LaneWidthLine';
+
+ line.userData.is_selectable = true;
+
+ line.userData.lane = lane;
+
+ line.renderOrder = 5;
+
+ line[ 'tag' ] = this.tag;
+
+ this.lines.push( line );
+
+ this.road.gameObject.add( line );
+ }
+
+ private getLineMaterial ( type: LineType ) {
+
+ if ( type == LineType.SOLID ) {
+
+ return new LineBasicMaterial().copy( this.basicMaterial );
+
+ } else if ( type == LineType.DASHED ) {
+
+ return new LineDashedMaterial().copy( this.dashedMaterial );
+
+ } else if ( type == LineType.BOTH ) {
+
+ return new LineDashedMaterial().copy( this.dashedMaterial );
+
+ } else {
+
+ console.warn( "unknown line type" );
+
+ return new LineBasicMaterial().copy( this.basicMaterial );
+
+ }
+ }
+
+ private convertToVector3List ( poses: TvPosTheta[] ): Vector3[] {
+
+ const tmp: Vector3[] = [];
+
+ poses.forEach( pose => {
+
+ tmp.push( new Vector3( pose.x, pose.y, 0 ) );
+
+ } );
+
+ return tmp;
+ }
+
+ private makeLanePoints ( laneSection: TvLaneSection, lane: TvLane, points: TvPosTheta[] = [] ) {
+
+ let s = laneSection.s;
+
+ while ( s <= laneSection.lastSCoordinate ) {
+
+ this.makeLanePointsLoop( s, laneSection, lane, points );
+
+ s++;
+ }
+
+ s = laneSection.lastSCoordinate - Maths.Epsilon;
+
+ this.makeLanePointsLoop( s, laneSection, lane, points );
+ }
+
+ private makeLanePointsLoop ( s, laneSection: TvLaneSection, lane: TvLane, points: TvPosTheta[] = [] ) {
+
+ const posTheta = new TvPosTheta();
+
+ // const laneOffset = this.road.lanes.getLaneOffsetValue( s );
+
+ let width = laneSection.getWidthUptoEnd( lane, s );
+
+ this.road.getGeometryCoords( s, posTheta );
+
+ // posTheta.addLateralOffset( laneOffset );
+
+ // If right side lane then make the offset negative
+ if ( lane.side === TvLaneSide.RIGHT ) {
+ width *= -1;
+ }
+
+ posTheta.addLateralOffset( width );
+
+ points.push( posTheta );
+
+ }
+
+
+
+}
diff --git a/src/app/modules/tv-map/builders/od-materials.service.ts b/src/app/modules/tv-map/builders/od-materials.service.ts
new file mode 100644
index 00000000..18f5454d
--- /dev/null
+++ b/src/app/modules/tv-map/builders/od-materials.service.ts
@@ -0,0 +1,26 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { MeshBasicMaterial } from 'three';
+import { TvLane } from '../models/tv-lane';
+import { OdTextures } from './od.textures';
+import { COLOR } from '../../../shared/utils/colors.service';
+
+export class OdMaterials {
+
+ static getLaneMaterial ( lane: TvLane, forceNew = false ) {
+
+ if ( !forceNew && lane.gameObject != null && lane.gameObject.material != null ) {
+
+ return lane.gameObject.material;
+
+ }
+
+ return new MeshBasicMaterial( {
+ map: OdTextures.getLaneTexture( lane ),
+ color: COLOR.WHITE,
+ wireframe: false,
+ } );
+ }
+}
diff --git a/src/app/modules/tv-map/builders/od-road-builder.ts b/src/app/modules/tv-map/builders/od-road-builder.ts
new file mode 100644
index 00000000..9400a1cc
--- /dev/null
+++ b/src/app/modules/tv-map/builders/od-road-builder.ts
@@ -0,0 +1,14 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvRoad } from '../models/tv-road.model';
+
+export class OdRoadBuilder {
+
+ static build ( road: TvRoad ) {
+
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/app/modules/tv-map/builders/od-road-mark-builder.ts b/src/app/modules/tv-map/builders/od-road-mark-builder.ts
new file mode 100644
index 00000000..4199bf56
--- /dev/null
+++ b/src/app/modules/tv-map/builders/od-road-mark-builder.ts
@@ -0,0 +1,452 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvRoad } from '../models/tv-road.model';
+import * as THREE from 'three';
+import { Vector2, Vector3 } from 'three';
+import { TvPosTheta } from '../models/tv-pos-theta';
+import { TvLaneSection } from '../models/tv-lane-section';
+import { TvLane } from '../models/tv-lane';
+import { Maths } from 'app/utils/maths';
+import { GameObject } from '../../../core/game-object';
+import { ObjectTypes, TvLaneSide, TvRoadMarkTypes } from '../models/tv-common';
+import { TvLaneRoadMark } from '../models/tv-lane-road-mark';
+import { COLOR } from '../../../shared/utils/colors.service';
+import { MeshGeometryData } from '../models/mesh-geometry.data';
+import { Vertex } from '../models/vertex';
+import { OdBuilderConfig } from './od-builder-config';
+import { SceneService } from '../../../core/services/scene.service';
+
+export class OdRoadMarkBuilder {
+
+ private _texture: any;
+
+ constructor ( private road: TvRoad = null ) {
+
+ }
+
+ private get texture () {
+
+ if ( this._texture ) return this._texture;
+
+ const texture = new THREE.TextureLoader().load( 'assets/flat-roadmarks.png' );
+
+ texture.wrapS = THREE.RepeatWrapping;
+ texture.wrapT = THREE.RepeatWrapping;
+ texture.mapping = THREE.UVMapping;
+ texture.repeat.set( 1, 1 );
+
+ texture.anisotropy = 5;
+
+ this._texture = texture;
+
+ return this._texture;
+ }
+
+ public create (): void {
+
+ this.buildRoad( this.road );
+
+ }
+
+
+ public buildRoad ( road: TvRoad ): void {
+
+ this.road = road;
+
+ for ( let i = 0; i < road.lanes.laneSections.length; i++ ) {
+
+ const laneSection = road.lanes.laneSections[ i ];
+
+ laneSection.getLaneVector().forEach( lane => {
+
+ this.processLane( laneSection, lane );
+
+ } );
+
+ }
+ }
+
+ private createMeshIndices ( geom: MeshGeometryData ): void {
+
+ let index = 0;
+
+ for ( let i = 0; i < ( geom.indices.length / 2 ) - 1; i++ ) {
+
+ geom.triangles.push( index );
+ geom.triangles.push( index + 1 );
+ geom.triangles.push( index + 2 );
+
+ geom.triangles.push( index + 1 );
+ geom.triangles.push( index + 3 );
+ geom.triangles.push( index + 2 );
+
+ index += 2;
+ }
+ }
+
+
+ private processLane ( laneSection: TvLaneSection, lane: TvLane ) {
+
+ const roadMarks = lane.getRoadMarks();
+
+ this.clearPreviousMesh( roadMarks, lane );
+
+ this.updateLastCoordinate( roadMarks, laneSection, lane );
+
+ for ( const mark of roadMarks ) {
+
+ const mesh = new MeshGeometryData();
+
+ let sOffset = laneSection.s + mark.sOffset;
+
+ let relativeS = mark.sOffset;
+
+ for ( let s = 0; s < mark.length; s += OdBuilderConfig.ROAD_STEP ) {
+
+ this.createVertex( sOffset + s, mark, laneSection, lane, mesh, relativeS + s );
+
+ }
+
+ // one last entry to nearest to the end
+ this.createVertex( ( sOffset + mark.length ) - Maths.Epsilon, mark, laneSection, lane, mesh, ( relativeS + mark.length ) - Maths.Epsilon );
+
+ this.drawRoadMark( mark, mesh, lane );
+
+ }
+
+ // for ( let s = laneSection.s; s <= laneSection.lastSCoordinate; s++ ) {
+ //
+ // this.makeLanePointsLoop( s, laneSection, lane, mesh );
+ //
+ // }
+
+ // last entry to close any issues
+ // const s = laneSection.lastSCoordinate - Maths.Epsilon;
+
+ // this.makeLanePointsLoop( s, laneSection, lane, points );
+ }
+
+ private createVertex ( s, roadMark: TvLaneRoadMark, laneSection: TvLaneSection, lane: TvLane, mesh: MeshGeometryData, laneSectionS: number ) {
+
+ const posTheta = new TvPosTheta();
+
+ const cumulativeWidth = this.getCumulativeWidth( laneSectionS, lane, laneSection );
+
+ // if ( cumulativeWidth > 100 ) console.log( laneSectionS, this.road.id, laneSection.s, lane.id, cumulativeWidth );
+
+ this.road.getGeometryCoords( s, posTheta );
+
+ // const laneOffset = this.road.lanes.getLaneOffsetAt( s );
+ // posTheta.addLateralOffset( laneOffset );
+ // let laneWidth = laneSection.getWidthUptoEnd( lane, s );
+
+ const height = lane.getHeightValue( laneSectionS );
+ const elevation = this.road.getElevationValue( laneSectionS );
+
+ // console.log( roadMark.getHeight() );
+
+ const cosHdgPlusPiO2 = Maths.cosHdgPlusPiO2( lane.side, posTheta.hdg );
+ const sinHdgPlusPiO2 = Maths.sinHdgPlusPiO2( lane.side, posTheta.hdg );
+
+ const laneBorderX = posTheta.x + ( cosHdgPlusPiO2 * cumulativeWidth );
+ const laneBorderY = posTheta.y + ( sinHdgPlusPiO2 * cumulativeWidth );
+
+ // console.log( `LaneSection: ${laneSection.id} Lane: ${lane.id} Width: ${cumulativeWidth} S: ${s} LS: ${laneSectionS}` );
+
+ const roadMarkWidth = roadMark.getWidth();
+
+ const x1 = laneBorderX + ( cosHdgPlusPiO2 * roadMarkWidth * 0.5 );
+ const y1 = laneBorderY + ( sinHdgPlusPiO2 * roadMarkWidth * 0.5 );
+
+ const x2 = laneBorderX - ( cosHdgPlusPiO2 * roadMarkWidth * 0.5 );
+ const y2 = laneBorderY - ( sinHdgPlusPiO2 * roadMarkWidth * 0.5 );
+
+ //////////////////////////////////////////////////////////////
+
+ let roadMarkColor, type;
+
+ const roadMarkTexWidth = 0.5;
+ let roadMarkTexModifierMin1 = 0;
+ let roadMarkTexModifierMax1 = 0;
+
+ let roadMarkTexModifierMin2 = 0;
+ let roadMarkTexModifierMax2 = 0;
+
+ // Define the color of the current road mark:
+ if ( roadMark.getColor() === ( 'standard' ) || roadMark.getColor() === ( 'white' ) ) {
+ roadMarkColor = [ 255, 255, 255 ];
+ } else if ( roadMark.getColor() === 'yellow' ) {
+ roadMarkColor = [ 255, 255, 0 ];
+ } else {
+ roadMarkColor = [ 255, 255, 255 ];
+ }
+
+ // Set the tex coords modifiers based on the type of the road mark
+ if ( roadMark.getType() === TvRoadMarkTypes.NONE ) {
+ type = -1; // debug only
+ return false;
+ }
+
+ if ( roadMark.getType() === TvRoadMarkTypes.SOLID ) {
+
+ type = 0;
+ roadMarkTexModifierMax1 = roadMarkTexWidth;
+
+ } else if ( roadMark.getType() === TvRoadMarkTypes.BROKEN ) {
+
+ type = 0;
+ roadMarkTexModifierMin1 = roadMarkTexWidth;
+ roadMarkTexModifierMax1 = 1;
+
+ } else if ( roadMark.getType() === TvRoadMarkTypes.SOLID_SOLID ) {
+
+ type = 1;
+ roadMarkTexModifierMax1 = roadMarkTexWidth;
+ roadMarkTexModifierMax2 = roadMarkTexWidth;
+
+ } else if ( roadMark.getType() === TvRoadMarkTypes.SOLID_BROKEN ) {
+
+ type = 2;
+ roadMarkTexModifierMin1 = 0;
+ roadMarkTexModifierMax1 = roadMarkTexWidth;
+ roadMarkTexModifierMin2 = roadMarkTexWidth;
+ roadMarkTexModifierMax2 = 1;
+
+ } else if ( roadMark.getType() === TvRoadMarkTypes.BROKEN_SOLID ) {
+
+ type = 3;
+ roadMarkTexModifierMin1 = roadMarkTexWidth;
+ roadMarkTexModifierMax1 = 1;
+ roadMarkTexModifierMin2 = 0;
+ roadMarkTexModifierMax2 = roadMarkTexWidth;
+
+ }
+
+ // ===============================
+ // Add Vertices
+ // ===============================
+ let texX;
+ const texY = s / TvLaneRoadMark.ROADMARK_BROKEN_TILING;
+
+ // // shift the x,y in case it is a 2 line mark
+ // if ( type > 0 ) {
+ //
+ // x1 = laneBorderX + cosHdgPlusPiO2 * 1.5 * width;
+ // y1 = laneBorderY + sinHdgPlusPiO2 * 1.5 * width;
+ //
+ // x2 = laneBorderX + cosHdgPlusPiO2 * 0.25 * width;
+ // y2 = laneBorderY + sinHdgPlusPiO2 * 0.25 * width;
+ //
+ // x3 = laneBorderX - cosHdgPlusPiO2 * 0.25 * width;
+ // y3 = laneBorderY - sinHdgPlusPiO2 * 0.25 * width;
+ //
+ // x4 = laneBorderX - cosHdgPlusPiO2 * 1.5 * width;
+ // y4 = laneBorderY - sinHdgPlusPiO2 * 1.5 * width;
+ // }
+
+
+ ///////////////////////////////////////////////////////////////
+
+ // First vertex
+ texX = roadMarkTexModifierMin1;
+
+ const v1 = new Vertex();
+ v1.Position = new Vector3( x1, y1, elevation );
+ v1.TexCoord = new Vector2( texX, texY );
+
+
+ // Second vertex
+ texX = roadMarkTexModifierMax1;
+
+ const v2 = new Vertex();
+ v2.Position = new Vector3( x2, y2, elevation + height.getOuter() );
+ v2.TexCoord = new Vector2( texX, texY );
+
+ if ( lane.side == TvLaneSide.LEFT ) {
+ this.addVertex( mesh, v1 );
+ this.addVertex( mesh, v2 );
+ } else {
+ this.addVertex( mesh, v2 );
+ this.addVertex( mesh, v1 );
+ }
+
+ // if ( type > 0 ) {
+ //
+ // // First vertex of the second line
+ // texX = roadMarkTexModifierMin2;
+ //
+ // roadMarksGeometry.vertices.push( y3, z, x3 );
+ // roadMarksGeometry.normals.push( 0, 0, 1 );
+ // roadMarksGeometry.colors.push( roadMarkColor );
+ // roadMarksGeometry.texCoords.push( texX, texY );
+ // roadMarksGeometry.currentIndex++;
+ //
+ // // Second vertex of the second line
+ // texX = roadMarkTexModifierMax2;
+ //
+ // roadMarksGeometry.vertices.push( y4, z, x4 );
+ // roadMarksGeometry.normals.push( 0, 0, 1 );
+ // roadMarksGeometry.colors.push( roadMarkColor );
+ // roadMarksGeometry.texCoords.push( texX, texY );
+ // roadMarksGeometry.currentIndex++;
+ //
+ // }
+ }
+
+ private addVertex ( meshData: MeshGeometryData, v1: Vertex ) {
+ meshData.vertices.push( v1.Position.x, v1.Position.y, v1.Position.z + OdBuilderConfig.ROADMARK_ELEVATION_SHIFT );
+ meshData.normals.push( v1.Normal.x, v1.Normal.y, v1.Normal.z );
+ meshData.texCoords.push( v1.TexCoord.x, v1.TexCoord.y );
+ meshData.indices.push( meshData.currentIndex++ );
+ }
+
+ private drawRoadMark ( roadMark: TvLaneRoadMark, mesh: MeshGeometryData, lane: TvLane ) {
+
+ this.createMeshIndices( mesh );
+
+ const material = this.getMaterial( roadMark );
+
+ const geometry = this.getGeometry( mesh );
+
+ roadMark.gameObject = new GameObject( 'RoadMark:', geometry, material );
+
+ roadMark.gameObject.Tag = ObjectTypes.LANE_MARKING;
+
+ roadMark.gameObject.userData.data = lane;
+
+ roadMark.gameObject.userData.lane = lane;
+
+ roadMark.gameObject.userData.roadMark = roadMark;
+
+ lane.gameObject.add( roadMark.gameObject );
+ }
+
+ private getMaterial ( roadMark: TvLaneRoadMark ) {
+
+ let color = COLOR.WHITE;
+
+ switch ( roadMark.color ) {
+
+ case 'standard':
+ color = COLOR.WHITE;
+ break;
+
+ case 'white':
+ color = COLOR.WHITE;
+ break;
+
+ case 'yellow':
+ color = COLOR.YELLOW;
+ break;
+
+ case 'red':
+ color = COLOR.RED;
+ break;
+
+ default:
+ color = COLOR.WHITE;
+ break;
+
+ }
+
+ return new THREE.MeshBasicMaterial( {
+ color: color,
+ map: this.texture,
+ transparent: true,
+ alphaTest: 0.1,
+ wireframe: false,
+ side: THREE.FrontSide
+ } );
+ }
+
+ private getGeometry ( mesh: MeshGeometryData ) {
+
+ const geometry = new THREE.BufferGeometry();
+ const vertices = new Float32Array( mesh.vertices );
+ const colors = new Float32Array( mesh.colors );
+ const normals = new Float32Array( mesh.normals );
+ const faces = new Float32Array( mesh.texCoords );
+
+ geometry.setIndex( mesh.triangles );
+
+ geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
+ geometry.addAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
+ geometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
+ geometry.addAttribute( 'uv', new THREE.Float32BufferAttribute( faces, 2 ) );
+
+ geometry.computeBoundingBox();
+ geometry.computeVertexNormals();
+
+ return geometry;
+ }
+
+ private getCumulativeWidth ( s, lane: TvLane, laneSection: TvLaneSection ) {
+
+ let width = 0;
+
+ switch ( lane.side ) {
+
+ case TvLaneSide.LEFT:
+ width = laneSection.getWidthUptoEnd( lane, s );
+ break;
+
+ case TvLaneSide.CENTER:
+ width = 0;
+ break;
+
+ case TvLaneSide.RIGHT:
+ width = laneSection.getWidthUptoEnd( lane, s );
+ break;
+
+ }
+
+ return width;
+ }
+
+ private clearPreviousMesh ( roadMarks: TvLaneRoadMark[], lane: TvLane ) {
+
+ roadMarks.forEach( mark => {
+
+ if ( mark.gameObject ) {
+
+ SceneService.remove( mark.gameObject );
+
+ lane.gameObject.remove( mark.gameObject );
+
+ }
+
+ mark.gameObject = null;
+
+ } );
+
+ }
+
+ private updateLastCoordinate ( roadMarks: TvLaneRoadMark[], laneSection: TvLaneSection, lane: TvLane ) {
+
+ // setting the last coordinate
+ roadMarks.forEach( ( mark, index ) => {
+
+ if ( index < roadMarks.length - 1 ) {
+
+ mark.lastSCoordinate = roadMarks[ index + 1 ].sOffset;
+
+ } else {
+
+ mark.lastSCoordinate = laneSection.length;
+
+ }
+
+ // console.log( laneSection.roadId, laneSection.id, lane.id, mark.sOffset, mark.lastSCoordinate );
+
+ } );
+
+ // console.log( roadMarks, laneSection );
+ }
+
+ private processRoadMark ( roadmark: TvLaneRoadMark, road: TvRoad, laneSection: TvLaneSection, lane: TvLane, mesh: MeshGeometryData ) {
+
+
+ }
+}
diff --git a/src/app/modules/tv-map/builders/od-road-reference-line-builder.ts b/src/app/modules/tv-map/builders/od-road-reference-line-builder.ts
new file mode 100644
index 00000000..1ff53573
--- /dev/null
+++ b/src/app/modules/tv-map/builders/od-road-reference-line-builder.ts
@@ -0,0 +1,102 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvRoad } from '../models/tv-road.model';
+import { TvPosTheta } from '../models/tv-pos-theta';
+import * as THREE from 'three';
+import { BufferGeometry, Line, Vector3 } from 'three';
+import { Maths } from 'app/utils/maths';
+import { COLOR } from '../../../shared/utils/colors.service';
+
+export class OdRoadReferenceLineBuilder {
+
+ // private points: OdPosTheta[] = [];
+
+ private material = new THREE.LineBasicMaterial( { color: COLOR.RED } );
+
+ line: any;
+ private cache: Map = new Map();
+
+ constructor ( private road: TvRoad ) {
+
+ }
+
+ public clear ( road: TvRoad ) {
+
+ // if ( this.cache.has( road.id ) ) {
+
+ // road.GameObject.remove( this.cache.get( road.id ) );
+
+ // this.cache.deconste( road.id );
+
+ // }
+
+ }
+
+ public create () {
+
+ this.buildRoad( this.road );
+
+ }
+
+ public buildRoad ( road: TvRoad ) {
+
+ this.road = road;
+
+ const tmp = new TvPosTheta();
+ const points: TvPosTheta[] = [];
+
+ for ( let s = 0; s <= this.road.length; s++ ) {
+
+ road.getGeometryCoords( s, tmp );
+ points.push( new TvPosTheta( tmp.x, tmp.y, tmp.hdg ) );
+
+ }
+
+ // last entry
+ road.getGeometryCoords( this.road.length - Maths.Epsilon, tmp );
+ points.push( new TvPosTheta( tmp.x, tmp.y, tmp.hdg ) );
+
+ this.drawLine( road, points );
+
+ }
+
+ private drawLine ( road: TvRoad, positions: TvPosTheta[] ) {
+
+ // this.curve = new THREE.CatmullRomCurve3( this.getVector3Points() );
+
+ // const points = this.curve.getPoints( 50 );
+
+ const points = this.convertToVector3( positions );
+
+ const geometry = new BufferGeometry().setFromPoints( points );
+
+ const object = new Line( geometry, this.material );
+
+ object.name = 'Line';
+
+ object.userData.is_selectable = false;
+
+ if ( this.line ) road.gameObject.remove( this.line );
+
+ this.line = object;
+
+ road.gameObject.add( this.line );
+
+ // this.cache.set( road.id, object );
+ }
+
+ private convertToVector3 ( points: TvPosTheta[] ): Vector3[] {
+
+ const tmp: Vector3[] = [];
+
+ points.forEach( point => {
+
+ tmp.push( new Vector3( point.x, point.y, 0 ) );
+
+ } );
+
+ return tmp;
+ }
+}
diff --git a/src/app/modules/tv-map/builders/od-signal-builder.ts b/src/app/modules/tv-map/builders/od-signal-builder.ts
new file mode 100644
index 00000000..fdedf61c
--- /dev/null
+++ b/src/app/modules/tv-map/builders/od-signal-builder.ts
@@ -0,0 +1,261 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { GameObject } from '../../../core/game-object';
+import { BoxGeometry, CylinderBufferGeometry, CylinderGeometry, FrontSide, MeshBasicMaterial, TextureLoader, Vector3 } from 'three';
+import { TvObjectType } from '../interfaces/i-tv-object';
+import { TvDynamicTypes, TvOrientation, TvUnit } from '../models/tv-common';
+import { TvMapSourceFile } from '../services/tv-map-source-file';
+import { TvRoadSignal } from '../models/tv-road-signal.model';
+import { TvRoad } from '../models/tv-road.model';
+import { COLOR } from '../../../shared/utils/colors.service';
+import { Maths } from '../../../utils/maths';
+import { SignShapeType } from '../services/tv-sign.service';
+import { SnackBar } from '../../../services/snack-bar.service';
+
+export class OdSignalBuilder {
+
+ constructor () {
+ }
+
+ createSignalGameObject ( road: TvRoad, signal: TvRoadSignal, shape?: SignShapeType ) {
+
+ const position = road.getPositionAt( signal.s, signal.t );
+
+ this.createPole( position.toVector3(), signal, road, shape );
+
+ }
+
+ createSignal (
+ assetName: string,
+ signShape: SignShapeType,
+ roadId: number,
+ s: number,
+ t: number,
+ name: string = '',
+ dynamic: TvDynamicTypes = TvDynamicTypes.NO,
+ orientation: TvOrientation = TvOrientation.MINUS,
+ zOffset: number = 0,
+ country: string = 'OpenDrive',
+ type: string = '-1',
+ subtype: string = '-1',
+ value: number = null,
+ unit: TvUnit = null,
+ height: number = 4,
+ width: number = null,
+ text: string = null,
+ hOffset: number = null,
+ pitch: number = null,
+ roll: number = null
+ ): TvRoadSignal {
+
+ const road = TvMapSourceFile.openDrive.getRoadById( roadId );
+
+ const id = road.getRoadSignalCount() + 1;
+
+ const signal = road.addRoadSignal(
+ s, t, id, name, dynamic, orientation, zOffset,
+ country, type, subtype,
+ value, unit,
+ height, width, text,
+ hOffset, pitch, roll
+ );
+
+ signal.addUserData( 'asset_name', assetName );
+ signal.addUserData( 'sign_shape', signShape as string );
+
+ return signal;
+ }
+
+
+ createPole ( position: Vector3, signal: TvRoadSignal, road: TvRoad, shape?: SignShapeType ) {
+
+ const assetName = signal.assetName ? signal.assetName.attr_value : 'default';
+
+ // if shape is supplied
+ // else check for shape in signal data
+ const signShape = shape ? shape : signal.signShape ? signal.signShape : 'default';
+
+ const poleWidth = 0.05;
+ const poleHeight = signal.height;
+
+ const geometry = new CylinderBufferGeometry( poleWidth, poleWidth, poleHeight, 32 );
+ const material = new MeshBasicMaterial( { color: COLOR.DARKGRAY } );
+ const pole = new GameObject( 'Signal', geometry, material );
+
+ let sign: GameObject;
+
+ switch ( signShape ) {
+
+ case SignShapeType.circle:
+ sign = this.createSphericalSignal( assetName, signal );
+ break;
+
+ case SignShapeType.square:
+ sign = this.createSquareSignal( assetName, signal );
+ break;
+
+ case SignShapeType.diamond:
+ sign = this.createSquareSignal( assetName, signal );
+ break;
+
+ case SignShapeType.triangle:
+ sign = this.createSquareSignal( assetName, signal );
+ break;
+
+ case SignShapeType.triangle_inverted:
+ sign = this.createSquareSignal( assetName, signal );
+ break;
+
+ case SignShapeType.square_tilted:
+ sign = this.createTiltedSquareSignal( assetName, signal );
+ break;
+
+ case SignShapeType.rectangle:
+ sign = this.createRectangleSignal( assetName, signal );
+ break;
+ case 'default':
+ break;
+
+ default:
+ SnackBar.show( 'Sign shape type not specified' );
+ console.error( 'Sign shape type not specified' );
+ break;
+ }
+
+ if ( !sign ) return;
+
+ pole.add( sign );
+
+ sign.position.set( 0, poleHeight * 0.5, poleWidth );
+
+ pole.rotateX( 90 * Maths.Deg2Rad );
+
+
+ pole.position.setZ( pole.position.z + ( poleHeight * 0.5 ) );
+
+ signal.gameObject = new GameObject( 'Signal' );
+ signal.gameObject.add( pole );
+ signal.gameObject.position.setX( position.x );
+ signal.gameObject.position.setY( position.y );
+
+ road.gameObject.add( signal.gameObject );
+ }
+
+ createRectangleSignal ( sign: string, signal: TvRoadSignal ): GameObject {
+
+ const geometry = new BoxGeometry( 1, 1.5, 0.05 );
+
+ const signMaterial = this.getSignMaterial( sign );
+
+ return this.createObject( geometry, signMaterial, signal );
+
+ }
+
+ createSquareSignal ( sign: string, signal: TvRoadSignal ): GameObject {
+
+ const geometry = new BoxGeometry( 1, 1, 0.05 );
+
+ const signMaterial = this.getSignMaterial( sign );
+
+ const gameObject = this.createObject( geometry, signMaterial, signal );
+
+ return gameObject;
+ }
+
+ createTiltedSquareSignal ( sign: string, signal: TvRoadSignal ): GameObject {
+
+ const geometry = new CylinderGeometry( 0.5, 0.5, 0.05, 4, 1 );
+
+ const signMaterial = this.getSignMaterial( sign );
+
+ const gameObject = this.createObject( geometry, signMaterial, signal );
+
+ gameObject.rotateX( 90 * Maths.Deg2Rad );
+ gameObject.rotateY( 90 * Maths.Deg2Rad );
+
+ return gameObject;
+ }
+
+ createSphericalSignal ( sign: string, signal: TvRoadSignal ): GameObject {
+
+ const geometry = new CylinderGeometry( 0.5, 0.5, 0.05, 32, 1 );
+
+ const signMaterial = this.getSignMaterial( sign );
+
+ const gameObject = this.createObject( geometry, signMaterial, signal );
+
+ gameObject.rotateX( 90 * Maths.Deg2Rad );
+ gameObject.rotateY( 90 * Maths.Deg2Rad );
+
+ return gameObject;
+ }
+
+ private createObject ( geometry, signMaterial, signal: TvRoadSignal ) {
+
+ const gameObject = new GameObject( 'Signal', geometry, signMaterial );
+
+ gameObject.Tag = TvObjectType[ TvObjectType.SIGNAL ];
+
+ gameObject.OpenDriveType = TvObjectType.SIGNAL;
+
+ gameObject.userData.data = signal;
+
+ return gameObject;
+ }
+
+ private getBlankMaterial () {
+ const blankTexture = new TextureLoader().load( `assets/textures/blank.png` );
+ const blankMaterial = new MeshBasicMaterial( { map: blankTexture, transparent: true, alphaTest: 0.1, side: FrontSide } );
+ }
+
+ private getMetalMaterial () {
+ const metalTexture = new TextureLoader().load( `assets/signs/metal.png` );
+ const metalMaterial = new MeshBasicMaterial( { map: metalTexture, transparent: true, alphaTest: 0.1, side: FrontSide } );
+ }
+
+ private getBackMaterial () {
+ const signBackTexture = new TextureLoader().load( `assets/signs/back_circle.png` );
+ const signBackMaterial = new MeshBasicMaterial( { map: signBackTexture, transparent: true, alphaTest: 0.1, side: FrontSide } );
+ return signBackMaterial;
+ }
+
+ private getSignMaterial ( sign: string ) {
+ const signTexture = new TextureLoader().load( `assets/signs/${sign}.png` );
+ const signMaterial = new MeshBasicMaterial( { map: signTexture, transparent: true, alphaTest: 0.1, side: FrontSide } );
+ return signMaterial;
+ }
+
+ // createPlaneSignal ( sign: string, signal: OdRoadSignal ): GameObject {
+ //
+ // const geometry = new PlaneBufferGeometry( 1, 1, 1 );
+ //
+ // let boxMaterial: MeshBasicMaterial;
+ //
+ // const gameObject = new GameObject( 'Signal', geometry, boxMaterial );
+ //
+ // new TextureLoader().load( `assets/signs/${sign}.png`, function ( texture ) {
+ //
+ // gameObject.material = new MeshBasicMaterial( { map: texture, transparent: true, opacity: 0.9 } );
+ //
+ // ( gameObject.material as Material ).needsUpdate = true;
+ //
+ // } );
+ //
+ // // var position = road.getPositionAt( signal.attr_s, signal.attr_t );
+ //
+ // // gameObject.position.set( position.x, position.y, 0 );
+ //
+ // gameObject.Tag = OpenDriveObjectType[ OpenDriveObjectType.SIGNAL ];
+ //
+ // gameObject.OpenDriveType = OpenDriveObjectType.SIGNAL;
+ //
+ // gameObject.userData.data = signal;
+ //
+ // return gameObject;
+ //
+ // }
+
+
+}
diff --git a/src/app/modules/tv-map/builders/od.textures.ts b/src/app/modules/tv-map/builders/od.textures.ts
new file mode 100644
index 00000000..7e345c9e
--- /dev/null
+++ b/src/app/modules/tv-map/builders/od.textures.ts
@@ -0,0 +1,82 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvLane } from '../models/tv-lane';
+import { TvLaneType } from '../models/tv-common';
+import * as THREE from 'three';
+import { Texture } from 'three';
+
+export class OdTextures {
+
+ static uv_grid = new THREE.TextureLoader().load( 'assets/uv_grid_opengl.jpg' );
+ static asphalt = new THREE.TextureLoader().load( 'assets/flat-asphalt.png' );
+ static border = new THREE.TextureLoader().load( 'assets/flat-asphalt.png' );
+ static sidewalk = new THREE.TextureLoader().load( 'assets/sidewalk.jpg' );
+ static stop = new THREE.TextureLoader().load( 'assets/flat-asphalt.png' );
+ static shoulder = new THREE.TextureLoader().load( 'assets/flat-asphalt.png' );
+ static biking = new THREE.TextureLoader().load( 'assets/flat-asphalt.png' );
+ static restricted = new THREE.TextureLoader().load( 'assets/flat-asphalt.png' );
+ static parking = new THREE.TextureLoader().load( 'assets/flat-asphalt.png' );
+ static terrain = new THREE.TextureLoader().load( 'assets/grass.jpg' );
+
+ // misc
+ static point = new THREE.TextureLoader().load( 'assets/point.png' );
+ static arrow = new THREE.TextureLoader().load( 'assets/arrow.svg' );
+
+ static getLaneTexture ( lane: TvLane ): Texture {
+
+ let texture: Texture;
+
+ switch ( lane.type ) {
+
+ case TvLaneType.none:
+ texture = this.asphalt;
+ break;
+
+ case TvLaneType.driving:
+ texture = this.asphalt;
+ break;
+
+ case TvLaneType.stop:
+ texture = this.stop;
+ break;
+
+ case TvLaneType.shoulder:
+ texture = this.shoulder;
+ break;
+
+ case TvLaneType.biking:
+ texture = this.biking;
+ break;
+
+ case TvLaneType.sidewalk:
+ texture = this.sidewalk;
+ break;
+
+ case TvLaneType.border:
+ texture = this.border;
+ break;
+
+ case TvLaneType.restricted:
+ texture = this.restricted;
+ break;
+
+ case TvLaneType.parking:
+ texture = this.parking;
+ break;
+
+ default:
+ texture = this.terrain;
+ break;
+ }
+
+ texture.wrapS = THREE.RepeatWrapping;
+ texture.wrapT = THREE.RepeatWrapping;
+ texture.mapping = THREE.UVMapping;
+ texture.repeat.set( 1, 1 );
+ texture.anisotropy = 5;
+
+ return texture;
+ }
+}
diff --git a/src/app/modules/tv-map/dialogs/new-road-dialog/new-road-dialog.component.css b/src/app/modules/tv-map/dialogs/new-road-dialog/new-road-dialog.component.css
new file mode 100644
index 00000000..707523f7
--- /dev/null
+++ b/src/app/modules/tv-map/dialogs/new-road-dialog/new-road-dialog.component.css
@@ -0,0 +1,4 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
diff --git a/src/app/modules/tv-map/dialogs/new-road-dialog/new-road-dialog.component.html b/src/app/modules/tv-map/dialogs/new-road-dialog/new-road-dialog.component.html
new file mode 100644
index 00000000..b7b50f5f
--- /dev/null
+++ b/src/app/modules/tv-map/dialogs/new-road-dialog/new-road-dialog.component.html
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
Create New Road
+
+
Or
+
+
Open From Computer
+
+
+
Open From Computer
+
+
+
+
+
+
+
+
+ Recent Files
+
+ folder
+ {{file.path}}
+ {{file.type}}
+
+
+
+
\ No newline at end of file
diff --git a/src/app/modules/tv-map/dialogs/new-road-dialog/new-road-dialog.component.ts b/src/app/modules/tv-map/dialogs/new-road-dialog/new-road-dialog.component.ts
new file mode 100644
index 00000000..94de9261
--- /dev/null
+++ b/src/app/modules/tv-map/dialogs/new-road-dialog/new-road-dialog.component.ts
@@ -0,0 +1,95 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Component, OnInit } from '@angular/core';
+import { MatDialogRef } from '@angular/material';
+import { ElectronService } from 'ngx-electron';
+import { RecentFileService } from 'app/services/recent-file.service';
+import { IFile } from 'app/core/models/file';
+import { MainFileService } from 'app/services/main-file.service';
+
+@Component( {
+ selector: 'app-new-road-dialog',
+ templateUrl: './new-road-dialog.component.html',
+ styleUrls: [ './new-road-dialog.component.css' ]
+} )
+export class NewRoadDialogComponent implements OnInit {
+
+ constructor (
+ private dialogRef: MatDialogRef,
+ private electron: ElectronService,
+ private recentFileService: RecentFileService,
+ private mainFileService: MainFileService
+ ) {
+ }
+
+ get recentFiles () {
+
+ return this.recentFileService.recentFiles;
+
+ }
+
+ get isElectronApp () {
+
+ return this.electron.isElectronApp;
+
+ }
+
+ ngOnInit () {
+
+ // console.log( this.recentFileService.recentFiles );
+
+ }
+
+ createNew () {
+
+ this.mainFileService.newFile();
+
+ // this.oscService.newFile();
+
+ this.dialogRef.close();
+
+ }
+
+ openFromComputer () {
+
+ this.mainFileService.showOpenWindow();
+
+ this.dialogRef.close();
+
+ }
+
+ openFile ( file: IFile ) {
+
+ this.mainFileService.openFromPath( file.path, () => {
+
+ this.dialogRef.close();
+
+ } );
+
+ }
+
+ public fileChange ( event ) {
+
+ const self = this;
+
+ const reader = new FileReader();
+
+ if ( event.target.files && event.target.files.length > 0 ) {
+
+ const file = event.target.files[ 0 ];
+
+ reader.readAsText( file );
+
+ reader.onload = ( data ) => {
+
+ self.mainFileService.importViaContent( reader.result as string );
+
+ self.dialogRef.close();
+
+ };
+ }
+
+ }
+}
diff --git a/src/app/modules/tv-map/interfaces/i-tv-object.ts b/src/app/modules/tv-map/interfaces/i-tv-object.ts
new file mode 100644
index 00000000..27e0f485
--- /dev/null
+++ b/src/app/modules/tv-map/interfaces/i-tv-object.ts
@@ -0,0 +1,19 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+export interface ITvObject {
+
+ OpenDriveType: TvObjectType;
+
+ getType (): TvObjectType;
+}
+
+export enum TvObjectType {
+ ROAD = 1,
+ LANE = 2,
+ ROADMARK = 3,
+ SIGNAL = 4,
+ OBJECT = 5,
+ VEHICLE = 6,
+}
\ No newline at end of file
diff --git a/src/app/modules/tv-map/models/geometries/tv-abstract-road-geometry.ts b/src/app/modules/tv-map/models/geometries/tv-abstract-road-geometry.ts
new file mode 100644
index 00000000..0573c7da
--- /dev/null
+++ b/src/app/modules/tv-map/models/geometries/tv-abstract-road-geometry.ts
@@ -0,0 +1,254 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvGeometryType, TvSide } from '../tv-common';
+import { TvPosTheta } from '../tv-pos-theta';
+import { Curve, Vector2, Vector3 } from 'three';
+import { Maths } from '../../../../utils/maths';
+import { _Math } from 'three/src/math/Math';
+
+export abstract class TvAbstractRoadGeometry {
+
+ public readonly uuid: string;
+
+ public attr_S;
+ public attr_x;
+ public attr_y;
+ public attr_hdg;
+ public attr_length;
+ protected _s2;
+ protected _geometryType: TvGeometryType;
+
+ constructor ( s: number, x: number, y: number, hdg: number, length: number ) {
+
+ this.attr_S = s;
+ this.attr_x = x;
+ this.attr_y = y;
+ this.attr_hdg = hdg;
+ this.attr_length = length;
+
+ this._s2 = s + length;
+
+ this.uuid = _Math.generateUUID();
+ }
+
+ get s2 () {
+
+ return this._s2;
+
+ }
+
+ get startV3 (): Vector3 {
+
+ return new Vector3( this.x, this.y, 0 );
+
+ }
+
+ get geometryType (): TvGeometryType {
+ return this._geometryType;
+ }
+
+ set geometryType ( type: TvGeometryType ) {
+ this._geometryType = type;
+ }
+
+ get s () {
+
+ return this.attr_S;
+
+ }
+
+ set s ( value: number ) {
+
+ this.attr_S = value;
+
+ this._s2 = this.attr_S + this.attr_length;
+ }
+
+ get x () {
+
+ return this.attr_x;
+
+ }
+
+ set x ( value: number ) {
+
+ this.attr_x = value;
+
+ }
+
+ get y () {
+
+ return this.attr_y;
+
+ }
+
+ set y ( value: number ) {
+
+ this.attr_y = value;
+
+ }
+
+ get hdg () {
+
+ return this.attr_hdg;
+
+ }
+
+ set hdg ( value: number ) {
+
+ this.attr_hdg = value;
+
+ this.computeVars();
+ }
+
+ get length () {
+
+ return this.attr_length;
+
+ }
+
+ set length ( value: number ) {
+
+ this.attr_length = value;
+
+ this.computeVars();
+ }
+
+ static getTypeAsString ( geometryType: TvGeometryType ): string {
+
+ if ( geometryType === TvGeometryType.LINE ) {
+
+ return 'line';
+
+ } else if ( geometryType === TvGeometryType.ARC ) {
+
+ return 'arc';
+
+ } else if ( geometryType === TvGeometryType.SPIRAL ) {
+
+ return 'spiral';
+
+ } else if ( geometryType === TvGeometryType.POLY3 ) {
+
+ return 'poly3';
+
+ } else if ( geometryType === TvGeometryType.PARAMPOLY3 ) {
+
+ return 'paramPoly3';
+
+ }
+
+ }
+
+ setBase ( s: number, x: number, y: number, hdg: number, length: number, recalculate: boolean ) {
+
+ this.attr_S = s;
+ this.attr_x = x;
+ this.attr_y = y;
+ this.attr_hdg = hdg;
+ this.attr_length = length;
+
+ this._s2 = s + length;
+
+ if ( recalculate ) {
+ this.computeVars();
+ }
+ }
+
+ checkInterval ( sCheck: number ): boolean {
+
+ if ( ( sCheck >= this.attr_S ) && ( sCheck <= this.s2 ) ) {
+
+ return true;
+
+ }
+
+ return false;
+ }
+
+ abstract getCoords ( sCheck, posTheta: TvPosTheta );
+
+ abstract computeVars ();
+
+ abstract getCurve (): Curve;
+
+ public updateControlPoints () {
+
+ }
+
+ public getNearestPointFrom ( x: number, y: number, posTheta?: TvPosTheta ): Vector2 {
+
+ return this.loopToGetNearestPoint( x, y, posTheta );
+
+ }
+
+ protected loopToGetNearestPoint ( x: number, y: number, refPosTheta?: TvPosTheta ): Vector2 {
+
+ let nearestPoint: Vector2 = null;
+
+ const point = new Vector2( x, y );
+
+ // const curve = this.getCurve();
+
+ const tmpPosTheta = new TvPosTheta();
+
+ let minDistance = Number.MAX_SAFE_INTEGER;
+
+ // const curveLength = curve.getLength();
+
+ for ( let s = this.s; s <= this.s2; s++ ) {
+
+ this.getCoords( s, tmpPosTheta );
+
+ const distance = tmpPosTheta.toVector2().distanceTo( point );
+
+ if ( distance < minDistance ) {
+
+ minDistance = distance;
+ nearestPoint = tmpPosTheta.toVector2();
+
+ if ( refPosTheta ) {
+
+ refPosTheta.x = x;
+ refPosTheta.y = y;
+ refPosTheta.s = s;
+ refPosTheta.t = distance;
+ refPosTheta.hdg = tmpPosTheta.hdg;
+ }
+ }
+ }
+
+ if ( nearestPoint == null ) {
+
+ throw new Error( 'could not find the nearest point' );
+
+ } else {
+
+ if ( refPosTheta ) {
+
+ // calculating the lane side for correct value of t
+
+ const tmp1 = new TvPosTheta();
+ const tmp2 = new TvPosTheta();
+
+ this.getCoords( refPosTheta.s, tmp1 );
+ this.getCoords( refPosTheta.s + 1, tmp2 );
+
+ const side = Maths.direction( tmp1.toVector3(), tmp2.toVector3(), refPosTheta.toVector3() );
+
+ if ( side == TvSide.RIGHT ) refPosTheta.t *= -1;
+
+ }
+
+ }
+
+ return nearestPoint;
+ }
+
+ polyeval ( t: number, v: Vector3 ): number {
+
+ return ( v.x ) + ( v.y * t ) + ( v.z * t * t );
+ }
+}
diff --git a/src/app/modules/tv-map/models/geometries/tv-arc-geometry.spec.ts b/src/app/modules/tv-map/models/geometries/tv-arc-geometry.spec.ts
new file mode 100644
index 00000000..f399380f
--- /dev/null
+++ b/src/app/modules/tv-map/models/geometries/tv-arc-geometry.spec.ts
@@ -0,0 +1,72 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvRoad } from '../tv-road.model';
+import { TvPosTheta } from '../tv-pos-theta';
+import { TvMap } from '../tv-map.model';
+import { TvMapQueries } from '../../queries/tv-map-queries';
+import { TvMapSourceFile } from '../../services/tv-map-source-file';
+
+describe( 'OdArcGeometry', () => {
+
+ let openDrive: TvMap;
+ let road: TvRoad;
+ let pose = new TvPosTheta();
+
+ beforeEach( () => {
+
+ openDrive = new TvMap();
+
+ TvMapSourceFile.openDrive = openDrive;
+
+ road = new TvRoad( '', 10, 1, -1 );
+
+ road.addPlanView();
+
+ } );
+
+ it( 'should give nearest point on arc', () => {
+
+ // add roads
+ // 3 arc roads
+ // ==========|==========|==========
+ const road1 = openDrive.addRoad( '', 10, 1, -1 );
+ const road2 = openDrive.addRoad( '', 10, 2, -1 );
+ const road3 = openDrive.addRoad( '', 10, 3, -1 );
+
+ road1.addPlanView();
+ road2.addPlanView();
+ road3.addPlanView();
+
+ road1.addGeometryArc( 0, 0, 0, 0, 10, 0 );
+ road2.addGeometryArc( 0, 10, 0, 0, 10, 0 );
+ road3.addGeometryArc( 0, 20, 0, 0, 10, 0 );
+
+ let roadResult = TvMapQueries.getRoadByCoords( 0, 0 );
+ expect( roadResult ).not.toBeNull();
+ expect( roadResult.id ).toBe( 1 );
+
+ roadResult = TvMapQueries.getRoadByCoords( 1, 9 );
+ expect( roadResult ).not.toBeNull();
+ expect( roadResult.id ).toBe( 1 );
+
+ roadResult = TvMapQueries.getRoadByCoords( 11, 0 );
+ expect( roadResult ).not.toBeNull();
+ expect( roadResult.id ).toBe( 2 );
+
+ roadResult = TvMapQueries.getRoadByCoords( 11, 10 );
+ expect( roadResult ).not.toBeNull();
+ expect( roadResult.id ).toBe( 2 );
+
+ roadResult = TvMapQueries.getRoadByCoords( 21, 0 );
+ expect( roadResult ).not.toBeNull();
+ expect( roadResult.id ).toBe( 3 );
+
+ roadResult = TvMapQueries.getRoadByCoords( 21, 10 );
+ expect( roadResult ).not.toBeNull();
+ expect( roadResult.id ).toBe( 3 );
+
+ } );
+
+} );
diff --git a/src/app/modules/tv-map/models/geometries/tv-arc-geometry.ts b/src/app/modules/tv-map/models/geometries/tv-arc-geometry.ts
new file mode 100644
index 00000000..e4bfad65
--- /dev/null
+++ b/src/app/modules/tv-map/models/geometries/tv-arc-geometry.ts
@@ -0,0 +1,292 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvGeometryType } from '../tv-common';
+import { Curve, Object3D, SplineCurve, Vector2, Vector3 } from 'three';
+import { TvPosTheta } from '../tv-pos-theta';
+import { Maths } from '../../../../utils/maths';
+import { TvAbstractRoadGeometry } from './tv-abstract-road-geometry';
+
+export class TvArcGeometry extends TvAbstractRoadGeometry {
+
+ public attr_curvature;
+
+ public cp1: Object3D;
+ public cp2: Object3D;
+ public cp3: Object3D;
+
+ private curve: Curve;
+
+ constructor ( s: number, x: number, y: number, hdg: number, length: number, curvature: number ) {
+
+ super( s, x, y, hdg, length );
+
+ this._geometryType = TvGeometryType.ARC;
+
+ if ( curvature == 0 ) {
+ this.attr_curvature = Maths.Epsilon;
+ } else {
+ this.attr_curvature = curvature;
+ }
+
+ this.computeVars();
+ }
+
+ get curvature (): number {
+ return this.attr_curvature;
+ }
+
+ set curvature ( value: number ) {
+
+ this.attr_curvature = value;
+
+ this.computeVars();
+
+ }
+
+ get radius (): number {
+
+ return 1.0 / Math.abs( this.attr_curvature );
+
+ }
+
+ get centre (): Vector3 {
+
+ const clockwise = this.attr_curvature < 0;
+
+ const circleX = this.x - this.radius * Math.cos( this.hdg - Maths.M_PI_2 ) * ( clockwise ? -1 : 1 );
+ const circleY = this.y - this.radius * Math.sin( this.hdg - Maths.M_PI_2 ) * ( clockwise ? -1 : 1 );
+
+ return new Vector3( circleX, circleY, 0 );
+ }
+
+ get startV3 (): Vector3 {
+
+ return new Vector3( this.x, this.y, 0 );
+
+ }
+
+ get middleV3 (): Vector3 {
+
+ const pos = new TvPosTheta();
+
+ this.getCoords( this.s2 / 2, pos );
+
+ return pos.toVector3();
+ }
+
+ get endV3 (): Vector3 {
+
+ const pos = new TvPosTheta();
+
+ this.getCoords( this.s2, pos );
+
+ return pos.toVector3();
+
+ }
+
+ get headingEnd (): Vector3 {
+
+ // find the end of the chord line
+ const x = this.attr_x + Math.cos( this.attr_hdg ) * this.s2;
+ const y = this.attr_y + Math.sin( this.attr_hdg ) * this.s2;
+
+ return new Vector3( x, y, 0 );
+
+ }
+
+ updateControlPoints () {
+
+ if ( this.cp1 ) this.cp1.position.copy( this.startV3 );
+ if ( this.cp2 ) this.cp2.position.copy( this.middleV3 );
+ if ( this.cp3 ) this.cp3.position.copy( this.endV3 );
+
+ }
+
+ getCoords ( s, odPosTheta: TvPosTheta ) {
+
+ // calculate the first geometry element for the returning geometries
+ var ds = s - this.s;
+ var curvature = this.curvature;
+ var theta = ds * curvature;
+ var radius = 1 / Math.abs( this.curvature );
+ var rotation = this.hdg - Math.sign( curvature ) * Math.PI / 2;
+
+ const retX = this.x - radius * Math.cos( rotation ) + radius * Math.cos( rotation + theta );
+ const retY = this.y - radius * Math.sin( rotation ) + radius * Math.sin( rotation + theta );
+
+ odPosTheta.x = retX;
+ odPosTheta.y = retY;
+ odPosTheta.hdg = this.hdg + theta;
+
+ return this.geometryType;
+ }
+
+ computeVars () {
+
+ this._s2 = this.s + this.length;
+
+ // this.startAngle = this.hdg;
+ //
+ // this.radius = 0.0;
+ //
+ // // if curvature is 0, radius is also 0, otherwise, radius is 1/curvature
+ // if ( Math.abs( this.attr_curvature ) > 1.00e-15 ) {
+ //
+ // // radius = Math.abs( 1.0 / this.attr_curvature );
+ //
+ // this.radius = 1.0 / Math.abs( this.attr_curvature );
+ //
+ // }
+
+ // this.clockwise = this.attr_curvature < 0;
+ //
+ // this.arcAngle = this.length * this.curvature;
+ //
+ // this.circleX = this.x - this.radius * Math.cos( this.startAngle - Maths.M_PI_2 ) * (this.clockwise ? -1 : 1);
+ // this.circleY = this.y - this.radius * Math.sin( this.startAngle - Maths.M_PI_2 ) * (this.clockwise ? -1 : 1);
+
+ // // calculate the start angle for the arc plot
+ // // if ( this.attr_curvature <= 0 ) {
+ // if ( this.attr_curvature < 0 ) {
+ //
+ // this.startAngle = this.attr_hdg + Maths.M_PI_2;
+ //
+ // } else {
+ //
+ // this.startAngle = this.attr_hdg - Maths.M_PI_2;
+ //
+ // }
+ //
+ // const cos = Math.cos( this.startAngle - Maths.M_PI );
+ // const sin = Math.sin( this.startAngle - Maths.M_PI );
+ //
+ // this.circleX = this.attr_x + cos * this.radius;
+ // this.circleY = this.attr_y + sin * this.radius;
+ }
+
+ findS ( x, y ) {
+
+ // not working just for reference
+
+ // // calculate the first geometry element for the returning geometries
+ // // var ds = s - this.s;
+ // var curvature = this.curvature;
+ // // var theta = ds * curvature;
+ // var radius = 1 / Math.abs( this.curvature );
+ // var rotation = this.hdg - Math.sign( curvature ) * Math.PI / 2;
+
+ // let c = ( ( x - this.x ) + ( radius * Math.cos( rotation ) ) ) / radius;
+ // let a = Math.cos( rotation );
+ // let b = Math.sin( rotation );
+
+ // let alphaCos = Math.acos( a / ( Math.sqrt( a * a + b * b ) ) ); // positive
+ // let alphaSin = Math.asin( b / ( Math.sqrt( a * a + b * b ) ) ); // negative
+
+ // let thetaCos = Math.asin( c / ( Math.sqrt( a * a + b * b ) ) ) - alphaCos;
+ // let thetaSin = Math.asin( c / ( Math.sqrt( a * a + b * b ) ) ) - alphaSin;
+
+ // let thetaNew = Math.asin( c / ( Math.sqrt( a * a + b * b ) ) );
+
+ // let theta = x * this.curvature;
+
+ // let thetaCosCorrect = Maths.approxEquals( theta, thetaCos );
+ // let thetaSinCorrect = Maths.approxEquals( theta, thetaSin );
+ // let thetaNewCorrect = Maths.approxEquals( theta, thetaNew );
+
+ // let actualS = theta / curvature;
+ // let cosS = theta / curvature;
+ // let sinS = theta / curvature;
+ // let foundS = thetaNew / curvature;
+
+ // let foundSCorrect = Maths.approxEquals( actualS, foundS );
+
+ }
+
+ getCurve (): Curve {
+
+ if ( this.curve != null ) return this.curve;
+
+ const points: Vector2[] = [];
+ const posTheta = new TvPosTheta();
+
+ for ( let sCoordinate = this.s; sCoordinate <= this.s2; sCoordinate++ ) {
+
+ this.getCoords( sCoordinate, posTheta );
+ points.push( posTheta.toVector2() );
+
+ }
+
+ this.getCoords( this.s2 - Maths.Epsilon, posTheta );
+ points.push( posTheta.toVector2() );
+
+ return this.curve = new SplineCurve( points );
+ }
+
+ // getCoords ( sCheck, odPosTheta: OdPosTheta ) {
+ // // s from the beginning of the segment
+ // const currentLength = sCheck - this.attr_S;
+ // let endAngle = this.startAngle;
+ // let radius = 0.0;
+ //
+ // let retX, retY, retHdg;
+ //
+ // // if curvature is 0, radius is also 0, so don't add anything to the initial radius,
+ // // otherwise, radius is 1/curvature so the central angle can be calculated and added to the initial direction
+ // if ( Math.abs( this.attr_curvature ) > 1.00e-15 ) {
+ //
+ // endAngle += currentLength / (1.0 / this.attr_curvature);
+ //
+ // radius = Math.abs( 1.0 / this.attr_curvature );
+ //
+ // }
+ //
+ // const cos = Math.cos( endAngle );
+ // const sin = Math.sin( endAngle );
+ //
+ // retX = this.circleX + cos * radius;
+ // retY = this.circleY + sin * radius;
+ //
+ //
+ // // heading at the given position
+ // if ( this.attr_curvature <= 0 ) {
+ //
+ // retHdg = endAngle - Maths.M_PI_2;
+ //
+ // } else {
+ //
+ // retHdg = endAngle + Maths.M_PI_2;
+ //
+ // }
+ //
+ // // // tangent to arc (road direction)
+ // // const theta = this.clockwise ? this.startAngle - sCheck / this.radius : this.startAngle + sCheck / this.radius;
+ // //
+ // // // angle arc subtends at center
+ // // const arcTheta = theta - Maths.M_PI_2;11
+ // //
+ // // const cos = Math.cos( theta );
+ // // const sin = Math.sin( theta );
+ // //
+ // // // lateralOffset is perpendicular to road
+ // // // NA
+ // // const r = this.radius - 0 * (this.clockwise ? -1 : 1);
+ // //
+ // // odPosTheta.x = this.circleX + r * Math.cos( arcTheta ) * (this.clockwise ? -1 : 1);
+ // //
+ // // odPosTheta.y = this.circleY + r * Math.sin( arcTheta ) * (this.clockwise ? -1 : 1);
+ // //
+ // // odPosTheta.hdg =
+
+ setAll ( s: number, x: number, y: number, hdg: number, length: number, curvature: number ) {
+
+ this.setBase( s, x, y, hdg, length, false );
+
+ this.attr_curvature = curvature;
+
+ this.computeVars();
+
+ }
+
+}
diff --git a/src/app/modules/tv-map/models/geometries/tv-line-geometry.spec.ts b/src/app/modules/tv-map/models/geometries/tv-line-geometry.spec.ts
new file mode 100644
index 00000000..a6629ebc
--- /dev/null
+++ b/src/app/modules/tv-map/models/geometries/tv-line-geometry.spec.ts
@@ -0,0 +1,188 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvRoad } from '../tv-road.model';
+import { TvPosTheta } from '../tv-pos-theta';
+import { TvMap } from '../tv-map.model';
+import { TvMapQueries } from '../../queries/tv-map-queries';
+import { TvMapSourceFile } from '../../services/tv-map-source-file';
+
+describe( 'OdLineGeometry', () => {
+
+ let openDrive: TvMap;
+ let road: TvRoad;
+ let pose = new TvPosTheta();
+
+ beforeEach( () => {
+
+ openDrive = new TvMap();
+
+ TvMapSourceFile.openDrive = openDrive;
+
+ road = new TvRoad( '', 10, 1, -1 );
+
+ road.addPlanView();
+
+ } );
+
+ it( 'should give correct coordinates with 0-degree hdg', () => {
+
+ road.addGeometryLine( 0, 0, 1, 0, 10 );
+
+ const posTheta = new TvPosTheta();
+
+ road.getGeometryCoords( 0, posTheta );
+
+ expect( posTheta.x ).toBe( 0 );
+ expect( posTheta.y ).toBe( 1 );
+ expect( posTheta.hdg ).toBe( 0 );
+
+ road.getGeometryCoords( 10, posTheta );
+
+ expect( posTheta.x ).toBe( 10 );
+ expect( posTheta.y ).toBe( 1 );
+ expect( posTheta.hdg ).toBe( 0 );
+
+ } );
+
+ it( 'should give correct coordinates with 90degree hdg', () => {
+
+ const hdg = 90 * (Math.PI / 180);
+
+ road.addGeometryLine( 0, 1, 0, hdg, 10 );
+
+ const posTheta = new TvPosTheta();
+
+ road.getGeometryCoords( 0, posTheta );
+
+ expect( posTheta.x ).toBe( 1 );
+ expect( posTheta.y ).toBe( 0 );
+ expect( posTheta.hdg ).toBe( hdg );
+
+ road.getGeometryCoords( 10, posTheta );
+
+ expect( Math.round( posTheta.x ) ).toBe( 1 );
+ expect( Math.round( posTheta.y ) ).toBe( 10 );
+ expect( posTheta.hdg ).toBe( hdg );
+
+ } );
+
+ it( 'should give correct coordinates with 180 degree hdg', () => {
+
+ const hdg = 180 * (Math.PI / 180);
+
+ road.addGeometryLine( 0, 0, 0, hdg, 10 );
+
+ const posTheta = new TvPosTheta();
+
+ road.getGeometryCoords( 0, posTheta );
+
+ expect( Math.round( posTheta.x ) ).toBe( 0 );
+ expect( posTheta.y ).toBe( 0 );
+ expect( posTheta.hdg ).toBe( hdg );
+
+ road.getGeometryCoords( 10, posTheta );
+
+ expect( Math.round( posTheta.x ) ).toBe( -10 );
+ expect( Math.round( posTheta.y ) ).toBe( 0 );
+ expect( posTheta.hdg ).toBe( hdg );
+
+ } );
+
+ it( 'should give correct coordinates for s and t with 0 degree hdg', () => {
+
+ const hdg = 0;
+ const x = 0;
+ const y = 0;
+ const s = 0;
+ const length = 10;
+
+ let t = 0;
+
+ road.addGeometryLine( s, x, y, hdg, length );
+
+ road.getGeometryCoordsAt( s, t, pose );
+
+ expect( Math.round( pose.x ) ).toBe( 0 );
+ expect( pose.y ).toBe( 0 );
+
+ t = 1;
+
+ road.getGeometryCoordsAt( s, t, pose );
+
+ expect( Math.round( pose.x ) ).toBe( 0 );
+ expect( pose.y ).toBe( t );
+
+ } );
+
+ it( 'should give correct coordinates for s and t with 90 degree hdg', () => {
+
+ const hdg = 90 * (Math.PI / 180);
+ const x = 0;
+ const y = 0;
+ const s = 0;
+ const length = 10;
+
+ let t = 0;
+
+ road.addGeometryLine( s, x, y, hdg, length );
+
+ road.getGeometryCoordsAt( s, t, pose );
+
+ expect( Math.round( pose.x ) ).toBe( 0 );
+ expect( Math.round( pose.y ) ).toBe( 0 );
+
+ t = 1;
+
+ road.getGeometryCoordsAt( s, t, pose );
+
+ expect( Math.round( pose.x ) ).toBe( -1 );
+ expect( Math.round( pose.y ) ).toBe( 0 );
+
+ } );
+
+ it( 'should give nearest point on line', () => {
+
+ // add roads
+ // 3 straight road
+ // ==========|==========|==========
+ const road1 = openDrive.addRoad( '', 10, 1, -1 );
+ const road2 = openDrive.addRoad( '', 10, 2, -1 );
+ const road3 = openDrive.addRoad( '', 10, 3, -1 );
+
+ road1.addPlanView();
+ road2.addPlanView();
+ road3.addPlanView();
+
+ road1.addGeometryLine( 0, 0, 0, 0, 10 );
+ road2.addGeometryLine( 0, 10, 0, 0, 10 );
+ road3.addGeometryLine( 0, 20, 0, 0, 10 );
+
+ let roadResult = TvMapQueries.getRoadByCoords( 0, 0 );
+ expect( roadResult ).not.toBeNull();
+ expect( roadResult.id ).toBe( 1 );
+
+ roadResult = TvMapQueries.getRoadByCoords( 1, 10 );
+ expect( roadResult ).not.toBeNull();
+ expect( roadResult.id ).toBe( 1 );
+
+ roadResult = TvMapQueries.getRoadByCoords( 11, 0 );
+ expect( roadResult ).not.toBeNull();
+ expect( roadResult.id ).toBe( 2 );
+
+ roadResult = TvMapQueries.getRoadByCoords( 11, 10 );
+ expect( roadResult ).not.toBeNull();
+ expect( roadResult.id ).toBe( 2 );
+
+ roadResult = TvMapQueries.getRoadByCoords( 21, 0 );
+ expect( roadResult ).not.toBeNull();
+ expect( roadResult.id ).toBe( 3 );
+
+ roadResult = TvMapQueries.getRoadByCoords( 21, 10 );
+ expect( roadResult ).not.toBeNull();
+ expect( roadResult.id ).toBe( 3 );
+
+ } );
+
+} );
diff --git a/src/app/modules/tv-map/models/geometries/tv-line-geometry.ts b/src/app/modules/tv-map/models/geometries/tv-line-geometry.ts
new file mode 100644
index 00000000..b6a0e1c0
--- /dev/null
+++ b/src/app/modules/tv-map/models/geometries/tv-line-geometry.ts
@@ -0,0 +1,227 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvGeometryType } from '../tv-common';
+import { Curve, LineCurve, Vector2, Vector3 } from 'three';
+import { TvPosTheta } from '../tv-pos-theta';
+import { TvAbstractRoadGeometry } from './tv-abstract-road-geometry';
+import { Maths } from 'app/utils/maths';
+import { TvArcGeometry } from './tv-arc-geometry';
+
+export class TvLineGeometry extends TvAbstractRoadGeometry {
+
+ private cosTheta: number;
+ private sinTheta: number;
+
+ constructor ( s: number, x: number, y: number, hdg: number, length: number ) {
+
+ super( s, x, y, hdg, length );
+
+ this._geometryType = TvGeometryType.LINE;
+
+ this.computeVars();
+
+ }
+
+ get start (): Vector2 {
+
+ return new Vector2( this.x, this.y );
+
+ }
+
+ get end (): Vector2 {
+
+ // find the end of the chord line
+ const x = this.attr_x + Math.cos( this.attr_hdg ) * this.s2;
+ const y = this.attr_y + Math.sin( this.attr_hdg ) * this.s2;
+
+ return new Vector2( x, y );
+
+ }
+
+ get endV3 (): Vector3 {
+
+ // find the end of the chord line
+ const x = this.attr_x + Math.cos( this.attr_hdg ) * this.s2;
+ const y = this.attr_y + Math.sin( this.attr_hdg ) * this.s2;
+
+ return new Vector3( x, y, 0 );
+
+ }
+
+ getCurve (): Curve {
+
+ return new LineCurve( this.start, this.end );
+
+ }
+
+ computeVars () {
+
+ this._s2 = this.s + this.length;
+
+ this.sinTheta = Math.sin( this.hdg );
+ this.cosTheta = Math.cos( this.hdg );
+
+ }
+
+ clone () {
+
+ }
+
+ setAll ( s: number, x: number, y: number, hdg: number, length: number ) {
+
+ this.setBase( s, x, y, hdg, length, false );
+
+ this.computeVars();
+ }
+
+ getCoords ( sCheck, odPosTheta: TvPosTheta ): TvGeometryType {
+
+ const lateralOffset = 0;
+
+ const newLength = sCheck - this.attr_S;
+
+ // find the end of the chord line
+ odPosTheta.x = this.attr_x + Math.cos( this.attr_hdg ) * newLength;
+ odPosTheta.y = this.attr_y + Math.sin( this.attr_hdg ) * newLength;
+ odPosTheta.hdg = this.attr_hdg;
+
+ // odPosTheta.x = this.x + sCheck * this.cosTheta - lateralOffset * this.sinTheta;
+ // odPosTheta.y = this.y + sCheck * this.sinTheta + lateralOffset * this.cosTheta;
+ // odPosTheta.hdg = this.hdg;
+
+ return this.geometryType;
+
+ }
+
+ // closestPointToPointParmeter ( point: Vector3 ) {
+ //
+ // const _startP = new Vector3();
+ // const _startEnd = new Vector3();
+ //
+ // const start = new Vector3( this.start.x, this.start.y, 0 );
+ // const end = new Vector3( this.end.x, this.end.y, 0 );
+ //
+ // _startP.subVectors( point, start );
+ // _startEnd.subVectors( end, start );
+ //
+ // var startEnd2 = _startEnd.dot( _startEnd );
+ // var startEnd_startP = _startEnd.dot( _startP );
+ //
+ // const t = startEnd_startP / startEnd2;
+ //
+ // return t;
+ // }
+
+ // getNearestPointFrom ( x2: number, y2: number ): Vector2 {
+ //
+ // return this.loopToGetNearestPoint( x2, y2 );
+ //
+ // // const point = new Vector3( x2, y2, 0 );
+ // //
+ // // const t = this.closestPointToPointParmeter( point );
+ // //
+ // // const start = new Vector3( this.start.x, this.start.y, 0 );
+ // //
+ // // const delta = new Vector3();
+ // //
+ // // const res = delta.multiplyScalar( t ).add( start );
+ // //
+ // // return new Vector2( res.x, res.y );
+ // }
+
+ getStCoordinates ( posTheta: TvPosTheta ) {
+
+ const objPosition = new Vector2( posTheta.x, posTheta.y );
+
+ // get nearest point on road from this new point
+ const nearestPointOnRoad = this.getNearestPointFrom( objPosition.x, objPosition.y );
+
+ // Debug.log( nearestPointOnRoad );
+
+ // s value is simply the distance from start to the nearestPointOnRoad
+ const s = ( new Vector2( this.x, this.y ) ).distanceTo( nearestPointOnRoad );
+
+ // TODO: Find negative t value as well
+ // t value is simple the distance from nearPointOnRoad to object position
+ const t = nearestPointOnRoad.distanceTo( objPosition );
+
+ posTheta.s = s;
+ posTheta.t = t;
+ posTheta.hdg = this.hdg;
+
+ return new Vector2( s, t );
+ }
+
+ findS ( x, y ) {
+
+ // not working just for reference
+
+ // const s1 = ( x - this.x ) / Math.cos( this.hdg );
+ // const s2 = ( y - this.y ) / Math.sin( this.hdg );
+
+ // let s1Valid = false;
+ // let s2Valid = false;
+
+ // if ( isNaN( s1 ) || ( s1 >= 0 && s1 <= this.length ) ) {
+ // s1Valid = true;
+ // }
+
+ // if ( isNaN( s2 ) || ( s2 >= 0 && s2 <= this.length ) ) {
+ // s2Valid = true;
+ // }
+
+ // return [ s1, s2, s1Valid && s2Valid, s1Valid, s2Valid ];
+ }
+
+ /**
+ *
+ * @param geometry
+ * @deprecated not working currently
+ */
+ public getIntersections ( geometry: TvAbstractRoadGeometry ): Vector3[] {
+
+ // throw new Error( "Method not implemented." );
+
+ if ( geometry instanceof TvLineGeometry ) {
+
+ const x1 = this.x;
+ const y1 = this.y;
+ const t1 = this.hdg;
+
+ const x2 = geometry.x;
+ const y2 = geometry.y;
+ const t2 = geometry.hdg;
+
+ const sX = ( x2 - x1 ) / Math.cos( t2 ) - Math.cos( t1 );
+ const sY = ( y2 - y1 ) / Math.sin( t2 ) - Math.sin( t1 );
+
+ // const intersection = Maths.lineLineIntersection( this.startV3, this.endV3, geometry.startV3, geometry.endV3 );
+
+ // if ( intersection &&
+ // Maths.isPointOnLine( this.startV3, this.endV3, intersection ) &&
+ // Maths.isPointOnLine( geometry.startV3, geometry.endV3, intersection )
+ // ) {
+
+ // return [ intersection ];
+
+ // }
+
+ } else if ( geometry instanceof TvArcGeometry ) {
+
+
+ return Maths.getLineArcIntersections( this.startV3, this.endV3, geometry.centre, geometry.radius );
+
+ } else {
+
+ // console.error( "Intersection with ", geometry.geometryType, "not implemented" );
+
+ // throw new Error( "Method not implemented." );
+
+ }
+
+ return [];
+ }
+
+}
diff --git a/src/app/modules/tv-map/models/geometries/tv-param-poly3-geometry.spec.ts b/src/app/modules/tv-map/models/geometries/tv-param-poly3-geometry.spec.ts
new file mode 100644
index 00000000..5fbb81b0
--- /dev/null
+++ b/src/app/modules/tv-map/models/geometries/tv-param-poly3-geometry.spec.ts
@@ -0,0 +1,60 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvRoad } from '../tv-road.model';
+import { TvPosTheta } from '../tv-pos-theta';
+
+describe( 'OdParamPoly3Geometry', () => {
+
+ let road: TvRoad;
+ let pose: TvPosTheta;
+
+ beforeEach( () => {
+
+ pose = new TvPosTheta();
+
+ road = new TvRoad( '', 100, 1, -1 );
+
+ road.addPlanView();
+
+ } );
+
+ it( 'should give correct coordinates with 0-degree hdg', () => {
+
+ const s = 0;
+ const x = 0;
+ const y = 0;
+ const hdg = 0;
+ const length = 100;
+
+ const aU = 0;
+ const bU = 0;
+ const cU = 40;
+ const dU = 50;
+
+ const aV = 0;
+ const bV = 0;
+ const cV = 10;
+ const dV = 0;
+
+ road.addGeometryParamPoly( s, x, y, hdg, length, aU, bU, cU, dU, aV, bV, cV, dV );
+
+ road.getGeometryCoords( 0, pose );
+ expect( pose.x ).toBe( 0 );
+ expect( pose.y ).toBe( 0 );
+ expect( pose.hdg ).toBe( 0 );
+
+ road.getGeometryCoords( 10, pose );
+ expect( pose.x ).toBe( 0.45 );
+ expect( pose.y ).toBe( 0.1 );
+ expect( pose.hdg ).toBe( 0.20749622643520266 );
+
+ road.getGeometryCoords( 20, pose );
+ expect( pose.x ).toBe( 2.0 );
+ expect( pose.y ).toBe( 0.4 );
+ expect( pose.hdg ).toBe( 0.17985349979247828 );
+
+ } );
+
+} );
diff --git a/src/app/modules/tv-map/models/geometries/tv-param-poly3-geometry.ts b/src/app/modules/tv-map/models/geometries/tv-param-poly3-geometry.ts
new file mode 100644
index 00000000..d7f085fa
--- /dev/null
+++ b/src/app/modules/tv-map/models/geometries/tv-param-poly3-geometry.ts
@@ -0,0 +1,176 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvAbstractRoadGeometry } from './tv-abstract-road-geometry';
+import { TvPosTheta } from '../tv-pos-theta';
+import { Curve, SplineCurve, Vector2, Vector3 } from 'three';
+import { TvGeometryType } from '../tv-common';
+
+export class TvParamPoly3Geometry extends TvAbstractRoadGeometry {
+
+ private sinTheta;
+ private cosTheta;
+ private curve: Curve;
+
+ constructor (
+ public s: number,
+ public x: number,
+ public y: number,
+ public hdg: number,
+ public length: number,
+ public aU: number, public bU: number, public cU: number, public dU: number,
+ public aV: number, public bV: number, public cV: number, public dV: number
+ ) {
+ super( s, x, y, hdg, length );
+
+ this._geometryType = TvGeometryType.PARAMPOLY3;
+
+ this.computeVars();
+
+ // this.getCurve();
+ }
+
+ get v0 () {
+ return new Vector2( this.aU, this.aV );
+ }
+
+ get v1 () {
+ return new Vector2( this.bU, this.bV );
+ }
+
+ get v2 () {
+ return new Vector2( this.cU, this.cV );
+ }
+
+ get v3 () {
+ return new Vector2( this.dU, this.dV );
+ }
+
+ computeVars () {
+
+ this.sinTheta = Math.sin( this.hdg );
+ this.cosTheta = Math.cos( this.hdg );
+
+ }
+
+ getCoords ( sCheck, posTheta: TvPosTheta ) {
+
+ // normalised p between 0 to 1
+ const du = sCheck - this.s;
+ const p = ( du / this.length );
+
+ const uLocal =
+ ( this.aU ) +
+ ( this.bU * p ) +
+ ( this.cU * p * p ) +
+ ( this.dU * p * p * p );
+
+ const vLocal =
+ (this.aV) +
+ (this.bV * p) +
+ (this.cV * p * p) +
+ (this.dV * p * p * p);
+
+ // apply rotation with respect to start
+ const xnew = uLocal * this.cosTheta - vLocal * this.sinTheta;
+ const ynew = uLocal * this.sinTheta + vLocal * this.cosTheta;
+
+ // Derivate to get heading change
+ const dCoeffsU = (new Vector3( this.bU, this.cU, this.dU )).multiply( new Vector3( 1, 2, 3 ) );
+ const dCoeffsV = (new Vector3( this.bV, this.cV, this.dV )).multiply( new Vector3( 1, 2, 3 ) );
+
+ const dx = this.polyeval( p, dCoeffsU );
+ const dy = this.polyeval( p, dCoeffsV );
+
+ const tangent = Math.atan2( dy, dx );
+
+ // apply tranformation with respect to start
+ posTheta.x = this.x + xnew;
+ posTheta.y = this.y + ynew;
+ posTheta.hdg = this.hdg + tangent;
+
+ // if ( this.curve != null ) {
+ //
+ // const du = sCheck - this.s;
+ //
+ // const p = du / this.length;
+ //
+ // const point = this.curve.getPointAt( p );
+ //
+ // posTheta.x = point.x;
+ // posTheta.y = point.y;
+ //
+ // const tangent = this.curve.getTangent( p );
+ //
+ // posTheta.hdg = tangent.angle();
+ //
+ // return this.geometryType;
+ // }
+ //
+ // return;
+
+ // if ( this.curve != null ) {
+
+ // const tangent = this.curve.getTangent( p );
+
+ // posTheta.hdg = tangent.angle();
+
+ // } else {
+
+ // posTheta.hdg = this.hdg;
+
+ // }
+
+ return this.geometryType;
+ }
+
+ getCurve (): Curve {
+
+ // const v0 = this.v0;
+ // const v1 = this.v1;
+ // const v2 = this.v2;
+ // const v3 = this.v3;
+ //
+ // return new CubicBezierCurve( v0, v1, v2, v3 );
+
+ const resolution = 1;
+ const s = this.sinTheta;
+ const c = this.cosTheta;
+ const pMax = this.length;
+ const pStep = pMax / Math.ceil( this.length / resolution );
+
+ const points: Vector2[] = [];
+
+ for ( let p = 0; p <= pMax + pStep; p += pStep ) {
+
+ const x =
+ (this.aU) +
+ (this.bU * p) +
+ (this.cU * p * p) +
+ (this.dU * p * p * p);
+
+ const y =
+ (this.aV) +
+ (this.bV * p) +
+ (this.cV * p * p) +
+ (this.dV * p * p * p);
+
+ const xnew = x * c - y * s;
+ const ynew = x * s + y * c;
+
+ points.push( new Vector2( this.x + xnew, this.y + ynew ) );
+
+ }
+
+ // for ( let sCoordinate = this.s; sCoordinate < this.s2; sCoordinate++ ) {
+
+ // this.getCoords( sCoordinate, posTheta );
+ // points.push( posTheta.toVector2() );
+
+ // }
+
+ return this.curve = new SplineCurve( points );
+
+ }
+}
diff --git a/src/app/modules/tv-map/models/geometries/tv-poly3-geometry.spec.ts b/src/app/modules/tv-map/models/geometries/tv-poly3-geometry.spec.ts
new file mode 100644
index 00000000..16ec5e58
--- /dev/null
+++ b/src/app/modules/tv-map/models/geometries/tv-poly3-geometry.spec.ts
@@ -0,0 +1,60 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvRoad } from '../tv-road.model';
+import { TvPosTheta } from '../tv-pos-theta';
+
+describe( 'OdPoly3Geometry', () => {
+
+ let road: TvRoad;
+ let pose: TvPosTheta;
+
+ beforeEach( () => {
+
+ pose = new TvPosTheta();
+
+ road = new TvRoad( '', 100, 1, -1 );
+
+ road.addPlanView();
+
+ } );
+
+ it( 'should give correct coordinates with 0-degree hdg', () => {
+
+ const s = 0;
+ const x = 0;
+ const y = 0;
+ const hdg = 0;
+ const length = 10;
+
+ const a = 0;
+ const b = 0;
+ const c = 1;
+ const d = 0;
+
+ road.addGeometryPoly( s, x, y, hdg, length, a, b, c, d );
+
+ road.getGeometryCoords( 0, pose );
+ expect( pose.x ).toBe( 0 );
+ expect( pose.y ).toBe( 0 );
+ expect( pose.hdg ).toBe( 0 );
+
+ road.getGeometryCoords( 1, pose );
+ expect( pose.x ).toBe( 1 );
+ expect( pose.y ).toBe( 1 );
+ expect( pose.hdg ).toBe( 2 );
+
+ road.getGeometryCoords( 2, pose );
+ expect( pose.x ).toBe( 2 );
+ expect( pose.y ).toBe( 4 );
+ expect( pose.hdg ).toBe( 4 );
+
+ road.getGeometryCoords( 3, pose );
+ expect( pose.x ).toBe( 3 );
+ expect( pose.y ).toBe( 9 );
+ expect( pose.hdg ).toBe( 6 );
+
+ } );
+
+} );
diff --git a/src/app/modules/tv-map/models/geometries/tv-poly3-geometry.ts b/src/app/modules/tv-map/models/geometries/tv-poly3-geometry.ts
new file mode 100644
index 00000000..0d3711b1
--- /dev/null
+++ b/src/app/modules/tv-map/models/geometries/tv-poly3-geometry.ts
@@ -0,0 +1,94 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvGeometryType } from '../tv-common';
+import { Curve, SplineCurve, Vector2, Vector3 } from 'three';
+import { TvPosTheta } from '../tv-pos-theta';
+import { TvAbstractRoadGeometry } from './tv-abstract-road-geometry';
+
+export class TvPoly3Geometry extends TvAbstractRoadGeometry {
+
+ public attr_a: number;
+ public attr_b: number;
+ public attr_c: number;
+ public attr_d: number;
+
+ private staringPoint;
+ private sinTheta;
+ private cosTheta;
+ private curve: Curve;
+
+ constructor ( s: number, x: number, y: number, hdg: number, length: number, a: number, b: number, c: number, d: number ) {
+
+ super( s, x, y, hdg, length );
+
+ this._geometryType = TvGeometryType.POLY3;
+
+ this.attr_a = a;
+ this.attr_b = b;
+ this.attr_c = c;
+ this.attr_d = d;
+
+ this.computeVars();
+ }
+
+ getCoords ( sCheck: any, posTheta: TvPosTheta ) {
+
+ const vLocal = this.getBezierValue( sCheck );
+
+ const x = sCheck;
+ const y = vLocal;
+
+ const xnew = x * this.cosTheta - y * this.sinTheta;
+ const ynew = x * this.sinTheta + y * this.cosTheta;
+
+ // Derivate to get heading change
+ const dCoeffs = (new Vector3( this.attr_b, this.attr_c, this.attr_d )).multiply( new Vector3( 1, 2, 3 ) );
+ const tangent = this.polyeval( sCheck, dCoeffs );
+
+ posTheta.x = this.x + xnew;
+ posTheta.y = this.y + ynew;
+
+ posTheta.hdg = this.hdg + tangent;
+
+ return this.geometryType;
+ }
+
+ getCurve (): Curve {
+
+ const points: Vector2[] = [];
+ const posTheta = new TvPosTheta();
+
+ for ( let sCoordinate = this.s; sCoordinate < this.s2; sCoordinate++ ) {
+
+ this.getCoords( sCoordinate, posTheta );
+ points.push( posTheta.toVector2() );
+
+ }
+
+ return this.curve = new SplineCurve( points );
+
+ }
+
+ computeVars () {
+
+
+ this.sinTheta = Math.sin( this.hdg );
+ this.cosTheta = Math.cos( this.hdg );
+
+
+ // throw new Error("Method not implemented.");
+ }
+
+ getBezierValue ( sCheck ): number {
+
+ const du = sCheck - this.s;
+
+ return (this.attr_a) +
+ (this.attr_b * du) +
+ (this.attr_c * du * du) +
+ (this.attr_d * du * du * du);
+ }
+
+}
diff --git a/src/app/modules/tv-map/models/geometries/tv-spiral-geometry.ts b/src/app/modules/tv-map/models/geometries/tv-spiral-geometry.ts
new file mode 100644
index 00000000..ecc92d7c
--- /dev/null
+++ b/src/app/modules/tv-map/models/geometries/tv-spiral-geometry.ts
@@ -0,0 +1,273 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Maths } from '../../../../utils/maths';
+import { TvGeometryType } from '../tv-common';
+import { SpiralUtils } from 'app/utils/spiral-utils';
+import { TvPosTheta } from '../tv-pos-theta';
+import { TvAbstractRoadGeometry } from './tv-abstract-road-geometry';
+
+export class TvSpiralGeometry extends TvAbstractRoadGeometry {
+
+ public attr_curvStart;
+ public attr_curvEnd;
+
+ private sqrtPiO2 = Math.sqrt( Maths.M_PI_2 );
+ private mA;
+ private mCurvature;
+ private mDenormalizeFactor;
+ private endX;
+ private endY;
+ private normalDirection;
+
+ private differenceAngle;
+ private mRotCos;
+ private mRotSin;
+
+ constructor ( s: number, x: number, y: number, hdg: number, length: number, curvStart: number, curvEnd: number ) {
+
+ super( s, x, y, hdg, length );
+
+ this.attr_curvStart = curvStart;
+ this.attr_curvEnd = curvEnd;
+
+ this._geometryType = TvGeometryType.SPIRAL;
+
+ }
+
+ computeVars () { }
+
+ /**
+ * Gets the coordinates at the sample S offset
+ * @param sCheck
+ * @param odPosTheta
+ */
+ getCoords ( sCheck, odPosTheta: TvPosTheta ) {
+
+ const dist = Maths.clamp( sCheck - this.s, 0.0, this.length );
+
+ const curveEnd = this.attr_curvEnd;
+ const curveStart = this.attr_curvStart;
+ const curveDot = ( curveEnd - curveStart ) / this.length;
+ const s_o = curveStart / curveDot;
+ const s = s_o + dist;
+
+ const xyt = SpiralUtils.odrSpiral( s, curveDot, 0, 0, 0 );
+
+ const xyt_o = SpiralUtils.odrSpiral( s_o, curveDot, 0, 0, 0 );
+
+ const x = xyt.x - xyt_o.x;
+ const y = xyt.y - xyt_o.y;
+ const t = xyt.t - xyt_o.t;
+
+ const angle = this.hdg - xyt_o.t;
+
+ // Translate the curve to the required position & rotate it
+ const retX = this.x + x * Math.cos( angle ) - y * Math.sin( angle );
+ const retY = this.y + y * Math.cos( angle ) + x * Math.sin( angle );
+
+ odPosTheta.x = retX;
+ odPosTheta.y = retY;
+ odPosTheta.hdg = this.hdg + t;
+
+ return this.geometryType;
+ }
+
+ getCurve (): import( "three" ).Curve {
+ throw new Error( "Method not implemented." );
+ }
+
+}
+
+
+// old version for reference
+// export class OdSpiralGeometry extends OdAbstractRoadGeometry {
+
+// public attr_curvStart;
+// public attr_curvEnd;
+
+// private sqrtPiO2 = Math.sqrt( Maths.M_PI_2 );
+// private mA;
+// private mCurvature;
+// private mDenormalizeFactor;
+// private endX;
+// private endY;
+// private normalDirection;
+
+// private differenceAngle;
+// private mRotCos;
+// private mRotSin;
+
+// constructor ( s: number, x: number, y: number, hdg: number, length: number, curvStart: number, curvEnd: number ) {
+
+// super( s, x, y, hdg, length );
+
+// this.attr_curvStart = curvStart;
+// this.attr_curvEnd = curvEnd;
+
+// this._geometryType = OdGeometryType.SPIRAL;
+
+// this.computeVars();
+// }
+
+// computeVars () {
+
+// this.mA = 0;
+
+// // if the curvatureEnd is the non-zero curvature, then the motion is in normal direction along the spiral
+// if ( (Math.abs( this.attr_curvEnd ) > Maths.Epsilon) && (Math.abs( this.attr_curvStart ) <= Maths.Epsilon) ) {
+
+// this.normalDirection = true;
+
+// this.mCurvature = this.attr_curvEnd;
+
+// // Calculate the normalization term :
+// // a = 1.0/sqrt(2*End_Radius*Total_Curve_Length)
+// this.mA = 1.0 / Math.sqrt( 2 * (1.0 / Math.abs( this.mCurvature )) * this.length );
+
+// this.mDenormalizeFactor = 1.0 / this.mA;
+
+// // Calculate the sine and cosine of the heading angle used to
+// // rotate the spiral according to the heading
+// this.mRotCos = Math.cos( this.attr_hdg );
+// this.mRotSin = Math.sin( this.attr_hdg );
+
+// } else {
+
+// this.normalDirection = false;
+
+// this.mCurvature = this.attr_curvStart;
+
+// this.mA = 1.0 / Math.sqrt( 2 * 1.0 / Math.abs( this.mCurvature ) * this.length );
+
+// // Because we move in the inverse direction, we need to rotate the curve according to the heading
+// // around the last point of the normalized spiral
+// // Calculate the total length, normalize it and divide by sqrtPiO2, then, calculate the position of the final point.
+
+// const L = (( this._s2 - this.s ) * this.mA) / this.sqrtPiO2;
+
+// const res = SpiralUtils.fresnel( L, this.endY, this.endX );
+
+// this.endX = res.x;
+// this.endY = res.y;
+
+// // Invert the curve if the curvature is negative
+// if ( this.mCurvature < 0 ) {
+// this.endY = -this.endY;
+// }
+
+// // Denormalisation factor
+// this.mDenormalizeFactor = 1.0 / this.mA;
+
+// // Find the x,y coords of the final point fo the curve in local curve coordinates
+// this.endX *= this.mDenormalizeFactor * this.sqrtPiO2;
+// this.endY *= this.mDenormalizeFactor * this.sqrtPiO2;
+
+// // Calculate the tangent angle
+// this.differenceAngle = L * L * ( this.sqrtPiO2 * this.sqrtPiO2 );
+
+// let diffAngle;
+
+// // Calculate the tangent and heading angle difference that will be used to rotate the spiral
+// if ( this.mCurvature < 0 ) {
+
+// diffAngle = this.hdg - this.differenceAngle - Math.PI;
+
+// } else {
+
+// diffAngle = this.hdg + this.differenceAngle - Math.PI;
+
+// }
+
+// // Calculate the sine and cosine of the different angle
+// this.mRotCos = Math.cos( diffAngle );
+// this.mRotSin = Math.sin( diffAngle );
+
+// }
+
+
+// }
+
+// /**
+// * Gets the coordinates at the sample S offset
+// * @param sCheck
+// * @param odPosTheta
+// */
+// getCoords ( sCheck, odPosTheta: OdPosTheta ) {
+
+// let l = 0.0;
+// let tmpX = 0.0, tmpY = 0.0;
+
+// let retX, retY, retHdg;
+
+// // Depending on the moving direction, calculate the length of the curve from its beginning to the current point and normalize
+// // it by multiplying with the "a" normalization term
+// // Cephes lib for solving Fresnel Integrals, uses cos/sin (PI/2 * X^2) format in its function.
+// // So, in order to use the function, transform the argument (which is just L) by dividing it
+// // by the sqrt(PI/2) factor and multiply the results by it.
+// if ( this.normalDirection ) {
+
+// l = (( sCheck - this.s ) * this.mA) / this.sqrtPiO2;
+
+// } else {
+
+// l = (( this._s2 - sCheck ) * this.mA) / this.sqrtPiO2;
+
+// }
+
+// // Solve the Fresnel Integrals
+// const res = SpiralUtils.fresnel( l, tmpY, tmpX );
+// tmpY = res.y;
+// tmpX = res.x;
+
+// // If the curvature is negative, invert the curve on the Y axis
+// if ( this.mCurvature < 0 ) {
+// tmpY = -tmpY;
+// }
+
+// // Denormalize the results and multiply by the sqrt(PI/2) term
+// tmpX *= this.mDenormalizeFactor * this.sqrtPiO2;
+// tmpY *= this.mDenormalizeFactor * this.sqrtPiO2;
+
+// // Calculate the heading at the found position.
+// // Kill the sqrt(PI/2) term that was added to the L
+// l = ( sCheck - this.s ) * this.mA;
+
+// let tangentAngle = l * l;
+
+// if ( this.mCurvature < 0 ) {
+// tangentAngle = -tangentAngle;
+// }
+
+// retHdg = this.hdg + tangentAngle;
+
+
+// if ( !this.normalDirection ) {
+
+// // If we move in the inverse direction, translate the spiral
+// // in order to rotate around its final point
+// tmpX -= this.endX;
+// tmpY -= this.endY;
+
+// // also invert the spiral in the y-axis
+// tmpY = -tmpY;
+// }
+
+// // Translate the curve to the required position & rotate it
+// retX = this.x + tmpX * this.mRotCos - tmpY * this.mRotSin;
+// retY = this.y + tmpY * this.mRotCos + tmpX * this.mRotSin;
+
+
+// odPosTheta.x = retX;
+// odPosTheta.y = retY;
+// odPosTheta.hdg = retHdg;
+
+// return this.geometryType;
+// }
+
+// getCurve (): import( "three" ).Curve {
+// throw new Error( "Method not implemented." );
+// }
+
+// }
diff --git a/src/app/modules/tv-map/models/geometries/tv-spline-geometry.ts b/src/app/modules/tv-map/models/geometries/tv-spline-geometry.ts
new file mode 100644
index 00000000..e7a2c679
--- /dev/null
+++ b/src/app/modules/tv-map/models/geometries/tv-spline-geometry.ts
@@ -0,0 +1,37 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvAbstractRoadGeometry } from './tv-abstract-road-geometry';
+import { TvGeometryType } from '../tv-common';
+import { TvPosTheta } from '../tv-pos-theta';
+import { Curve, Vector2 } from 'three';
+
+export class TvSplineGeometry extends TvAbstractRoadGeometry {
+
+ constructor ( s: number, x: number, y: number, hdg: number, length: number ) {
+
+ super( s, x, y, hdg, length );
+
+ this._geometryType = TvGeometryType.SPLINE;
+ }
+
+ getCoords ( sCheck: any, posTheta: TvPosTheta ) {
+
+ throw new Error( 'Method not implemented.' );
+
+ }
+
+ computeVars () {
+
+ throw new Error( "Method not implemented." );
+
+ }
+
+ getCurve (): Curve {
+
+ throw new Error( "Method not implemented." );
+
+ }
+
+}
diff --git a/src/app/modules/tv-map/models/lane.data.ts b/src/app/modules/tv-map/models/lane.data.ts
new file mode 100644
index 00000000..fd43b668
--- /dev/null
+++ b/src/app/modules/tv-map/models/lane.data.ts
@@ -0,0 +1,30 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { GameObject } from '../../../core/game-object';
+
+class LaneData {
+
+ public parent: GameObject;
+ public cumulativeWidth: number;
+
+ public elevation: number;
+ public elevationAccum: number;
+
+ public cosAngleLeft: number;
+ public cosAngleRight: number;
+ public sinAngleLeft: number;
+ public sinAngleRight: number;
+ public laneOffset: number;
+
+ public Clear (): void {
+
+ this.cumulativeWidth = 0;
+ this.elevationAccum = 0;
+ this.cosAngleLeft = 0;
+ this.cosAngleRight = 0;
+ this.sinAngleLeft = 0;
+ this.sinAngleRight = 0;
+ }
+}
diff --git a/src/app/modules/tv-map/models/mesh-geometry.data.ts b/src/app/modules/tv-map/models/mesh-geometry.data.ts
new file mode 100644
index 00000000..8c9e75bf
--- /dev/null
+++ b/src/app/modules/tv-map/models/mesh-geometry.data.ts
@@ -0,0 +1,14 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+export class MeshGeometryData {
+ public vertices: any[] = [];
+ public triangles = [];
+ public currentIndex = 0;
+ public indices: number[] = [];
+ public colors: number[] = [];
+ public normals: number[] = [];
+ public texCoords: number[] = [];
+ public material: any;
+}
diff --git a/src/app/modules/tv-map/models/prop-curve.ts b/src/app/modules/tv-map/models/prop-curve.ts
new file mode 100644
index 00000000..fddab151
--- /dev/null
+++ b/src/app/modules/tv-map/models/prop-curve.ts
@@ -0,0 +1,47 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { PropModel } from 'app/core/models/prop-model.model';
+import { AbstractSpline } from 'app/core/shapes/abstract-spline';
+import { CatmullRomSpline } from 'app/core/shapes/catmull-rom-spline';
+import { AnyControlPoint } from 'app/modules/three-js/objects/control-point';
+import { Object3D } from 'three';
+
+export class PropCurve {
+
+ public reverse: boolean = false;
+
+ public spacing: number = 5.0;
+
+ public rotation: number = 0.0;
+
+ public positionVariance: number = 0.0;
+
+ public props: Object3D[] = [];
+
+ constructor ( public propGuid: string, public spline?: CatmullRomSpline, public headings: number[] = [] ) {
+
+ if ( !this.spline ) {
+
+ this.spline = new CatmullRomSpline( false, 'catmullrom', 0.001 );
+
+ this.spline.init();
+
+ }
+
+ }
+
+ update () {
+
+ this.spline.update();
+
+ }
+
+ addControlPoint ( cp: AnyControlPoint ) {
+
+ ( this.spline as CatmullRomSpline ).add( cp );
+
+ this.update();
+ }
+}
diff --git a/src/app/modules/tv-map/models/prop-polygons.ts b/src/app/modules/tv-map/models/prop-polygons.ts
new file mode 100644
index 00000000..9e447162
--- /dev/null
+++ b/src/app/modules/tv-map/models/prop-polygons.ts
@@ -0,0 +1,120 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { CatmullRomSpline } from 'app/core/shapes/catmull-rom-spline';
+import { AnyControlPoint } from 'app/modules/three-js/objects/control-point';
+import { Object3D, Mesh, Shape, ShapeBufferGeometry, MeshBasicMaterial, Vector2, ShapeUtils, BufferGeometryUtils, BufferAttribute, Vector3 } from 'three';
+import { GameObject } from 'app/core/game-object';
+import * as THREE from 'three';
+import { PropService } from 'app/services/prop-service';
+
+export class PropPolygon {
+
+ public static index = 0;
+
+ public static tag: string = 'prop-polygon';
+
+ public id: number;
+
+ public props: Object3D[] = [];
+
+ public mesh: Mesh;
+
+ constructor ( public propGuid: string, public spline?: CatmullRomSpline, public density = 0.5 ) {
+
+ if ( !this.spline ) {
+
+ this.spline = new CatmullRomSpline( true, 'catmullrom', 0.001 );
+
+ }
+
+ this.id = PropPolygon.index++;
+
+ // make a blank shape to avoid any errors
+ this.mesh = this.makeMesh( new Shape() );
+ }
+
+ makeMesh ( shape: Shape ): Mesh {
+
+ const geometry = new ShapeBufferGeometry( shape );
+
+ const groundMaterial = new MeshBasicMaterial( {} );
+
+ const mesh = new GameObject( PropPolygon.tag, geometry, groundMaterial );
+
+ mesh.position.set( 0, 0, -0.1 );
+
+ mesh.Tag = PropPolygon.tag;
+
+ mesh.userData.polygon = this;
+
+ return mesh;
+ }
+
+ update () {
+
+ this.spline.update();
+
+ if ( this.spline.controlPoints.length < 3 ) return;
+
+ const points: Vector2[] = this.spline.curve.getPoints( 50 ).map(
+ p => new Vector2( p.x, p.y )
+ );
+
+ const shape = new Shape();
+
+ const first = points.shift();
+
+ shape.moveTo( first.x, first.y );
+
+ shape.splineThru( points );
+
+ this.mesh.geometry.dispose();
+
+ const geometry = this.mesh.geometry = new ShapeBufferGeometry( shape );
+
+ geometry.computeBoundingBox();
+
+ PropService.updateCurvePolygonProps( this );
+ }
+
+ addControlPoint ( cp: AnyControlPoint ) {
+
+ ( this.spline as CatmullRomSpline ).add( cp );
+
+ this.update();
+ }
+
+ delete () {
+
+ this.hideControlPoints();
+
+ this.hideCurve();
+
+ }
+
+ hideControlPoints () {
+
+ this.spline.hidecontrolPoints();
+
+ }
+
+ hideCurve () {
+
+ this.spline.hide();
+
+ }
+
+ showCurve () {
+
+ this.spline.show();
+
+ }
+
+ showControlPoints () {
+
+ this.spline.showcontrolPoints();
+
+ }
+}
diff --git a/src/app/modules/tv-map/models/third-order-polynom.ts b/src/app/modules/tv-map/models/third-order-polynom.ts
new file mode 100644
index 00000000..da7606d2
--- /dev/null
+++ b/src/app/modules/tv-map/models/third-order-polynom.ts
@@ -0,0 +1,119 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Math } from 'three';
+
+export class ThirdOrderPolynom {
+
+ public readonly uuid: string;
+
+ protected _s: number;
+ protected _a: number;
+ protected _b: number;
+ protected _c: number;
+ protected _d: number;
+
+
+ /**
+ * Constructor that initializes the polynom with base properties
+ *
+ * @param {number} s
+ * @param {number} a
+ * @param {number} b
+ * @param {number} c
+ * @param {number} d
+ */
+ constructor ( s: number, a: number, b: number, c: number, d: number ) {
+
+ this._s = s;
+ this._a = a;
+ this._b = b;
+ this._c = c;
+ this._d = d;
+
+ this.uuid = Math.generateUUID();
+ }
+
+ get s (): number {
+ return this._s;
+ }
+
+ set s ( value: number ) {
+ this._s = value;
+ }
+
+ get d (): number {
+ return this._d;
+ }
+
+ set d ( value: number ) {
+ this._d = value;
+ }
+
+ get c (): number {
+ return this._c;
+ }
+
+ set c ( value: number ) {
+ this._c = value;
+ }
+
+ get b (): number {
+ return this._b;
+ }
+
+ set b ( value: number ) {
+ this._b = value;
+ }
+
+ get a (): number {
+ return this._a;
+ }
+
+ set a ( value: number ) {
+ this._a = value;
+ }
+
+ setValues ( s, a, b, c, d ) {
+
+ this.s = s;
+ this.a = a;
+ this.b = b;
+ this.c = c;
+ this.d = d;
+
+ }
+
+
+ /**
+ * Method to check if sample S is inside the record interval
+ *
+ * @param sOther
+ * @returns {boolean}
+ */
+ checkInterval ( sOther ): boolean {
+
+ return sOther >= this._s;
+
+ }
+
+ /**
+ * Returns the value at sample S
+ *
+ * @param sCheck
+ * @returns {number}
+ */
+ getValue ( sCheck: number ): number {
+
+ if ( isNaN( sCheck ) ) console.error( "s in not a number" );
+
+ const ds = sCheck - this._s;
+
+ return ( this._a ) +
+ ( this._b * ds ) +
+ ( this._c * ds * ds ) +
+ ( this._d * ds * ds * ds );
+ }
+
+}
diff --git a/src/app/modules/tv-map/models/tv-common.ts b/src/app/modules/tv-map/models/tv-common.ts
new file mode 100644
index 00000000..28b8e567
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-common.ts
@@ -0,0 +1,241 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+export class TvUserData {
+ constructor ( public attr_code: string, public attr_value ) {
+ }
+}
+
+export enum TvElementType {
+ road = 'road',
+ junction = 'junction'
+}
+
+export enum TvLaneType {
+ none = 'none',
+ driving = 'driving',
+ stop = 'stop',
+ shoulder = 'shoulder',
+ biking = 'biking',
+ sidewalk = 'sidewalk',
+ border = 'border',
+ restricted = 'restricted',
+ parking = 'parking',
+ bidirectional = 'bidirectional',
+ median = 'median',
+ special1 = 'special1',
+ special2 = 'special2',
+ special3 = 'special3',
+ roadWorks = 'roadWorks',
+ tram = 'tram',
+ rail = 'rail',
+ entry = 'entry',
+ exit = 'exit',
+ offRamp = 'offRamp',
+ onRamp = 'onRamp',
+}
+
+export enum TvRoadMarkTypes {
+ NONE = 'none',
+ SOLID = 'solid',
+ BROKEN = 'broken',
+ SOLID_SOLID = 'solid solid',
+ SOLID_BROKEN = 'solid broken',
+ BROKEN_SOLID = 'broken solid',
+ BROKEN_BROKEN = 'broken broken',
+ BOTTS_DOTS = 'botts dots',
+ GRASS = 'grass',
+ CURB = 'curb',
+}
+
+export enum TvRoadMarkWeights {
+ STANDARD = 'standard',
+ BOLD = 'bold'
+}
+
+export enum TvColors {
+ STANDARD = 'standard',
+ BLUE = 'blue',
+ GREEN = 'green',
+ RED = 'red',
+ WHITE = 'white',
+ YELLOW = 'yellow',
+}
+
+export enum TvParkingSpaceAccess {
+ ALL = 'all',
+ CAR = 'car',
+ WOMEN = 'women',
+ HANDICAPPED = 'handicapped',
+ BUS = 'bus',
+ TRUCK = 'truck',
+ ELECTRIC = 'electric',
+ RESIDENTS = 'residents'
+}
+
+export enum TvParkingSpaceMarkingSides {
+ FRONT = 'front',
+ REAR = 'rear',
+ LEFT = 'left',
+ RIGHT = 'right',
+}
+
+export enum TvBridgeTypes {
+ CONCRETE = 'concrete',
+ STEEL = 'steel',
+ BRICK = 'brick',
+ WOOD = 'wood',
+}
+
+export enum TvTunnelTypes {
+ STANDARD = 'standard',
+ UNDERPASS = 'underpass'
+}
+
+export enum TvOrientation {
+ PLUS = '+',
+ MINUS = '-',
+ NONE = 'none'
+}
+
+export enum TvUnit {
+ METER = 'm',
+ KM = 'km',
+ FEET = 'ft',
+ MILE = 'mile',
+ METER_PER_SECOND = 'm/s',
+ MILES_PER_HOUR = 'mph',
+ KM_PER_HOUR = 'km/h',
+ KG = 'kg',
+ T = 't',
+ PERCENT = '%'
+}
+
+export enum TvDirection {
+ SAME = 'same',
+ OPPOSITE = 'opposite'
+}
+
+export enum TravelDirection {
+ forward = 'forward',
+ backward = 'backward',
+ bidirectional = 'bidirectional',
+ undirected = 'undirected'
+}
+
+
+export enum TvRoadType {
+ UNKNOWN = 'unknown',
+ RURAL = 'rural',
+ MOTORWAY = 'motorway',
+ TOWN = 'town',
+ LOW_SPEED = 'lowSpeed',
+ PEDESTRIAN = 'pedestrian',
+ BICYCLE = 'bicycle',
+}
+
+export enum TvSide {
+ LEFT = 'left',
+ RIGHT = 'right'
+}
+
+export enum TvContactPoint {
+ START = 'start',
+ END = 'end'
+}
+
+export enum TvStationTypes {
+ SMALL = 'small',
+ MEDIUM = 'medium',
+ LARGE = 'large'
+}
+
+export enum TvJunctionGroupTypes {
+ ROUNDABOUT = 'roundabout',
+ UNKNOWN = 'unknown'
+}
+
+export enum TvDynamicTypes {
+ YES = 'yes',
+ NO = 'no'
+}
+
+export enum TvActionMode {
+ SELECT,
+ DROP,
+ EDIT
+}
+
+export enum TvEditingMode {
+ ANY,
+ ROAD,
+ LANE,
+ ROADMARK,
+ SIGNAL,
+ OBJECT,
+}
+
+export enum TvGeometryType {
+ LINE = 1,
+ ARC = 2,
+ SPIRAL = 3,
+ POLY3 = 4,
+ PARAMPOLY3 = 5,
+ SPLINE = 6,
+}
+
+export enum TvLaneSide {
+ LEFT,
+ CENTER,
+ RIGHT
+}
+
+export enum ObjectTypes {
+ ROAD = 'ROAD',
+ LANE = 'LANE',
+ LANE_MARKING = 'LANE_MARKING',
+ VEHICLE = 'vehicle'
+}
+
+export class EnumHelper {
+
+ static stringToOdUnits ( value ): TvUnit {
+
+ switch ( value ) {
+
+ case 'm':
+ return TvUnit.METER; break;
+
+ case 'km':
+ return TvUnit.KM; break;
+
+ case 'ft':
+ return TvUnit.FEET; break;
+
+ case 'mile':
+ return TvUnit.MILE; break;
+
+ case 'm/s':
+ return TvUnit.METER_PER_SECOND; break;
+
+ case 'mph':
+ return TvUnit.MILES_PER_HOUR; break;
+
+ case 'km/h':
+ return TvUnit.KM_PER_HOUR; break;
+
+ case 'kg':
+ return TvUnit.KG; break;
+
+ case 't':
+ return TvUnit.T; break;
+
+ case '%':
+ return TvUnit.PERCENT; break;
+
+ default:
+ console.error( "unknown unit" ); break;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/app/modules/tv-map/models/tv-controller.ts b/src/app/modules/tv-map/models/tv-controller.ts
new file mode 100644
index 00000000..c93f0f30
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-controller.ts
@@ -0,0 +1,81 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+export class TvController {
+
+ private _controls: TvControllerControl[] = [];
+ private _id: number;
+ private _name: string;
+ private _sequence: number;
+
+ constructor ( id: number, name: string, sequence?: number ) {
+ this._id = id;
+ this._name = name;
+ this._sequence = sequence;
+ }
+
+ get controls (): TvControllerControl[] {
+ return this._controls;
+ }
+
+ set controls ( value: TvControllerControl[] ) {
+ this._controls = value;
+ }
+
+ get id (): number {
+ return this._id;
+ }
+
+ set id ( value: number ) {
+ this._id = value;
+ }
+
+ get name (): string {
+ return this._name;
+ }
+
+ set name ( value: string ) {
+ this._name = value;
+ }
+
+ get sequence (): number {
+ return this._sequence;
+ }
+
+ set sequence ( value: number ) {
+ this._sequence = value;
+ }
+
+ addControl ( odControllerControl: TvControllerControl ) {
+ this.controls.push( odControllerControl );
+ }
+}
+
+export class TvControllerControl {
+
+ private _signalId: number;
+ private _type: string;
+
+ constructor ( signalId: number, type: string ) {
+ this._signalId = signalId;
+ this._type = type;
+ }
+
+ get type (): string {
+ return this._type;
+ }
+
+ set type ( value: string ) {
+ this._type = value;
+ }
+
+ get signalId (): number {
+ return this._signalId;
+ }
+
+ set signalId ( value: number ) {
+ this._signalId = value;
+ }
+
+}
diff --git a/src/app/modules/tv-map/models/tv-elevation-profile.ts b/src/app/modules/tv-map/models/tv-elevation-profile.ts
new file mode 100644
index 00000000..21f08ba3
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-elevation-profile.ts
@@ -0,0 +1,26 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvElevation } from './tv-elevation';
+
+export class TvElevationProfile {
+
+ public elevation: TvElevation[] = [];
+
+ constructor () {
+
+ }
+
+ getElevations (): TvElevation[] {
+ return this.elevation;
+ }
+
+ getElevation ( i: number ): any {
+ return this.elevation[ i ];
+ }
+
+ getElevationCount (): number {
+ return this.elevation.length;
+ }
+}
diff --git a/src/app/modules/tv-map/models/tv-elevation.ts b/src/app/modules/tv-map/models/tv-elevation.ts
new file mode 100644
index 00000000..725428dc
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-elevation.ts
@@ -0,0 +1,8 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { ThirdOrderPolynom } from './third-order-polynom';
+
+export class TvElevation extends ThirdOrderPolynom {
+}
\ No newline at end of file
diff --git a/src/app/modules/tv-map/models/tv-junction-connection.ts b/src/app/modules/tv-map/models/tv-junction-connection.ts
new file mode 100644
index 00000000..7d955e6f
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-junction-connection.ts
@@ -0,0 +1,131 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvContactPoint } from './tv-common';
+import { TvJunctionLaneLink } from './tv-junction-lane-link';
+import { TvRoad } from './tv-road.model';
+import { Math } from 'three';
+
+export class TvJunctionConnection {
+
+ public readonly uuid: string;
+
+ public laneLink: TvJunctionLaneLink[] = [];
+
+ private lastAddedJunctionLaneLinkIndex: number;
+
+ constructor (
+ public id: number,
+ public incomingRoad: number,
+ public connectingRoad: number,
+ public contactPoint: TvContactPoint,
+ public outgoingRoad?: number
+ ) {
+ this.uuid = Math.generateUUID();
+ }
+
+ /**
+ * Add a lane link record
+ *
+ * @param {number} from
+ * @param {number} to
+ * @returns {number}
+ */
+ public addJunctionLaneLink ( from: number, to: number ) {
+
+ const instance = new TvJunctionLaneLink( from, to );
+
+ this.addLaneLink( instance );
+
+ this.lastAddedJunctionLaneLinkIndex = this.laneLink.length - 1;
+
+ return this.lastAddedJunctionLaneLinkIndex;
+
+ }
+
+ addNewLink ( from: number, to: number ) {
+
+ const link = new TvJunctionLaneLink( from, to );
+
+ this.addLaneLink( link );
+
+ return link;
+ }
+
+ getJunctionLaneLinkCount (): number {
+
+ return this.laneLink.length;
+
+ }
+
+ getJunctionLaneLink ( index: number ): TvJunctionLaneLink {
+
+ return this.laneLink[ index ];
+
+ }
+
+ public cloneJunctionLaneLink ( index ) {
+
+ // TODO
+
+ }
+
+ public deleteJunctionLaneLink ( index ) {
+
+ this.laneLink.splice( index, 1 );
+
+ }
+
+ public getLastAddedJunctionLaneLink (): TvJunctionLaneLink {
+
+ return this.laneLink[ this.lastAddedJunctionLaneLinkIndex ];
+
+ }
+
+ public addLaneLink ( laneLink: TvJunctionLaneLink ) {
+
+ this.laneLink.push( laneLink );
+
+ }
+
+ getConnectingRoad (): TvRoad {
+ return undefined;
+ }
+
+ getToLaneId ( laneId: number ): number {
+
+ for ( const link of this.laneLink ) {
+
+ if ( link.from == laneId ) {
+
+ return link.to;
+
+ }
+
+ }
+
+ return null;
+ }
+
+ getFromLaneId ( laneId: number ): number {
+
+ for ( const link of this.laneLink ) {
+
+ if ( link.to == laneId ) {
+
+ return link.from;
+
+ }
+
+ }
+
+ return null;
+ }
+
+ removeLinkAtIndex ( index: number ) {
+
+ this.laneLink.splice( index, 1 );
+
+ }
+}
diff --git a/src/app/modules/tv-map/models/tv-junction-controller.ts b/src/app/modules/tv-map/models/tv-junction-controller.ts
new file mode 100644
index 00000000..b944bcd2
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-junction-controller.ts
@@ -0,0 +1,41 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+export class TvJunctionController {
+
+ public attr_id: number;
+ public attr_type: string;
+ public attr_sequence: number;
+
+ constructor ( id: number, type: string, sequence?: number ) {
+ this.attr_id = id;
+ this.attr_type = type;
+ this.attr_sequence = sequence;
+ }
+
+ get id (): number {
+ return this.attr_id;
+ }
+
+ set id ( value ) {
+ this.attr_id = value;
+ }
+
+ get type (): string {
+ return this.attr_type;
+ }
+
+ set type ( value ) {
+ this.attr_type = value;
+ }
+
+ get sequence (): number {
+ return this.attr_sequence;
+ }
+
+ set sequence ( value ) {
+ this.attr_sequence = value;
+ }
+
+}
diff --git a/src/app/modules/tv-map/models/tv-junction-lane-link.ts b/src/app/modules/tv-map/models/tv-junction-lane-link.ts
new file mode 100644
index 00000000..aea7079c
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-junction-lane-link.ts
@@ -0,0 +1,87 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Color, Line, LineBasicMaterial, Mesh, Object3D } from 'three';
+import { TvRoad } from './tv-road.model';
+import { TvJunctionConnection } from './tv-junction-connection';
+
+export class TvJunctionLaneLink {
+
+ public lanePath: LanePathObject;
+
+ /**
+ *
+ * @param from ID of the incoming lane
+ * @param to ID of the connecting lane
+ */
+ constructor ( public from: number, public to: number ) {
+
+ }
+
+ show () {
+
+ if ( this.lanePath ) this.lanePath.hide();
+
+ }
+
+ hide () {
+
+ if ( this.lanePath ) this.lanePath.show();
+
+ }
+}
+
+export class LanePathObject extends Object3D {
+
+ public static tag = 'lane-path';
+
+ public mesh: Mesh | Line;
+
+ public isSelected: boolean;
+
+ constructor (
+ public incomingRoad: TvRoad,
+ public connectingRoad: TvRoad,
+ public connection: TvJunctionConnection,
+ public link: TvJunctionLaneLink
+ ) {
+ super();
+ }
+
+ get material () { return this.mesh.material as LineBasicMaterial }
+
+ select () {
+
+ this.isSelected = true;
+
+ // red
+ this.material.color = new Color( 0xff0000 );
+ this.material.needsUpdate = true;
+
+ }
+
+ unselect () {
+
+ this.isSelected = false;
+
+ // green
+ this.material.color = new Color( 0x00ffff );
+ this.material.needsUpdate = true;
+
+ }
+
+ hide () {
+
+ this.visible = false;
+ this.mesh.visible = false;
+
+ }
+
+ show () {
+
+ this.visible = true;
+ this.mesh.visible = true;
+
+ }
+}
diff --git a/src/app/modules/tv-map/models/tv-junction-priority.ts b/src/app/modules/tv-map/models/tv-junction-priority.ts
new file mode 100644
index 00000000..52723f50
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-junction-priority.ts
@@ -0,0 +1,30 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+export class TvJunctionPriority {
+
+ public attr_high: number;
+ public attr_low: number;
+
+ constructor ( high: number, low: number ) {
+ this.attr_high = high;
+ this.attr_low = low;
+ }
+
+ get high () {
+ return this.attr_high;
+ }
+
+ set high ( value: number ) {
+ this.attr_high = value;
+ }
+
+ get low () {
+ return this.attr_low;
+ }
+
+ set low ( value: number ) {
+ this.attr_low = value;
+ }
+}
diff --git a/src/app/modules/tv-map/models/tv-junction.ts b/src/app/modules/tv-map/models/tv-junction.ts
new file mode 100644
index 00000000..112847f1
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-junction.ts
@@ -0,0 +1,396 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { Maths } from '../../../utils/maths';
+import { TvJunctionConnection } from './tv-junction-connection';
+import { TvJunctionPriority } from './tv-junction-priority';
+import { TvJunctionController } from './tv-junction-controller';
+import { Vector3 } from 'three';
+import { TvContactPoint } from './tv-common';
+import { TvRoad } from './tv-road.model';
+
+export class TvJunction {
+
+ public position?: Vector3;
+
+ private _priorities: TvJunctionPriority[] = [];
+ private _controllers: TvJunctionController[] = [];
+ private _connections: Map = new Map();
+ private _name: string;
+ private _id: number;
+ private lastAddedJunctionConnectionIndex: number;
+ private lastAddedJunctionPriorityIndex: number;
+ private lastAddedJunctionControllerIndex: number;
+
+ constructor ( name: string, id: number ) {
+ this._name = name;
+ this._id = id;
+ }
+
+ get controllers (): TvJunctionController[] {
+ return this._controllers;
+ }
+
+ set controllers ( value: TvJunctionController[] ) {
+ this._controllers = value;
+ }
+
+ get priorities (): TvJunctionPriority[] {
+ return this._priorities;
+ }
+
+ set priorities ( value: TvJunctionPriority[] ) {
+ this._priorities = value;
+ }
+
+ get connections (): Map {
+ return this._connections;
+ }
+
+ set connections ( value: Map ) {
+ this._connections = value;
+ }
+
+ get id (): number {
+ return this._id;
+ }
+
+ set id ( value: number ) {
+ this._id = value;
+ }
+
+ get name (): string {
+ return this._name;
+ }
+
+ set name ( value: string ) {
+ this._name = value;
+ }
+
+ /**
+ * Adds a junction connection to the junction
+ *
+ * @param id ID within the junction
+ * @param incomingRoad ID of the incoming road
+ * @param connectingRoad ID of the connecting path
+ * @param contactPoint Contact point on the connecting road (start or end)
+ */
+ public addJunctionConnection ( id, incomingRoad, connectingRoad, contactPoint, outgoingRoad? ): TvJunctionConnection {
+
+ const connection = new TvJunctionConnection( id, incomingRoad, connectingRoad, contactPoint, outgoingRoad );
+
+ this._connections.set( id, connection );
+
+ return connection;
+ }
+
+ /**
+ * Adds a junction connection to the junction
+ *
+ * @param incomingRoad ID of the incoming road
+ * @param connectingRoad ID of the connecting path
+ * @param contactPoint Contact point on the connecting road (start or end)
+ */
+ public addNewConnection (
+ incomingRoad: number,
+ connectingRoad: number,
+ contactPoint: TvContactPoint,
+ outgoingRoad?: number
+ ): TvJunctionConnection {
+
+ const id = this.connections.size + 1;
+
+ const connection = this.addJunctionConnection( id, incomingRoad, connectingRoad, contactPoint, outgoingRoad );
+
+ return connection;
+ }
+
+ removeConnectionByUuid ( uuid: string ) {
+
+ throw new Error( "method not implemented" );
+
+ }
+
+ removeConnectionById ( id: number ): boolean {
+
+ return this.connections.delete( id );
+
+ }
+
+ removeConnection ( connection: TvJunctionConnection, incomingRoad: TvRoad, outgoingRoad: TvRoad ) {
+
+ if ( this.removeConnectionById( connection.id ) ) {
+
+ this.removeJunctionRelation( incomingRoad );
+
+ this.removeJunctionRelation( outgoingRoad );
+
+ }
+
+ }
+
+ /**
+ * Adds a priority parameter to the junction
+ *
+ * @param {number} high ID of the connecting road with higher priority
+ * @param {number} low ID of the connecting road with lower priority
+ * @returns {number}
+ */
+ public addJunctionPriority ( high: number, low: number ): TvJunctionPriority {
+
+ const priority = new TvJunctionPriority( high, low );
+
+ this._priorities.push( priority );
+
+ return priority;
+ }
+
+ /**
+ * Adds a controller to the junction
+ *
+ * @param {number} id ID of the controller to add
+ * @param {string} type Type of control
+ * @returns {number}
+ */
+ public addJunctionController ( id: number, type: string ): TvJunctionController {
+
+ const controller = new TvJunctionController( id, type );
+
+ this._controllers.push( controller );
+
+ return controller;
+ }
+
+ public closeJunctionConnection ( index ) {
+
+ // TODO:
+
+ }
+
+ public closeJunctionPriority ( index ) {
+
+ // TODO:
+
+ }
+
+ public closeJunctionController ( index ) {
+
+ // TODO:
+
+ }
+
+ public deleteJunctionConnection ( id: number ): void {
+
+ this._connections.delete( id );
+
+ }
+
+ public deleteJunctionPriority ( index: number ): void {
+
+ this._priorities.splice( index, 1 );
+
+ }
+
+ public deleteJunctionController ( index: number ): void {
+
+ this._controllers.splice( index, 1 );
+
+ }
+
+ public getJunctionPriorities (): TvJunctionPriority[] {
+
+ return this._priorities;
+
+ }
+
+ public getJunctionControllers (): TvJunctionController[] {
+
+ return this._controllers;
+
+ }
+
+ public getJunctionConnectionCount (): number {
+
+ return this._connections.size;
+
+ }
+
+ public getJunctionPriorityCount (): number {
+
+ return this._priorities.length;
+
+ }
+
+ public getJunctionControllerCount (): number {
+
+ return this._controllers.length;
+
+ }
+
+
+ public getJunctionConnection ( id: number ) {
+
+ return this.connections.get( id );
+
+ }
+
+ public getJunctionPriority ( index: number ) {
+
+ if ( index < this._priorities.length && this._priorities.length > 0 ) {
+
+ return this._priorities[ index ];
+
+ }
+
+ return null;
+ }
+
+ public getJunctionController ( index: number ) {
+
+ if ( index < this._controllers.length && this._controllers.length > 0 ) {
+
+ return this._controllers[ index ];
+
+ }
+
+ return null;
+ }
+
+ public getLastAddedJunctionConnection () {
+
+ if ( this.lastAddedJunctionConnectionIndex < this._connections.size ) {
+
+ return this._connections[ this.lastAddedJunctionConnectionIndex ];
+
+ }
+
+ return null;
+ }
+
+ public getLastAddedJunctionPriority () {
+
+ if ( this.lastAddedJunctionPriorityIndex < this._priorities.length ) {
+
+ return this._priorities[ this.lastAddedJunctionPriorityIndex ];
+
+ }
+
+ return null;
+ }
+
+ public getLastAddedJunctionController () {
+
+ if ( this.lastAddedJunctionConnectionIndex < this._controllers.length ) {
+
+ return this._controllers[ this.lastAddedJunctionConnectionIndex ];
+
+ }
+
+ return null;
+ }
+
+ addConnection ( connection: TvJunctionConnection ) {
+
+ this._connections.set( connection.id, connection );
+
+ }
+
+ addPriority ( priority: TvJunctionPriority ) {
+
+ this._priorities.push( priority );
+
+ }
+
+ addController ( controller: TvJunctionController ) {
+
+ this._controllers.push( controller );
+
+ }
+
+ getRandomConnectionFor ( incomingRoadId: number, laneId?: number ): TvJunctionConnection {
+
+ const connections = [ ...this.connections.values() ].filter( connection => {
+
+ if ( laneId ) {
+
+ return ( connection.incomingRoad === incomingRoadId && connection.getToLaneId( laneId ) );
+
+ } else {
+
+ return ( connection.incomingRoad === incomingRoadId );
+
+ }
+
+ } );
+
+ const randomIndex = Maths.randomNumberBetween( 0, connections.length - 1 );
+
+ return connections[ randomIndex ];
+ }
+
+ private removeJunctionRelation ( road: TvRoad ): void {
+
+ let hasConnections = false;
+
+ for ( const connection of this.connections ) {
+
+ if ( connection[ 1 ].incomingRoad === road.id ) {
+
+ hasConnections = true;
+
+ } else if ( connection[ 1 ].outgoingRoad === road.id ) {
+
+ hasConnections = true;
+
+ }
+ }
+
+ if ( !hasConnections ) {
+
+ if ( road.successor && road.successor.elementType === "junction" && road.successor.elementId === this.id ) {
+
+ road.successor = null;
+
+ } else if ( road.predecessor && road.predecessor.elementType === "junction" && road.predecessor.elementId === this.id ) {
+
+ road.predecessor = null;
+
+ }
+
+ }
+ }
+
+ private addJunctionRelation ( road: TvRoad ): void {
+
+ // let hasConnections = false;
+
+ // for ( const connection of this.connections ) {
+
+ // if ( connection[ 1 ].incomingRoad === road.id ) {
+
+ // hasConnections = true;
+
+ // } else if ( connection[ 1 ].outgoingRoad === road.id ) {
+
+ // hasConnections = true;
+
+ // }
+ // }
+
+ // if ( !hasConnections ) {
+
+ // if ( road.successor && road.successor.elementType === "junction" && road.successor.elementId === this.id ) {
+
+ // road.setSuccessor();
+
+ // } else if ( road.predecessor && road.predecessor.elementType === "junction" && road.predecessor.elementId === this.id ) {
+
+ // road.predecessor = null;
+
+ // }
+
+ // }
+ }
+}
+
diff --git a/src/app/modules/tv-map/models/tv-lane-access.ts b/src/app/modules/tv-map/models/tv-lane-access.ts
new file mode 100644
index 00000000..c63d9661
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-lane-access.ts
@@ -0,0 +1,30 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+export class TvLaneAccess {
+
+ public attr_sOffset;
+ public attr_restriction;
+
+ constructor ( sOffset: number, restriction: string ) {
+ this.attr_sOffset = sOffset;
+ this.attr_restriction = restriction;
+ }
+
+ get sOffset () {
+ return this.attr_sOffset;
+ }
+
+ set sOffset ( value ) {
+ this.attr_sOffset = value;
+ }
+
+ get restriction () {
+ return this.attr_restriction;
+ }
+
+ set restriction ( value ) {
+ this.attr_restriction = value;
+ }
+}
\ No newline at end of file
diff --git a/src/app/modules/tv-map/models/tv-lane-border.ts b/src/app/modules/tv-map/models/tv-lane-border.ts
new file mode 100644
index 00000000..25c59fe0
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-lane-border.ts
@@ -0,0 +1,8 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { ThirdOrderPolynom } from './third-order-polynom';
+
+export class TvLaneBorder extends ThirdOrderPolynom {
+}
\ No newline at end of file
diff --git a/src/app/modules/tv-map/models/tv-lane-coord.ts b/src/app/modules/tv-map/models/tv-lane-coord.ts
new file mode 100644
index 00000000..04b4b986
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-lane-coord.ts
@@ -0,0 +1,60 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+export class TvCoord {
+
+ constructor ( x, y, z, h, p, r ) {
+ }
+
+ static getDist2d ( a: TvCoord, b: TvCoord ) {
+
+ }
+
+ static getDist3d ( a: TvCoord, b: TvCoord ) {
+
+ }
+}
+
+export class TvLaneCoord {
+
+ // total 4 properties
+ // road-id
+ // lane-section-id
+ // s
+ // lane-Id
+ // lane-offset
+
+ constructor ( public roadId: number, public sectionId: number, public laneId: number, public s: number, public offset: number ) {
+
+ }
+
+ init () {
+
+ }
+
+ addTrackCoord ( value: TvRoadCoord ) {
+
+ }
+
+}
+
+export class TvRoadCoord {
+
+ constructor ( public roadId, public s: number, public t: number = 0, public z: number = 0, public h?, public p?, public r? ) {
+
+ }
+
+ init () {
+
+ }
+
+ add ( value: TvRoadCoord ) {
+ }
+}
+
+export class TvGeoCoord {
+
+ constructor ( lat, long, z, h, p, r ) {
+ }
+}
diff --git a/src/app/modules/tv-map/models/tv-lane-height.ts b/src/app/modules/tv-map/models/tv-lane-height.ts
new file mode 100644
index 00000000..71a4cfb0
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-lane-height.ts
@@ -0,0 +1,48 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+export class TvLaneHeight {
+ public attr_sOffset;
+ public attr_inner = 0;
+ public attr_outer = 0;
+
+ constructor ( sOffset: number, inner: number, outer: number ) {
+ this.attr_sOffset = sOffset;
+ this.attr_inner = inner;
+ this.attr_outer = outer;
+ }
+
+ get sOffset () {
+ return this.attr_sOffset;
+ }
+
+ set sOffset ( value ) {
+ this.attr_sOffset = value;
+ }
+
+ getOuter () {
+ return this.attr_outer;
+ }
+
+ get outer () {
+ return this.attr_outer;
+ }
+
+ setOuter ( value ) {
+ this.attr_outer = value;
+ }
+
+ getInner () {
+ return this.attr_inner;
+ }
+
+ get inner () {
+ return this.attr_inner;
+ }
+
+ setInner ( value ) {
+ this.attr_inner = value;
+ }
+
+}
\ No newline at end of file
diff --git a/src/app/modules/tv-map/models/tv-lane-material.ts b/src/app/modules/tv-map/models/tv-lane-material.ts
new file mode 100644
index 00000000..8a3a0d78
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-lane-material.ts
@@ -0,0 +1,55 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+export class TvLaneMaterial {
+
+ private _sOffset: number;
+ private _surface: string;
+ private _friction: number;
+ private _roughness: number;
+
+ constructor ( sOffset: number, surface: string, friction: number, roughness: number ) {
+ this._sOffset = sOffset;
+ this._surface = surface;
+ this._friction = friction;
+ this._roughness = roughness;
+ }
+
+ get s () {
+ return this.sOffset;
+ }
+
+ get sOffset () {
+ return this._sOffset;
+ }
+
+ set sOffset ( value ) {
+ this._sOffset = value;
+ }
+
+ get surface () {
+ return this._surface;
+ }
+
+ set surface ( value ) {
+ this._surface = value;
+ }
+
+ get friction () {
+ return this._friction;
+ }
+
+ set friction ( value ) {
+ this._friction = value;
+ }
+
+ get roughness () {
+ return this._roughness;
+ }
+
+ set roughness ( value ) {
+ this._roughness = value;
+ }
+
+}
diff --git a/src/app/modules/tv-map/models/tv-lane-road-mark.ts b/src/app/modules/tv-map/models/tv-lane-road-mark.ts
new file mode 100644
index 00000000..fd304c90
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-lane-road-mark.ts
@@ -0,0 +1,188 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvColors, TvRoadMarkTypes, TvRoadMarkWeights } from './tv-common';
+import { GameObject } from '../../../core/game-object';
+import { TvLane } from './tv-lane';
+import { LaneRoadMarkNode } from '../../three-js/objects/control-point';
+import { Math } from 'three';
+
+export class TvLaneRoadMark {
+
+ public readonly uuid: string;
+
+ // widths for the two types of weight values
+ public static STD_ROADMARK_WIDTH = 0.15;
+ public static BOLD_ROADMARK_WIDTH = 0.3;
+ // elevation shift, so that the road mark is drawn above the road
+ public static ROADMARK_ELEVATION_SHIFT = 0.01;
+ // broken mark tiling
+ public static ROADMARK_BROKEN_TILING = 3.0;
+ public gameObject: GameObject;
+ public attr_sOffset: number;
+ public attr_type: TvRoadMarkTypes;
+ public attr_weight: TvRoadMarkWeights;
+ public attr_color: TvColors;
+ public attr_material: string;
+ public attr_width: number;
+ public attr_laneChange: string;
+ public attr_height: number = 0;
+ public lastSCoordinate: number;
+ public readonly lane: TvLane;
+ public node: LaneRoadMarkNode;
+
+ constructor (
+ sOffset: number,
+ type: TvRoadMarkTypes,
+ weight: TvRoadMarkWeights,
+ color: TvColors,
+ width: number,
+ laneChange: string,
+ height: number,
+ lane: TvLane
+ ) {
+
+ this.uuid = Math.generateUUID();
+
+ this.attr_sOffset = sOffset;
+ this.attr_type = type;
+ this.attr_weight = weight;
+ this.attr_color = color;
+ this.attr_width = width;
+ this.attr_laneChange = laneChange;
+ this.attr_height = height;
+
+ this.lane = lane;
+ }
+
+ get length () {
+ return this.lastSCoordinate - this.sOffset;
+ }
+
+ get s () {
+ return this.sOffset;
+ }
+
+ get sOffset () {
+ return this.attr_sOffset;
+ }
+
+ set sOffset ( value ) {
+ this.attr_sOffset = value;
+ }
+
+ get type () {
+ return this.attr_type;
+ }
+
+ set type ( value: TvRoadMarkTypes ) {
+ this.attr_type = value;
+ }
+
+ get weight (): TvRoadMarkWeights {
+ return this.attr_weight;
+ }
+
+ set weight ( value ) {
+ this.attr_weight = value;
+ }
+
+ get color () {
+ return this.attr_color;
+ }
+
+ set color ( value: TvColors ) {
+ this.attr_color = value;
+ }
+
+ get material () {
+ return this.attr_material;
+ }
+
+ get width () {
+ return this.attr_width;
+ }
+
+ set width ( value: number ) {
+ this.attr_width = value;
+ }
+
+ get laneChange () {
+ return this.attr_laneChange;
+ }
+
+ get height () {
+ return this.attr_height;
+ }
+
+ getType () {
+ return this.attr_type;
+ }
+
+ setType ( value ) {
+ this.attr_type = value;
+ }
+
+ getWeight () {
+ return this.attr_weight;
+ }
+
+ setWeight ( value ) {
+ this.attr_weight = value;
+ }
+
+ getColor () {
+ return this.attr_color;
+ }
+
+ setColor ( value ) {
+ this.attr_color = value;
+ }
+
+ getMaterial () {
+ return this.attr_material;
+ }
+
+ setMaterial ( value ) {
+ this.attr_material = value;
+ }
+
+ getWidth () {
+ return this.attr_width;
+ }
+
+ setWidth ( value ) {
+ this.attr_width = value;
+ }
+
+ getLaneChange () {
+ return this.attr_laneChange;
+ }
+
+ setLaneChange ( value ) {
+ this.attr_laneChange = value;
+ }
+
+ getHeight () {
+ return this.attr_height;
+ }
+
+ setHeight ( value ) {
+ this.attr_height = value;
+ }
+
+ clone ( s?: number ) {
+
+ return new TvLaneRoadMark(
+ s || this.sOffset,
+ this.type,
+ this.weight,
+ this.color,
+ this.width,
+ this.laneChange,
+ this.height,
+ this.lane
+ );
+ }
+}
diff --git a/src/app/modules/tv-map/models/tv-lane-section-sample.ts b/src/app/modules/tv-map/models/tv-lane-section-sample.ts
new file mode 100644
index 00000000..265dc1e0
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-lane-section-sample.ts
@@ -0,0 +1,202 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvLaneHeight } from './tv-lane-height';
+import { TvLaneRoadMark } from './tv-lane-road-mark';
+
+
+export class TvLaneSectionSample {
+
+ private mLeftTypeVector: string[] = [];
+ private mLeftWidthVector: number[] = [];
+ private mLeftHeightVector: TvLaneHeight[] = [];
+ private mLeftRoadMarkVector: TvLaneRoadMark[] = [];
+ private mLeftLevelVector: boolean[] = [];
+
+ private mRightTypeVector: string[] = [];
+ private mRightWidthVector: number[] = [];
+ private mRightHeightVector: TvLaneHeight[] = [];
+ private mRightRoadMarkVector: TvLaneRoadMark[] = [];
+ private mRightLevelVector: boolean[] = [];
+
+
+ /**
+ * Lane Section Sample. Holds all the lane information at a certain S value including lane widths, levels,
+ * heights, etc
+ *
+ *
+ *
+ *
+ */
+ constructor () {
+
+ }
+
+
+ addLeftType ( type: string ): void {
+ this.mLeftTypeVector.push( type );
+ }
+
+ addLeftWidth ( width: number ): void {
+ this.mLeftWidthVector.push( width );
+ }
+
+ addLeftHeight ( height: TvLaneHeight ): void {
+ this.mLeftHeightVector.push( height );
+ }
+
+ addLeftRoadMark ( roadMark: TvLaneRoadMark ): void {
+ this.mLeftRoadMarkVector.push( roadMark );
+ }
+
+ addLeftLevel ( level: boolean ): void {
+ this.mLeftLevelVector.push( level );
+ }
+
+
+ addRightType ( type: string ): void {
+ this.mRightTypeVector.push( type );
+ }
+
+ addRightWidth ( width: number ): void {
+ this.mRightWidthVector.push( width );
+ }
+
+ addRightHeight ( height: TvLaneHeight ): void {
+ this.mRightHeightVector.push( height );
+ }
+
+ addRightRoadMark ( roadMark: TvLaneRoadMark ): void {
+ this.mRightRoadMarkVector.push( roadMark );
+ }
+
+ addRightLevel ( level: boolean ): void {
+ this.mRightLevelVector.push( level );
+ }
+
+ addLeftRecord ( type: string, width: number, height: TvLaneHeight, roadMark: TvLaneRoadMark, level: boolean ): void {
+ this.addLeftType( type );
+ this.addLeftWidth( width );
+ this.addLeftHeight( height );
+ this.addLeftRoadMark( roadMark );
+ this.addLeftLevel( level );
+ }
+
+ addRightRecord ( type: string, width: number, height: TvLaneHeight, roadMark: TvLaneRoadMark, level: boolean ): void {
+ this.addRightType( type );
+ this.addRightWidth( width );
+ this.addRightHeight( height );
+ this.addRightRoadMark( roadMark );
+ this.addRightLevel( level );
+ }
+
+ // LEFT
+
+ getLeftType ( i: number ): string {
+ return this.mLeftTypeVector[ i ];
+ }
+
+ getLeftWidth ( i: number ): number {
+ return this.mLeftWidthVector[ i ];
+ }
+
+ getLeftHeight ( i: number ): TvLaneHeight {
+ return this.mLeftHeightVector[ i ];
+ }
+
+ getLeftRoadMark ( i: number ): TvLaneRoadMark {
+ return this.mLeftRoadMarkVector[ i ];
+ }
+
+ getLeftLevel ( i: number ): boolean {
+ return this.mLeftLevelVector[ i ];
+ }
+
+ // RIGHT
+
+ getRightType ( i: number ): string {
+ return this.mRightTypeVector[ i ];
+ }
+
+ getRightWidth ( i: number ): number {
+ return this.mRightWidthVector[ i ];
+ }
+
+ getRightHeight ( i: number ): TvLaneHeight {
+ return this.mRightHeightVector[ i ];
+ }
+
+ getRightRoadMark ( i: number ): TvLaneRoadMark {
+ return this.mRightRoadMarkVector[ i ];
+ }
+
+ getRightLevel ( i: number ): boolean {
+ return this.mRightLevelVector[ i ];
+ }
+
+ getLeftVectorsSize (): number {
+ return this.mLeftWidthVector.length;
+ }
+
+ getRightVectorsSize (): number {
+ return this.mRightWidthVector.length;
+ }
+
+ getLeftTypeVector (): string[] {
+ return this.mLeftTypeVector;
+ }
+
+ getLeftWidthVector (): number[] {
+ return this.mLeftWidthVector;
+ }
+
+ getLeftHeigthVector (): TvLaneHeight[] {
+ return this.mLeftHeightVector;
+ }
+
+ getLeftRoadMarkVector (): TvLaneRoadMark[] {
+ return this.mLeftRoadMarkVector;
+ }
+
+ getLeftLevelVector (): boolean[] {
+ return this.mLeftLevelVector;
+ }
+
+ getRightTypeVector (): string[] {
+ return this.mRightTypeVector;
+ }
+
+ getRightWidthVector (): number[] {
+ return this.mRightWidthVector;
+ }
+
+ getRightHeigthVector (): TvLaneHeight[] {
+ return this.mRightHeightVector;
+ }
+
+ getRightRoadMarkVector (): TvLaneRoadMark[] {
+ return this.mRightRoadMarkVector;
+ }
+
+ getRightLevelVector (): boolean[] {
+ return this.mRightLevelVector;
+ }
+
+ clearVectors (): void {
+
+ this.mLeftTypeVector.splice( 0, this.mLeftTypeVector.length );
+ this.mLeftWidthVector.splice( 0, this.mLeftWidthVector.length );
+ this.mLeftHeightVector.splice( 0, this.mLeftHeightVector.length );
+ this.mLeftRoadMarkVector.splice( 0, this.mLeftRoadMarkVector.length );
+ this.mLeftLevelVector.splice( 0, this.mLeftLevelVector.length );
+
+ this.mRightTypeVector.splice( 0, this.mRightTypeVector.length );
+ this.mRightWidthVector.splice( 0, this.mRightWidthVector.length );
+ this.mRightHeightVector.splice( 0, this.mRightHeightVector.length );
+ this.mRightRoadMarkVector.splice( 0, this.mRightRoadMarkVector.length );
+ this.mRightLevelVector.splice( 0, this.mRightLevelVector.length );
+
+ }
+
+}
diff --git a/src/app/modules/tv-map/models/tv-lane-section.spec.ts b/src/app/modules/tv-map/models/tv-lane-section.spec.ts
new file mode 100644
index 00000000..d6c1f97c
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-lane-section.spec.ts
@@ -0,0 +1,162 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvLaneSide, TvLaneType } from './tv-common';
+import { TvLaneSection } from './tv-lane-section';
+import { TvLane } from './tv-lane';
+import { TvMapSourceFile } from '../services/tv-map-source-file';
+import { TvMap } from './tv-map.model';
+
+describe( 'OpenDrive LaneSection', () => {
+
+ let laneSection: TvLaneSection;
+
+ let leftOne: TvLane;
+ let leftTwo: TvLane;
+ let leftThree: TvLane;
+ let rightOne: TvLane;
+ let rightTwo: TvLane;
+ let rightThree: TvLane;
+ let rightFour: TvLane;
+
+ beforeEach( () => {
+
+ laneSection = new TvLaneSection( 1, 0, true, 1 );
+
+ laneSection.addLane( TvLaneSide.LEFT, 2, TvLaneType.driving, true, true );
+ leftTwo = laneSection.getLastAddedLane();
+
+ laneSection.addLane( TvLaneSide.LEFT, 3, TvLaneType.driving, true, true );
+ leftThree = laneSection.getLastAddedLane();
+
+ laneSection.addLane( TvLaneSide.LEFT, 1, TvLaneType.driving, true, true );
+ leftOne = laneSection.getLastAddedLane();
+
+ laneSection.addLane( TvLaneSide.CENTER, 0, TvLaneType.driving, true, true );
+
+ laneSection.addLane( TvLaneSide.RIGHT, -1, TvLaneType.driving, true, true );
+ rightOne = laneSection.getLastAddedLane();
+
+ laneSection.addLane( TvLaneSide.RIGHT, -3, TvLaneType.driving, true, true );
+ rightThree = laneSection.getLastAddedLane();
+
+ laneSection.addLane( TvLaneSide.RIGHT, -2, TvLaneType.driving, true, true );
+ rightTwo = laneSection.getLastAddedLane();
+
+ laneSection.addLane( TvLaneSide.RIGHT, -4, TvLaneType.driving, true, true );
+ rightFour = laneSection.getLastAddedLane();
+
+ laneSection.getLaneVector().forEach( lane => {
+
+ if ( lane.side !== TvLaneSide.CENTER ) {
+
+ lane.addWidthRecord( 0, 2, 0, 0, 0 );
+
+ }
+
+ } );
+
+ } );
+
+ it( 'should give correct width for first left lanes', () => {
+
+ const start = laneSection.getWidthUptoStart( leftOne, 0 );
+ const center = laneSection.getWidthUptoCenter( leftOne, 0 );
+ const end = laneSection.getWidthUptoEnd( leftOne, 0 );
+
+ expect( start ).toBe( 0 );
+ expect( center ).toBe( 1 );
+ expect( end ).toBe( 2 );
+
+ } );
+
+ it( 'should give correct width for second left lanes', () => {
+
+ const start = laneSection.getWidthUptoStart( leftTwo, 0 );
+ const center = laneSection.getWidthUptoCenter( leftTwo, 0 );
+ const end = laneSection.getWidthUptoEnd( leftTwo, 0 );
+
+ expect( start ).toBe( 2 );
+ expect( center ).toBe( 3 );
+ expect( end ).toBe( 4 );
+
+ } );
+
+ it( 'should give correct width for first right lanes', () => {
+
+ const start = laneSection.getWidthUptoStart( rightOne, 0 );
+ const center = laneSection.getWidthUptoCenter( rightOne, 0 );
+ const end = laneSection.getWidthUptoEnd( rightOne, 0 );
+
+ expect( start ).toBe( 0 );
+ expect( center ).toBe( 1 );
+ expect( end ).toBe( 2 );
+
+ } );
+
+ it( 'should give correct width for second right lanes', () => {
+
+ const start = laneSection.getWidthUptoStart( rightTwo, 0 );
+ const center = laneSection.getWidthUptoCenter( rightTwo, 0 );
+ const end = laneSection.getWidthUptoEnd( rightTwo, 0 );
+
+ expect( start ).toBe( 2 );
+ expect( center ).toBe( 3 );
+ expect( end ).toBe( 4 );
+
+ } );
+
+ it( 'should give correct count for total lanes', () => {
+ expect( laneSection.getLaneCount() ).toBe( 8 );
+ } );
+
+ it( 'should give correct count for left lanes', () => {
+ expect( laneSection.getLeftLaneCount() ).toBe( 3 );
+ } );
+
+ it( 'should give correct count for right lanes', () => {
+ expect( laneSection.getRightLaneCount() ).toBe( 4 );
+ } );
+
+ it( 'should give correct length for laneSection', () => {
+
+ TvMapSourceFile.openDrive = new TvMap();
+
+ const road = TvMapSourceFile.openDrive.addRoad( "", 100, 1, -1 );
+
+ const section1 = road.addGetLaneSection( 0, false );
+ const section2 = road.addGetLaneSection( 40, false );
+ const section3 = road.addGetLaneSection( 50, false );
+
+ expect( section1.roadId ).toBe( 1 );
+ expect( section2.roadId ).toBe( 1 );
+ expect( section3.roadId ).toBe( 1 );
+
+ expect( road.getLaneSectionLength( section1 ) ).toBe( 40 );
+ expect( road.getLaneSectionLength( section2 ) ).toBe( 10 );
+ expect( road.getLaneSectionLength( section3 ) ).toBe( 50 );
+
+ expect( section1.length ).toBe( 40 );
+ expect( section2.length ).toBe( 10 );
+ expect( section3.length ).toBe( 50 );
+
+ } );
+
+ it( 'should copy lane correctly', () => {
+
+ // 3 left 1 center 4 right
+ expect( laneSection.lanes.size ).toBe( 8 );
+
+ const newLane = leftTwo.clone( 1 );
+
+ laneSection.addLaneInstance( newLane, true );
+
+ expect( laneSection.lanes.size ).toBe( 9 );
+
+ expect( laneSection.lanes.get( 3 ).id ).toBe( 3 );
+ expect( laneSection.lanes.get( 4 ).id ).toBe( 4 );
+
+ } );
+
+} );
diff --git a/src/app/modules/tv-map/models/tv-lane-section.ts b/src/app/modules/tv-map/models/tv-lane-section.ts
new file mode 100644
index 00000000..4a67b05d
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-lane-section.ts
@@ -0,0 +1,823 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvLaneSide, TvLaneType } from './tv-common';
+import { TvLane } from './tv-lane';
+import { TvLaneSectionSample } from './tv-lane-section-sample';
+import { GameObject } from 'app/core/game-object';
+import { TvLaneRoadMark } from './tv-lane-road-mark';
+import { TvLaneHeight } from './tv-lane-height';
+import { Debug } from 'app/core/utils/debug';
+import { Math } from 'three';
+
+export class TvLaneSection {
+
+ public readonly id: number;
+ public readonly uuid: string;
+
+ public gameObject: GameObject;
+ public roadId: number;
+ public attr_s: number;
+ public attr_singleSide: boolean;
+ // old property
+ public lastSCoordinate: number;
+
+ // public left: OdRoadLaneSectionContainer;
+ // public center: OdRoadLaneSectionContainer;
+ // public right: OdRoadLaneSectionContainer;
+ private lastAddedLaneIndex: number;
+ private laneMap: Map = new Map();
+
+ private _length: number;
+
+ public get lanes () {
+ return this.laneMap;
+ }
+
+ constructor ( id: number, s: number, singleSide: boolean, roadId: number ) {
+ this.uuid = Math.generateUUID();
+ this.id = id;
+ this.attr_s = s;
+ this.attr_singleSide = singleSide;
+ this.roadId = roadId;
+ }
+
+ get s () {
+ return this.attr_s;
+ }
+
+ get length () {
+ return this._length;
+ // return this.lastSCoordinate - this.s;
+ }
+
+ set length ( value: number ) {
+ this._length = value;
+ }
+
+ // private laneVector: OdLane[] = [];
+ private get laneVector (): TvLane[] {
+ return [ ...this.laneMap.values() ];
+ }
+
+ updateMeshGeometry ( offset: number ): any {
+
+ this.getLeftLanes().reverse().forEach( ( lane, i ) => {
+
+ Debug.log( i, lane );
+
+ } );
+
+
+ }
+
+ getWidthUptoStart ( lane: TvLane, sCoordinate: number ): number {
+
+ let width = 0;
+ let lanes: TvLane[] = [];
+
+ if ( lane.side == TvLaneSide.RIGHT ) {
+
+ lanes = this.getRightLanes();
+
+ } else if ( lane.side == TvLaneSide.LEFT ) {
+
+ lanes = this.getLeftLanes().reverse();
+
+ } else {
+
+ width = 0;
+
+ return width;
+
+ }
+
+ for ( let i = 0; i < lanes.length; i++ ) {
+
+ // TODO: Check if this correct
+
+ var element = lanes[ i ];
+
+ if ( element.id == lane.id ) break;
+
+ width += element.getWidthValue( sCoordinate );
+ }
+
+ // console.log(`upto-start lane-id: ${lane.id} s: ${sCoordinate} width: ${width}`);
+
+ return width;
+
+ }
+
+ getWidthUptoEnd ( lane: TvLane, sCoordinate: number ): number {
+
+ let width = 0;
+ let lanes: TvLane[] = [];
+
+ if ( lane.side == TvLaneSide.RIGHT ) {
+
+ lanes = this.getRightLanes();
+
+ } else if ( lane.side == TvLaneSide.LEFT ) {
+
+ lanes = this.getLeftLanes().reverse();
+
+ } else {
+
+ return width = 0;
+
+ }
+
+ for ( let i = 0; i < lanes.length; i++ ) {
+
+ // TODO: Check if this correct
+
+ var element = lanes[ i ];
+
+ width += element.getWidthValue( sCoordinate );
+
+ if ( element.id == lane.id ) break;
+ }
+
+ // console.log(`upto-end lane-id: ${lane.id} s: ${sCoordinate} width: ${width}`);
+
+ return width;
+
+ }
+
+ getWidthUptoCenter ( lane: TvLane, sCoordinate: number ): number {
+
+ let cumulativeWidth = 0;
+ let lanes: TvLane[] = [];
+
+ if ( lane.side == TvLaneSide.RIGHT ) {
+
+ lanes = this.getRightLanes();
+
+ } else if ( lane.side == TvLaneSide.LEFT ) {
+
+ lanes = this.getLeftLanes().reverse();
+
+ } else {
+
+ return 0;
+ }
+
+ for ( let i = 0; i < lanes.length; i++ ) {
+
+ let element = lanes[ i ];
+
+ let width = element.getWidthValue( sCoordinate );
+
+ cumulativeWidth += width;
+
+ if ( element.id == lane.id ) {
+
+ cumulativeWidth -= width / 2;
+ break;
+ }
+ }
+
+ return cumulativeWidth;
+
+ }
+
+ /**
+ * Add a lane to the lane section
+ *
+ * @param {TvLaneSide} laneSide side the side of the road to which the lane will be added
+ * @param {number} id of the lane
+ * @param {string} type of the lane (Section 6.5 of the OpenDRIVE specification)
+ * @param {boolean} level Level parameter of the road
+ * @param {boolean} sort Defines if the lanes should be sorted when added. True by default
+ */
+ addLane ( laneSide: TvLaneSide, id: number, type: TvLaneType, level: boolean, sort: boolean ) {
+
+ const newLane = new TvLane( laneSide, id, type, level, this.roadId, this.id );
+
+ this.addLaneInstance( newLane, sort );
+
+ return newLane;
+
+ // return;
+ //
+ // let counter = 0;
+ //
+ // if ( sort ) {
+ //
+ // switch ( laneSide ) {
+ //
+ // case LaneSide.RIGHT:
+ //
+ // this.laneVector.push( new OdLane( laneSide, id, type, level, this.roadId ) );
+ //
+ // this.lastAddedLaneIndex = this.laneVector.length - 1;
+ //
+ // break;
+ //
+ // case LaneSide.CENTER:
+ //
+ // const size = this.getLaneCount();
+ //
+ // if ( size > 0 ) {
+ //
+ // for ( let i = 0; i < size; i++ ) {
+ //
+ // if ( this.laneVector[ i ].getId() < 0 ) {
+ //
+ // counter = i;
+ //
+ // this.laneVector[ counter ] = new OdLane( laneSide, id, type, level, this.roadId );
+ //
+ // this.lastAddedLaneIndex = counter;
+ //
+ // }
+ //
+ // }
+ //
+ // } else {
+ //
+ // counter = 0;
+ //
+ // this.laneVector.push( new OdLane( laneSide, id, type, level, this.roadId ) );
+ //
+ // this.lastAddedLaneIndex = counter;
+ // }
+ //
+ // break;
+ //
+ // case LaneSide.LEFT:
+ //
+ // // add to the beginning of the array
+ // this.laneVector.unshift( new OdLane( laneSide, id, type, level, this.roadId ) );
+ //
+ // this.lastAddedLaneIndex = counter;
+ //
+ // break;
+ //
+ // }
+ // } else {
+ //
+ // this.laneVector.push( new OdLane( laneSide, id, type, level, this.roadId ) );
+ //
+ // this.lastAddedLaneIndex = this.laneVector.length - 1;
+ //
+ // return this.lastAddedLaneIndex;
+ // }
+ }
+
+ /**
+ * Delete lane at provided index
+ *
+ * @param index
+ */
+ deleteLane ( index ) {
+ this.laneVector.splice( index, 1 );
+ }
+
+ /**
+ * Delete the outside left lane
+ */
+ deleteLeftLane () {
+
+ // Remove first element of array
+ this.laneVector.shift();
+ }
+
+ /**
+ * Delete the outside right lane
+ */
+ deleteRightLane () {
+
+ // Remove last element of array
+ this.laneVector.pop();
+ }
+
+ getLastLane (): TvLane {
+
+ if ( this.laneVector.length > 0 ) {
+ return this.laneVector[ this.laneVector.length - 1 ];
+ }
+
+ return null;
+ }
+
+ getLastAddedLane (): TvLane {
+
+ if ( this.laneMap.has( this.lastAddedLaneIndex ) ) {
+ return this.laneMap.get( this.lastAddedLaneIndex );
+ }
+
+ // TODO : remove this
+ if ( this.lastAddedLaneIndex < this.laneVector.length ) {
+ return this.laneVector[ this.lastAddedLaneIndex ];
+ }
+
+ return null;
+ }
+
+ getLastLeftLane (): TvLane {
+
+ if ( this.laneVector.length > 0 ) {
+
+ if ( this.laneVector[ 0 ].getSide() === TvLaneSide.LEFT ) {
+
+ return this.laneVector[ 0 ];
+
+ } else {
+
+ return null;
+ }
+ }
+
+ return null;
+ }
+
+ getLastRightLane (): TvLane {
+
+ if ( this.laneVector.length > 0 ) {
+
+ const index = this.laneVector.length - 1;
+
+ if ( this.laneVector[ index ].getSide() === TvLaneSide.RIGHT ) {
+
+ return this.laneVector[ index ];
+
+ } else {
+
+ return null;
+ }
+ }
+
+ return null;
+ }
+
+ getLastCenterLane (): TvLane {
+
+ const size = this.getLaneCount();
+
+ for ( let i = 0; i < size; i++ ) {
+
+ if ( this.laneVector[ i ].getSide() === TvLaneSide.CENTER ) {
+
+ return this.laneVector[ i ];
+
+ }
+ }
+
+ return null;
+ }
+
+ getLane ( index ): TvLane {
+
+ if ( this.laneVector.length > 0 && index < this.laneVector.length ) {
+ return this.laneVector[ index ];
+ }
+
+ return null;
+ }
+
+ getLaneCount () {
+ return this.laneVector.length;
+ }
+
+ getLaneVector () {
+ return this.laneVector;
+ }
+
+ /**
+ * Get the lane section s-offset
+ */
+ getS () {
+ return this.attr_s;
+ }
+
+ setS ( value ) {
+ this.attr_s = value;
+ }
+
+ /**
+ * Get the lane section final s-offset which is the
+ * s-offset of the last record of the lane section
+ */
+ getS2 () {
+
+ let maxSValue = 0;
+
+ const size = this.getLaneCount();
+
+ for ( let i = 0; i < size; i++ ) {
+
+ const lane = this.getLane( i );
+
+ const width = lane.getLaneWidth( i );
+ if ( width != null ) {
+ if ( width.s > maxSValue ) {
+ maxSValue = width.s;
+ }
+ }
+
+ const roadMark = lane.getLaneRoadMark( i );
+ if ( roadMark != null ) {
+ if ( roadMark.sOffset > maxSValue ) {
+ maxSValue = roadMark.sOffset;
+ }
+ }
+
+ const material = lane.getLaneMaterial( i );
+ if ( material != null ) {
+ if ( material.sOffset > maxSValue ) {
+ maxSValue = material.sOffset;
+ }
+ }
+
+ const visibility = lane.getLaneVisibility( i );
+ if ( visibility != null ) {
+ if ( visibility.sOffset > maxSValue ) {
+ maxSValue = visibility.sOffset;
+ }
+ }
+
+ const speed = lane.getLaneSpeed( i );
+ if ( speed != null ) {
+ if ( speed.sOffset > maxSValue ) {
+ maxSValue = speed.sOffset;
+ }
+ }
+
+ const access = lane.getLaneAccess( i );
+ if ( access != null ) {
+ if ( access.sOffset > maxSValue ) {
+ maxSValue = access.sOffset;
+ }
+ }
+
+ const height = lane.getLaneHeight( i );
+ if ( height != null ) {
+ if ( height.sOffset > maxSValue ) {
+ maxSValue = height.sOffset;
+ }
+ }
+ }
+
+ return this.getS() + maxSValue;
+ }
+
+ /**
+ * Check if the tested s-offset is inside the lane section interval
+ * @param sCheck A double s-offset value that has to be checked
+ * @returns {boolean} Return true if the s-offset value belongs to current lane section, false otherwise
+ */
+ checkInterval ( sCheck ): boolean {
+
+ if ( sCheck >= this.attr_s ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Return the lane-0 index in the lanes vector
+ */
+ getZeroLaneIndex () {
+
+ for ( let i = 0; i < this.getLaneCount(); i++ ) {
+
+ if ( this.laneVector[ i ].getId() === 0 ) {
+
+ return i;
+
+ }
+
+ }
+
+ return 0;
+ }
+
+ getLeftLaneCount () {
+
+ const idGreaterThanZero = ( a, b ) => a[ 0 ] > 0;
+
+ const leftLanes = new Map( [ ...this.laneMap.entries() ].filter( idGreaterThanZero ) );
+
+ return leftLanes.size;
+
+ // let count = 0;
+ //
+ // for ( let i = 0; i < this.getLaneCount(); i++ ) {
+ //
+ // if ( this.laneVector[ i ].getSide() === LaneSide.LEFT ) {
+ //
+ // count++;
+ //
+ // }
+ // }
+ //
+ // return count;
+ }
+
+ getLeftLanes (): TvLane[] {
+
+ const idGreaterThanZero = ( a, b ) => a[ 0 ] > 0;
+
+ const leftLanes = new Map( [ ...this.laneMap.entries() ].filter( idGreaterThanZero ) );
+
+ return [ ...leftLanes.values() ];
+
+ // const lanes = [];
+ //
+ // for ( let i = 0; i < this.getLaneCount(); i++ ) {
+ //
+ // if ( this.laneVector[ i ].getSide() === LaneSide.LEFT ) {
+ //
+ // lanes.push( this.laneVector[ i ] );
+ //
+ // }
+ // }
+ //
+ // return lanes;
+ }
+
+ getCenterLaneCount () {
+
+ let count = 0;
+
+ for ( let i = 0; i < this.getLaneCount(); i++ ) {
+
+ if ( this.laneVector[ i ].getSide() === TvLaneSide.CENTER ) {
+
+ count++;
+
+ }
+ }
+
+ return count;
+ }
+
+ getRightLaneCount () {
+
+ const idLessThanZero = ( a, b ) => a[ 0 ] < 0;
+
+ const rightLanes = new Map( [ ...this.laneMap.entries() ].filter( idLessThanZero ) );
+
+ return rightLanes.size;
+
+ // let count = 0;
+ //
+ // for ( let i = 0; i < this.getLaneCount(); i++ ) {
+ //
+ // if ( this.laneVector[ i ].getSide() === LaneSide.RIGHT ) {
+ //
+ // count++;
+ //
+ // }
+ // }
+ //
+ // return count;
+ }
+
+ getRightLanes (): TvLane[] {
+
+ const idLessThanZero = ( a, b ) => a[ 0 ] < 0;
+
+ const rightLanes = new Map( [ ...this.laneMap.entries() ].filter( idLessThanZero ) );
+
+ return [ ...rightLanes.values() ];
+
+ // const lanes = [];
+ //
+ // for ( let i = 0; i < this.getLaneCount(); i++ ) {
+ //
+ // if ( this.laneVector[ i ].getSide() === LaneSide.RIGHT ) {
+ //
+ // lanes.push( this.laneVector[ i ] );
+ //
+ // }
+ // }
+ //
+ // return lanes;
+ }
+
+ /**
+ * Fill a special structure with all the lane / lane section data that is sampled at a provided s-offset position along the road
+ * @param sCheck s-offset along the road at which to sample the lane section
+ * @param laneSectionSample The structure that has to be filled with the sampled data
+ * @return Returns true if the operation was successful.
+ */
+ fillLaneSectionSample ( sCheck: number, laneSectionSample: TvLaneSectionSample ) {
+
+ laneSectionSample.clearVectors();
+
+
+ const leftMax = 0;
+ const rightMax = this.getLaneCount() - 1;
+
+ sCheck -= this.getS();
+
+ let level: boolean;
+ let type: string;
+ let height: TvLaneHeight;
+ let roadMark: TvLaneRoadMark;
+ let width = 0;
+
+ if ( this.getLeftLaneCount() > 0 ) {
+
+ const zeroLaneIndex = this.getZeroLaneIndex();
+
+ for ( let i = zeroLaneIndex; i >= leftMax; i-- ) {
+
+ const lane = this.getLane( i );
+
+ type = lane.getType();
+ level = lane.getLevel();
+ height = lane.getHeightValue( sCheck );
+ roadMark = lane.getRoadMark( sCheck );
+ width = lane.getWidthValue( sCheck ); // and accumulate the width
+
+ laneSectionSample.addLeftRecord( type, width, height, roadMark, level );
+ }
+ }
+
+ if ( this.getRightLaneCount() > 0 ) {
+
+ for ( let i = this.getZeroLaneIndex(); i <= rightMax; i++ ) {
+
+ const lane = this.getLane( i );
+
+ type = lane.getType();
+ level = lane.getLevel();
+ height = lane.getHeightValue( sCheck );
+ roadMark = lane.getRoadMark( sCheck );
+ width = lane.getWidthValue( sCheck );
+
+ laneSectionSample.addRightRecord( type, width, height, roadMark, level );
+
+ }
+ }
+
+ return true;
+ }
+
+ getLaneOffset ( lane: TvLane ) {
+
+ let offsetFromCenter = 0;
+
+ if ( this.laneVector.length > 0 ) {
+
+ for ( let i = 0; i < this.laneVector.length; i++ ) {
+
+ const element = this.laneVector[ i ];
+
+ if ( element.getSide() === lane.getSide() ) {
+
+ offsetFromCenter += element.getWidthValue( 0 );
+
+ }
+ }
+ }
+
+ return offsetFromCenter;
+ }
+
+ getLaneById ( laneId: number ): TvLane {
+
+ let lane = null;
+
+ if ( this.laneVector.length > 0 ) {
+
+ for ( let i = 0; i < this.laneVector.length; i++ ) {
+
+ const element = this.laneVector[ i ];
+
+ if ( element.id === laneId ) {
+
+ lane = element;
+ break;
+
+ }
+ }
+ }
+
+ return lane;
+ }
+
+ addLaneInstance ( newLane: TvLane, sort: boolean ): void {
+
+ if ( this.laneMap.has( newLane.id ) ) {
+
+ const lanes = [ ...this.laneMap.entries() ];
+
+ this.laneMap.clear();
+
+ for ( let i = 0; i < lanes.length; i++ ) {
+
+ const lane = lanes[ i ][ 1 ];
+
+ // shift left lanes
+ if ( lane.id >= newLane.id && newLane.id > 0 ) lane.setId( lane.id + 1 );
+
+ // shift right lanes
+ if ( lane.id <= newLane.id && newLane.id < 0 ) lane.setId( lane.id - 1 );
+
+ this.laneMap.set( lane.id, lane );
+
+ }
+
+ }
+
+ this.laneMap.set( newLane.id, newLane );
+
+ this.lastAddedLaneIndex = newLane.id;
+
+ if ( sort ) {
+
+ const inDescOrder = ( a, b ) => a[ 1 ].id > b[ 1 ].id ? -1 : 1;
+
+ this.laneMap = new Map( [ ...this.laneMap.entries() ].sort( inDescOrder ) );
+
+ }
+ }
+
+ removeLaneById ( laneId: number ) {
+
+ this.laneMap.delete( laneId );
+
+ const inDescOrder = ( a, b ) => a[ 0 ] > b[ 0 ] ? -1 : 1;
+
+ this.laneMap = new Map( [ ...this.laneMap.entries() ].sort( inDescOrder ) );
+
+ }
+
+ updateLaneWidthValues ( lane: TvLane ): void {
+
+ const widthSections = lane.getLaneWidthVector();
+
+ for ( let i = 0; i < widthSections.length; i++ ) {
+
+ const current = widthSections[ i ];
+
+ let pp0, pp1, pd0, pd1, length;
+
+ if ( ( i + 1 ) < widthSections.length ) {
+
+ const next = widthSections[ i + 1 ];
+
+ // next s cannot be less than current so we need to clamp it
+ if ( next.s <= current.s ) {
+
+ next.s = current.s + 0.1;
+
+ }
+
+ length = next.s - current.s;
+
+ pp0 = current.a; // width at start
+ pp1 = next.a; // width at end
+ pd0 = current.b; // tangent at start
+ pd1 = next.b; // tangent at end
+
+ } else {
+
+ // take lane section length
+ length = this.length - current.s;
+
+ pp0 = current.a; // width at start
+ pp1 = current.a; // width at end
+ pd0 = current.b; // tangent at start
+ pd1 = current.b; // tangent at end
+
+ }
+
+ let a = pp0;
+ let b = pd0;
+ let c = ( -3 * pp0 ) + ( 3 * pp1 ) + ( -2 * pd0 ) + ( -1 * pd1 );
+ let d = ( 2 * pp0 ) + ( -2 * pp1 ) + ( 1 * pd0 ) + ( 1 * pd1 );
+
+ b /= length;
+ c /= length * length;
+ d /= length * length * length;
+
+ current.a = a;
+ current.b = b;
+ current.c = c;
+ current.d = d;
+
+ }
+
+ }
+
+ cloneAtS ( id?: number, s?: number, side?: boolean, roadId?: number ): TvLaneSection {
+
+ const laneSection = new TvLaneSection( id || 0, 0, side || this.attr_singleSide, roadId || 0 );
+
+ this.laneMap.forEach( lane => {
+
+ laneSection.laneMap.set( lane.id, lane.cloneAtS( lane.id, s || 0 ) );
+
+ } );
+
+ return laneSection;
+ }
+}
diff --git a/src/app/modules/tv-map/models/tv-lane-speed.ts b/src/app/modules/tv-map/models/tv-lane-speed.ts
new file mode 100644
index 00000000..76c2f6c2
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-lane-speed.ts
@@ -0,0 +1,39 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+export class TvLaneSpeed {
+ public attr_sOffset;
+ public attr_max;
+ public attr_unit;
+
+ constructor ( sOffset: number, max: number, unit: string ) {
+ this.attr_sOffset = sOffset;
+ this.attr_max = max;
+ this.attr_unit = unit;
+ }
+
+ get sOffset () {
+ return this.attr_sOffset;
+ }
+
+ set sOffset ( value ) {
+ this.attr_sOffset = value;
+ }
+
+ get max () {
+ return this.attr_max;
+ }
+
+ set max ( value ) {
+ this.attr_max = value;
+ }
+
+ get unit () {
+ return this.attr_unit;
+ }
+
+ set unit ( value ) {
+ this.attr_unit = value;
+ }
+}
\ No newline at end of file
diff --git a/src/app/modules/tv-map/models/tv-lane-visibility.ts b/src/app/modules/tv-map/models/tv-lane-visibility.ts
new file mode 100644
index 00000000..80e61da8
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-lane-visibility.ts
@@ -0,0 +1,60 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+export class TvLaneVisibility {
+ public attr_sOffset;
+ public attr_forward;
+ public attr_back;
+ public attr_left;
+ public attr_right;
+
+ constructor ( sOffset: number, forward: number, back: number, left: number, right: number ) {
+ this.attr_sOffset = sOffset;
+ this.attr_forward = forward;
+ this.attr_back = back;
+ this.attr_left = left;
+ this.attr_right = right;
+ }
+
+ get sOffset () {
+ return this.attr_sOffset;
+ }
+
+ set sOffset ( value ) {
+ this.attr_sOffset = value;
+ }
+
+ get forward () {
+ return this.attr_forward;
+ }
+
+ set forward ( value ) {
+ this.attr_forward = value;
+ }
+
+ get back () {
+ return this.attr_back;
+ }
+
+ set back ( value ) {
+ this.attr_back = value;
+ }
+
+ get left () {
+ return this.attr_left;
+ }
+
+ set left ( value ) {
+ this.attr_left = value;
+ }
+
+ get right () {
+ return this.attr_right;
+ }
+
+ set right ( value ) {
+ this.attr_right = value;
+ }
+
+}
\ No newline at end of file
diff --git a/src/app/modules/tv-map/models/tv-lane-width.ts b/src/app/modules/tv-map/models/tv-lane-width.ts
new file mode 100644
index 00000000..99b7609f
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-lane-width.ts
@@ -0,0 +1,45 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { ThirdOrderPolynom } from './third-order-polynom';
+import { Math, Object3D } from 'three';
+
+export class TvLaneWidth extends ThirdOrderPolynom {
+
+ public mesh?: Object3D;
+
+ private _laneId: number;
+ private _roadId: number;
+
+ constructor ( s: number, a: number, b: number, c: number, d: number, laneId?: number, roadId?: number ) {
+
+ super( s, a, b, c, d );
+
+ this._laneId = laneId;
+ this._roadId = roadId;
+
+ }
+
+ get laneId () {
+ return this._laneId;
+ }
+
+ set laneId ( value ) {
+ this._laneId = value;
+ }
+
+ get roadId () {
+ return this._roadId;
+ }
+
+ set roadId ( value ) {
+ this._roadId = value;
+ }
+
+ clone ( s?: number ) {
+
+ return new TvLaneWidth( s || this.s, this.a, this.b, this.c, this.d, this._laneId, this._roadId );
+
+ }
+}
diff --git a/src/app/modules/tv-map/models/tv-lane.ts b/src/app/modules/tv-map/models/tv-lane.ts
new file mode 100644
index 00000000..3dd8eb09
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-lane.ts
@@ -0,0 +1,1220 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TravelDirection, TvColors, TvLaneSide, TvLaneType, TvRoadMarkTypes, TvRoadMarkWeights } from './tv-common';
+import { TvLaneRoadMark } from './tv-lane-road-mark';
+import { TvRoadLaneSectionLaneLink } from './tv-road-lane-section-lane-link';
+import { TvLaneWidth } from './tv-lane-width';
+import { TvLaneBorder } from './tv-lane-border';
+import { TvLaneMaterial } from './tv-lane-material';
+import { TvLaneVisibility } from './tv-lane-visibility';
+import { TvLaneSpeed } from './tv-lane-speed';
+import { TvLaneAccess } from './tv-lane-access';
+import { TvLaneHeight } from './tv-lane-height';
+import { GameObject } from 'app/core/game-object';
+import { MeshGeometryData } from './mesh-geometry.data';
+import { TvUtils } from './tv-utils';
+import { Math } from 'three';
+
+export class TvLane {
+
+ public readonly uuid: string;
+
+ public gameObject: GameObject;
+ public meshData: MeshGeometryData;
+ public markMeshData: MeshGeometryData;
+ public attr_id: number;
+ public attr_type: TvLaneType;
+ public attr_level;
+ public link: TvRoadLaneSectionLaneLink;
+ public width: TvLaneWidth[] = [];
+ public border: TvLaneBorder[] = [];
+ public roadMark: TvLaneRoadMark[] = [];
+ public material: TvLaneMaterial[] = [];
+ public visibility: TvLaneVisibility[] = [];
+ public speed: TvLaneSpeed[] = [];
+ public access: TvLaneAccess[] = [];
+ public height: TvLaneHeight[] = [];
+
+ private _roadId: number;
+ private _sectionId: number;
+
+ private _side: TvLaneSide;
+ private _predecessorExists: boolean;
+ private _successorExists: boolean;
+ private _predecessor: number;
+ private _successor: number;
+
+ private lastAddedLaneWidth: number;
+ private lastAddedLaneRoadMark: number;
+ private lastAddedLaneMaterial: number;
+ private lastAddedLaneVisibility: number;
+ private lastAddedLaneSpeed: number;
+ private lastAddedLaneAccess: number;
+ private lastAddedLaneHeight: number;
+
+
+ constructor ( laneSide: TvLaneSide, id: number, type: TvLaneType, level: boolean, roadId?: number, sectionId?: number ) {
+
+ this._side = laneSide;
+
+ this.uuid = Math.generateUUID();
+ this.attr_id = id;
+ this.attr_type = type;
+ this.attr_level = level;
+ this.roadId = roadId;
+ this._sectionId = sectionId;
+ }
+
+ get roadId () {
+ return this._roadId;
+ }
+
+ set roadId ( value ) {
+ this._roadId = value;
+ }
+
+ get laneSectionId () {
+ return this._sectionId;
+ }
+
+ set laneSectionId ( value ) {
+ this._sectionId = value;
+ }
+
+ get direction () {
+
+ if ( this.side === TvLaneSide.LEFT ) {
+
+ return TravelDirection.backward;
+
+ } else if ( this.side === TvLaneSide.RIGHT ) {
+
+ return TravelDirection.forward;
+
+ } else if ( this.side === TvLaneSide.CENTER ) {
+
+ return TravelDirection.undirected;
+
+ } else {
+
+ return TravelDirection.undirected;
+
+ }
+
+ }
+
+ // updateMeshGeometry (): any {
+
+ // let posTheta = new OdPosTheta;
+ // let road = OdEditorComponent.openDrive.getRoadById( this.roadId );
+ // let laneSection = road.getLaneSection( 0 );
+ // let cumulativeWidth = 0;
+
+ // this.meshData = null;
+ // this.meshData = new MeshGeometryData;
+
+ // for ( let sCoordinate = laneSection.s; sCoordinate < laneSection.lastSCoordinate; sCoordinate += OdBuilderConfig.ROAD_STEP ) {
+
+ // cumulativeWidth = laneSection.getCumulativeWidth( this, sCoordinate );
+
+ // road.getGeometryCoords( sCoordinate, posTheta );
+
+ // this.makeLaneVertices( sCoordinate, posTheta, lane, road, cumulativeWidth );
+
+ // }
+
+ // cumulativeWidth = laneSection.getCumulativeWidth( lane, laneSection.lastSCoordinate );
+
+ // this.makeLaneVertices( laneSection.lastSCoordinate - Maths.Epsilon, posTheta, lane, road, 0 );
+
+ // var geometry = new BufferGeometry();
+
+ // const vertices = new Float32Array( this.meshData.vertices );
+ // const colors = new Float32Array( this.meshData.colors );
+
+ // this.createMeshIndices( this.meshData );
+
+ // geometry.setIndex( this.meshData.triangles );
+
+ // geometry.addAttribute( 'position', new BufferAttribute( vertices, 3 ) );
+ // geometry.addAttribute( 'color', new BufferAttribute( colors, 3 ) );
+
+ // var material = new MeshBasicMaterial( { color: OdColorFactory.getLaneColor( lane ), transparent: true, opacity: 1 } );
+
+ // this.gameObject = new GameObject( "Lane:" + this.id, geometry, material );
+ // this.gameObject.Tag = OpenDriveObjectType[OpenDriveObjectType.LANE];
+ // this.gameObject.OpenDriveType = OpenDriveObjectType.LANE;
+ // this.gameObject.userData.data = lane;
+
+ // laneSection.gameObject.add( this.gameObject );
+
+ // }
+
+ // private mwidth; mheight; elevation; cosHdgPlusPiO2; sinHdgPlusPiO2;
+
+ // private makeLaneVertices ( sCoordinate: number, pos: OdPosTheta, lane: OdLane, road: OdRoad, cumulativeWidth: number ) {
+
+ // this.mwidth = lane.getWidthValue( sCoordinate );
+ // this.mheight = lane.getHeightValue( sCoordinate );
+ // this.elevation = road.getElevationValue( sCoordinate );
+
+ // this.cosHdgPlusPiO2 = Maths.cosHdgPlusPiO2( lane.side, pos.hdg );
+ // this.sinHdgPlusPiO2 = Maths.sinHdgPlusPiO2( lane.side, pos.hdg );
+
+ // var v1 = new Vertex();
+ // var p1X = this.cosHdgPlusPiO2 * cumulativeWidth;
+ // var p1Y = this.sinHdgPlusPiO2 * cumulativeWidth;
+ // v1.Position = new Vector3( pos.x + p1X, pos.y + p1Y, this.elevation );
+ // v1.TexCoord = new Vector2( 0, sCoordinate );
+
+ // var v2 = new Vertex();
+ // var p2X = cosHdgPlusPiO2 * ( cumulativeWidth + width );
+ // var p2Y = sinHdgPlusPiO2 * ( cumulativeWidth + width );
+ // v2.Position = new Vector3( pos.x + p2X, pos.y + p2Y, elevation + height.getOuter() );
+ // v2.TexCoord = new Vector2( width + height.getOuter(), sCoordinate )
+
+ // if ( lane.side == LaneSide.RIGHT ) {
+ // this.addVertex( lane.meshData, v1 );
+ // this.addVertex( lane.meshData, v2 );
+ // } else {
+ // this.addVertex( lane.meshData, v2 );
+ // this.addVertex( lane.meshData, v1 );
+ // }
+
+ // // Debug.log( v1.Position, v2.Position );
+ // }
+
+ // addVertex ( meshData: MeshGeometryData, v1: Vertex ) {
+
+ // meshData.vertices.push( v1.Position.x, v1.Position.y, v1.Position.z );
+ // meshData.normals.push( v1.Normal.x, v1.Normal.y, v1.Normal.z );
+ // meshData.texCoords.push( v1.TexCoord.x, v1.TexCoord.y );
+ // meshData.colors.push( 0, 1, 0 );
+ // meshData.indices.push( meshData.currentIndex++ );
+
+ // }
+
+ get predecessorExists (): boolean {
+ return this._predecessorExists;
+ }
+
+ set predecessorExists ( value: boolean ) {
+ this._predecessorExists = value;
+ }
+
+ get successorExists (): boolean {
+ return this._successorExists;
+ }
+
+ set successorExists ( value: boolean ) {
+ this._successorExists = value;
+ }
+
+ get side (): TvLaneSide {
+ return this._side;
+ }
+
+ get sideAsString (): string {
+
+ switch ( this.side ) {
+
+ case TvLaneSide.LEFT:
+ return 'left'
+ break;
+
+ case TvLaneSide.CENTER:
+ return 'center'
+ break;
+
+ case TvLaneSide.RIGHT:
+ return 'right'
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ set side ( value ) {
+
+ const som = '' + value;
+ const val = parseFloat( som );
+
+ if ( val === 0 ) {
+ this._side = TvLaneSide.LEFT;
+ } else if ( val === 1 ) {
+ this._side = TvLaneSide.CENTER;
+ } else if ( val === 2 ) {
+ this._side = TvLaneSide.RIGHT;
+ }
+ }
+
+ get id (): number {
+ return Number( this.attr_id );
+ }
+
+ get type (): TvLaneType {
+ return this.attr_type;
+ }
+
+ set type ( value: TvLaneType ) {
+ this.attr_type = value;
+ }
+
+ get level (): boolean {
+ return this.attr_level;
+ }
+
+ get predecessor () {
+ return this._predecessor;
+ }
+
+ set predecessor ( laneId: number ) {
+ this.setPredecessor( laneId );
+ }
+
+
+ //
+ // Methods used to add child records to the respective lane records
+ //
+
+ get succcessor () {
+ return this._successor;
+ }
+
+ set succcessor ( laneId: number ) {
+ this.setPredecessor( laneId );
+ }
+
+ setSide ( side: TvLaneSide ) {
+ this._side = side;
+ }
+
+ setId ( id: number ) {
+ this.attr_id = id;
+ }
+
+ setType ( type: TvLaneType ) {
+ this.attr_type = type;
+ }
+
+ setLevel ( level: boolean ) {
+ this.attr_level = level;
+ }
+
+ setPredecessor ( laneId: number ) {
+
+ this._predecessor = laneId;
+ this._predecessorExists = true;
+
+ }
+
+ //
+ // CLONE METHODS
+ //
+
+ setSuccessor ( laneId: number ) {
+
+ this._successor = laneId;
+ this._successorExists = true;
+
+ }
+
+ removePredecessor () {
+
+ this._predecessor = null;
+ this._predecessorExists = false;
+
+ }
+
+ removeSuccessor () {
+
+ this._successor = null;
+ this._successorExists = false;
+
+ }
+
+ addWidthRecord ( s: number, a: number, b: number, c: number, d: number ) {
+
+ return this.addWidthRecordInstance( new TvLaneWidth( s, a, b, c, d, this.id, this._roadId ) );
+
+ }
+
+ addRoadMarkRecord ( sOffset: number, type: TvRoadMarkTypes, weight: TvRoadMarkWeights, color: TvColors, width: number, laneChange: string, height: number ) {
+
+ return this.addRoadMarkInstance( new TvLaneRoadMark( sOffset, type, weight, color, width, laneChange, height, this ) );
+
+ }
+
+ addMaterialRecord ( sOffset: number, surface: string, friction: number, roughness: number ) {
+
+ const index = this.checkLaneMaterialInterval( sOffset ) + 1;
+
+ if ( index > this.getLaneMaterialCount() ) {
+
+ this.material.push( new TvLaneMaterial( sOffset, surface, friction, roughness ) );
+
+ } else {
+
+ this.material[ index ] = ( new TvLaneMaterial( sOffset, surface, friction, roughness ) );
+
+ }
+
+ this.lastAddedLaneMaterial = index;
+
+ return index;
+ }
+
+ addVisibilityRecord ( sOffset: number, forward: number, back: number, left: number, right: number ) {
+
+ const index = this.checkLaneVisibilityInterval( sOffset ) + 1;
+
+ if ( index > this.getLaneVisibilityCount() ) {
+
+ this.visibility.push( new TvLaneVisibility( sOffset, forward, back, left, right ) );
+
+ } else {
+
+ this.visibility[ index ] = new TvLaneVisibility( sOffset, forward, back, left, right );
+
+ }
+
+ this.lastAddedLaneVisibility = index;
+
+ return index;
+
+ }
+
+ //
+ // DELETE METHODS
+ //
+
+ addSpeedRecord ( sOffset: number, max: number, unit: string ) {
+
+ const index = this.checkLaneSpeedInterval( sOffset ) + 1;
+
+ if ( index > this.getLaneSpeedCount() ) {
+
+ this.speed.push( new TvLaneSpeed( sOffset, max, unit ) );
+
+ } else {
+
+ this.speed[ index ] = new TvLaneSpeed( sOffset, max, unit );
+
+ }
+
+ this.lastAddedLaneSpeed = index;
+
+ return index;
+ }
+
+ addAccessRecord ( sOffset: number, restriction: string ) {
+
+ const index = this.checkLaneAccessInterval( sOffset ) + 1;
+
+ if ( index > this.getLaneAccessCount() ) {
+
+ this.access.push( new TvLaneAccess( sOffset, restriction ) );
+
+ } else {
+
+ this.access[ index ] = new TvLaneAccess( sOffset, restriction );
+
+ }
+
+ this.lastAddedLaneAccess = index;
+
+ return index;
+ }
+
+ addHeightRecord ( sOffset: number, inner: number, outer: number ) {
+
+ const index = this.checkLaneHeightInterval( sOffset ) + 1;
+
+ if ( index > this.getLaneHeightCount() ) {
+
+ this.height.push( new TvLaneHeight( sOffset, inner, outer ) );
+
+ } else {
+
+ this.height[ index ] = new TvLaneHeight( sOffset, inner, outer );
+
+ }
+
+ this.lastAddedLaneHeight = index;
+
+ return index;
+ }
+
+ cloneLaneWidth ( index: number ) {
+
+ if ( index < this.width.length - 1 ) {
+
+ this.width[ index + 1 ] = this.width[ index ];
+
+ } else if ( index === this.width.length - 1 ) {
+
+ this.width.push( this.width[ index ] );
+
+ }
+
+ this.lastAddedLaneWidth = index + 1;
+
+ return this.lastAddedLaneWidth;
+ }
+
+ cloneLaneRoadMark ( index: number ) {
+
+ if ( index < this.roadMark.length - 1 ) {
+
+ this.roadMark[ index + 1 ] = ( this.roadMark[ index ] );
+
+ } else if ( index === this.roadMark.length - 1 ) {
+
+ this.roadMark.push( this.roadMark[ index ] );
+
+ }
+
+ this.lastAddedLaneRoadMark = index + 1;
+
+ return this.lastAddedLaneRoadMark;
+ }
+
+ cloneLaneMaterial ( index: number ) {
+
+ if ( index < this.material.length - 1 ) {
+
+ this.material[ index + 1 ] = ( this.material[ index ] );
+
+ } else if ( index === this.material.length - 1 ) {
+
+ this.material.push( this.material[ index ] );
+
+ }
+
+ this.lastAddedLaneMaterial = index + 1;
+
+ return this.lastAddedLaneMaterial;
+ }
+
+ cloneLaneVisibility ( index: number ) {
+
+ if ( index < this.visibility.length - 1 ) {
+
+ this.visibility[ index + 1 ] = ( this.visibility[ index ] );
+
+ } else if ( index === this.visibility.length - 1 ) {
+
+ this.visibility.push( this.visibility[ index ] );
+
+ }
+
+ this.lastAddedLaneVisibility = index + 1;
+
+ return this.lastAddedLaneVisibility;
+ }
+
+ cloneLaneSpeed ( index: number ) {
+
+ if ( index < this.speed.length - 1 ) {
+
+ this.speed[ index + 1 ] = ( this.speed[ index ] );
+
+ } else if ( index === this.speed.length - 1 ) {
+
+ this.speed.push( this.speed[ index ] );
+
+ }
+
+ this.lastAddedLaneSpeed = index + 1;
+
+ return this.lastAddedLaneSpeed;
+ }
+
+ cloneLaneAccess ( index: number ) {
+
+ if ( index < this.access.length - 1 ) {
+
+ this.access[ index + 1 ] = ( this.access[ index ] );
+
+ } else if ( index === this.access.length - 1 ) {
+
+ this.access.push( this.access[ index ] );
+
+ }
+
+ this.lastAddedLaneAccess = index + 1;
+
+ return this.lastAddedLaneAccess;
+ }
+
+ cloneLaneHeight ( index: number ) {
+
+ if ( index < this.height.length - 1 ) {
+
+ this.height[ index + 1 ] = ( this.height[ index ] );
+
+ } else if ( index === this.height.length - 1 ) {
+
+ this.height.push( this.height[ index ] );
+
+ }
+
+ this.lastAddedLaneHeight = index + 1;
+
+ return this.lastAddedLaneHeight;
+ }
+
+ deleteLaneWidth ( index: number ) {
+ this.width.splice( index, 1 );
+ }
+
+ deleteLaneRoadMark ( index: number ) {
+ this.roadMark.splice( index, 1 );
+ }
+
+ deleteLaneMaterial ( index: number ) {
+ this.material.splice( index, 1 );
+ }
+
+ deleteLaneVisibility ( index: number ) {
+ this.visibility.splice( index, 1 );
+ }
+
+ deleteLaneSpeed ( index: number ) {
+ this.speed.splice( index, 1 );
+ }
+
+ deleteLaneAccess ( index: number ) {
+ this.access.splice( index, 1 );
+ }
+
+ deleteLaneHeight ( index: number ) {
+ this.height.splice( index, 1 );
+ }
+
+ getSide (): TvLaneSide {
+ return this._side;
+ }
+
+ getId (): number {
+ return Number( this.attr_id );
+ }
+
+ getType (): string {
+ return this.attr_type;
+ }
+
+ getLevel (): boolean {
+ return this.attr_level;
+ }
+
+ isPredecessorSet () {
+ return this._predecessorExists;
+ }
+
+ getPredecessor () {
+ return this._predecessor;
+ }
+
+ isSuccessorSet () {
+ return this._successorExists;
+ }
+
+ getSuccessor () {
+ return this._successor;
+ }
+
+ //
+ // GET POINTER TO RECORDS
+
+ //
+ getLaneWidthVector (): TvLaneWidth[] {
+ return this.width;
+ }
+
+ getLaneRoadMarkVector (): TvLaneRoadMark[] {
+ return this.roadMark;
+ }
+
+ getLaneMaterialVector (): TvLaneMaterial[] {
+ return this.material;
+ }
+
+ getLaneVisibilityVector (): TvLaneVisibility[] {
+ return this.visibility;
+ }
+
+ getLaneSpeedVector (): TvLaneSpeed[] {
+ return this.speed;
+ }
+
+ getLaneAccessVector (): TvLaneAccess[] {
+ return this.access;
+ }
+
+ getLaneHeightVector (): TvLaneHeight[] {
+ return this.height;
+ }
+
+
+ //
+ // GET ELEMENT AT INDEX
+ //
+
+ getLaneWidth ( index ): TvLaneWidth {
+
+ if ( this.width.length > 0 && index < this.width.length ) {
+ return this.width[ index ];
+ }
+
+ return null;
+ }
+
+ getLaneRoadMark ( index ): TvLaneRoadMark {
+
+ if ( this.roadMark.length > 0 && index < this.roadMark.length ) {
+ return this.roadMark[ index ];
+ }
+
+ return null;
+ }
+
+ getLaneMaterial ( index ): TvLaneMaterial {
+
+ if ( this.material.length > 0 && index < this.material.length ) {
+ return this.material[ index ];
+ }
+
+ return null;
+ }
+
+ getLaneVisibility ( index ): TvLaneVisibility {
+
+ if ( this.visibility.length > 0 && index < this.visibility.length ) {
+ return this.visibility[ index ];
+ }
+
+ return null;
+ }
+
+ getLaneSpeed ( index ): TvLaneSpeed {
+
+ if ( this.speed.length > 0 && index < this.speed.length ) {
+ return this.speed[ index ];
+ }
+
+ return null;
+ }
+
+ getLaneAccess ( index ): TvLaneAccess {
+
+ if ( this.access.length > 0 && index < this.access.length ) {
+ return this.access[ index ];
+ }
+
+ return null;
+ }
+
+ getLaneHeight ( index ): TvLaneHeight {
+
+ if ( this.height.length > 0 && index < this.height.length ) {
+ return this.height[ index ];
+ }
+
+ return null;
+ }
+
+ //
+ // GET COUNT OF ELEMENTS
+ //
+
+ getLaneWidthCount (): number {
+ return this.width.length;
+ }
+
+ getLaneRoadMarkCount (): number {
+ return this.roadMark.length;
+ }
+
+ getLaneMaterialCount (): number {
+ return this.material.length;
+ }
+
+ getLaneVisibilityCount (): number {
+ return this.visibility.length;
+ }
+
+ getLaneSpeedCount (): number {
+ return this.speed.length;
+ }
+
+ getLaneAccessCount (): number {
+ return this.access.length;
+ }
+
+ getLaneHeightCount (): number {
+ return this.height.length;
+ }
+
+ //
+ // GET LAST ELEMENT
+ //
+
+ getLastLaneWidth () {
+
+ if ( this.width.length > 0 ) {
+ return this.width[ this.width.length - 1 ];
+ }
+
+ return null;
+ }
+
+ getLastLaneRoadMark () {
+
+ if ( this.roadMark.length > 0 ) {
+ return this.roadMark[ this.roadMark.length - 1 ];
+ }
+
+ return null;
+ }
+
+ getLastLaneMaterial () {
+
+ if ( this.material.length > 0 ) {
+ return this.material[ this.material.length - 1 ];
+ }
+
+ return null;
+ }
+
+ getLastLaneVisibility () {
+
+ if ( this.visibility.length > 0 ) {
+ return this.visibility[ this.visibility.length - 1 ];
+ }
+
+ return null;
+ }
+
+ getLastLaneSpeed () {
+
+ if ( this.speed.length > 0 ) {
+ return this.speed[ this.speed.length - 1 ];
+ }
+
+ return null;
+ }
+
+ getLastLaneAccess () {
+
+ if ( this.access.length > 0 ) {
+ return this.access[ this.access.length - 1 ];
+ }
+
+ return null;
+ }
+
+ getLastLaneHeight () {
+
+ if ( this.height.length > 0 ) {
+ return this.height[ this.height.length - 1 ];
+ }
+
+ return null;
+ }
+
+ /**
+ * Get the last added elements of a certain vectors
+ * (their position might not be at the end of the vector)
+ */
+
+ getLastAddedLaneWidth () {
+ if ( this.lastAddedLaneWidth < this.width.length ) {
+ return this.width[ this.lastAddedLaneWidth ];
+ }
+ return null;
+ }
+
+ getLastAddedLaneRoadMark () {
+ if ( this.lastAddedLaneRoadMark < this.roadMark.length ) {
+ return this.roadMark[ this.lastAddedLaneRoadMark ];
+ }
+ return null;
+ }
+
+ getLastAddedLaneMaterial () {
+ if ( this.lastAddedLaneMaterial < this.material.length ) {
+ return this.material[ this.lastAddedLaneMaterial ];
+ }
+ return null;
+ }
+
+ getLastAddedLaneVisibility () {
+ if ( this.lastAddedLaneVisibility < this.visibility.length ) {
+ return this.visibility[ this.lastAddedLaneVisibility ];
+ }
+ return null;
+ }
+
+ getLastAddedLaneSpeed () {
+ if ( this.lastAddedLaneSpeed < this.speed.length ) {
+ return this.speed[ this.lastAddedLaneSpeed ];
+ }
+ return null;
+ }
+
+ getLastAddedLaneAccess () {
+ if ( this.lastAddedLaneAccess < this.access.length ) {
+ return this.access[ this.lastAddedLaneAccess ];
+ }
+ return null;
+ }
+
+ getLastAddedLaneHeight () {
+ if ( this.lastAddedLaneHeight < this.height.length ) {
+ return this.height[ this.lastAddedLaneHeight ];
+ }
+ return null;
+ }
+
+ /**
+ * Check the intervals and return the index of the records that applies to the provided s-offset
+ */
+
+ getLaneWidthIndex ( sCheck: number ): number {
+
+ let result = null;
+
+ for ( let i = 0; i < this.width.length; i++ ) {
+
+ if ( sCheck >= this.width[ i ].s ) result = i;
+
+ }
+
+ return result;
+ }
+
+ checkLaneRoadMarkInterval ( sCheck: number ): number {
+
+ let res = -1;
+
+ for ( let i = 0; i < this.roadMark.length; i++ ) {
+
+ if ( sCheck >= this.roadMark[ i ].sOffset ) {
+
+ res = i;
+
+ } else {
+
+ break;
+
+ }
+
+ }
+
+ return res;
+ }
+
+ checkLaneMaterialInterval ( sCheck: number ): number {
+
+ let res = -1;
+
+ for ( let i = 0; i < this.material.length; i++ ) {
+
+ if ( sCheck >= this.material[ i ].sOffset ) {
+
+ res = i;
+
+ } else {
+
+ break;
+
+ }
+
+ }
+
+ return res;
+ }
+
+ checkLaneVisibilityInterval ( sCheck: number ): number {
+
+ let res = -1;
+
+ for ( let i = 0; i < this.visibility.length; i++ ) {
+
+ if ( sCheck >= this.visibility[ i ].sOffset ) {
+
+ res = i;
+
+ } else {
+
+ break;
+
+ }
+
+ }
+
+ return res;
+ }
+
+ checkLaneSpeedInterval ( sCheck: number ): number {
+
+ let res = -1;
+
+ for ( let i = 0; i < this.speed.length; i++ ) {
+
+ if ( sCheck >= this.speed[ i ].sOffset ) {
+
+ res = i;
+
+ } else {
+
+ break;
+
+ }
+
+ }
+
+ return res;
+ }
+
+ checkLaneAccessInterval ( sCheck: number ): number {
+
+ let res = -1;
+
+ for ( let i = 0; i < this.access.length; i++ ) {
+
+ if ( sCheck >= this.access[ i ].sOffset ) {
+
+ res = i;
+
+ } else {
+
+ break;
+
+ }
+
+ }
+
+ return res;
+ }
+
+ checkLaneHeightInterval ( s_value: number ): number {
+
+ let res = -1;
+
+ for ( let i = 0; i < this.height.length; i++ ) {
+
+ if ( s_value >= this.height[ i ].sOffset ) {
+
+ res = i;
+
+ } else {
+
+ break;
+
+ }
+
+ }
+
+ return res;
+ }
+
+ /**
+ * Evaluate the record and the return the width value
+ * @param sCheck
+ */
+ getWidthValue ( sCheck ): number {
+
+ const widthEntry = this.getLaneWidthAt( sCheck );
+
+ if ( widthEntry == null ) return 0;
+
+ return widthEntry.getValue( sCheck );
+ }
+
+ /**
+ * Evaluate the record and return the height object
+ * @param sCheck
+ */
+ getHeightValue ( sCheck ): TvLaneHeight {
+
+ const laneHeight = new TvLaneHeight( 0, 0, 0 );
+
+ const index = this.checkLaneHeightInterval( sCheck );
+
+ if ( index >= 0 ) {
+
+ const currentHeight = this.getLaneHeight( index );
+
+ laneHeight.setInner( currentHeight.getInner() );
+ laneHeight.setOuter( currentHeight.getOuter() );
+
+ }
+
+ return laneHeight;
+ }
+
+ /**
+ * Evaluate the road marks records and return the road
+ * mark object corresponding to the provided s-offset
+ * @param sCheck
+ */
+ getRoadMark ( sCheck ): TvLaneRoadMark {
+
+ let result = TvUtils.checkIntervalArray( this.roadMark, sCheck );
+
+ if ( result == null ) {
+
+ console.warn( 'roadmark not found using default' );
+
+ result = new TvLaneRoadMark( 0, TvRoadMarkTypes.SOLID, TvRoadMarkWeights.STANDARD, TvColors.WHITE, 0, '', 0, this );
+
+ }
+
+ return result;
+
+ // const laneRoadMark = new OdLaneRoadMark( 0, OdRoadMarkTypes.SOLID, OdRoadMarkWeights.STANDARD, OdColors.WHITE, 0, '', 0 );
+ //
+ // const index = this.checkLaneRoadMarkInterval( sCheck );
+ //
+ // if ( index >= 0 ) {
+ //
+ // const currentRoadMark = this.roadMark[ index ];
+ //
+ // laneRoadMark.setType( currentRoadMark.getType() );
+ // laneRoadMark.setWeight( currentRoadMark.getWeight() );
+ // laneRoadMark.setMaterial( currentRoadMark.getMaterial() );
+ // laneRoadMark.setWidth( currentRoadMark.getWidth() );
+ // laneRoadMark.setHeight( currentRoadMark.getHeight() );
+ // laneRoadMark.setLaneChange( currentRoadMark.getLaneChange() );
+ //
+ // }
+ //
+ // return laneRoadMark;
+ }
+
+ /**
+ * Returns the color for mesh of the lane
+ * @returns {string}
+ */
+ getColor (): string {
+
+ if ( this.attr_type === 'driving' ) {
+
+ return '#aeaeae';
+
+ } else if ( this.attr_type === 'border' ) {
+
+ return '#118400';
+
+ } else if ( this.attr_type === 'stop' ) {
+
+ return '#848484';
+ }
+
+ return '#ff00ab';
+ }
+
+ // clones the entire lane
+ clone ( id?: number ): TvLane {
+
+ const laneId = id || this.id;
+
+ const newLane = new TvLane( this.side, laneId, this.type, this.level, this.roadId, this.laneSectionId );
+
+ this.getLaneWidthVector().forEach( width => {
+ newLane.addWidthRecord( width.s, width.a, width.b, width.c, width.d );
+ } );
+
+ this.getLaneRoadMarkVector().forEach( roadMark => {
+ newLane.addRoadMarkRecord( roadMark.sOffset, roadMark.type, roadMark.weight, roadMark.color, roadMark.width, roadMark.laneChange, roadMark.height );
+ } );
+
+ return newLane;
+ }
+
+ // clones only the lane at s and avoid multiple entries for width, height etc
+ cloneAtS ( id?: number, s?: number ): TvLane {
+
+ const laneId = id || this.id;
+
+ const newLane = new TvLane( this.side, laneId, this.type, this.level, this.roadId, this.laneSectionId );
+
+ const width = this.getLaneWidthAt( s || 0 );
+
+ if ( width ) {
+
+ newLane.addWidthRecord( width.s, width.a, width.b, width.c, width.d );
+
+ }
+
+ const roadMark = this.getRoadMark( s || 0 );
+
+ if ( roadMark ) {
+
+ newLane.addRoadMarkRecord(
+ roadMark.sOffset,
+ roadMark.type,
+ roadMark.weight,
+ roadMark.color,
+ roadMark.width,
+ roadMark.laneChange,
+ roadMark.height
+ );
+
+ }
+
+ return newLane;
+
+ }
+
+ public getLaneWidthAt ( s: number ): TvLaneWidth {
+
+ return TvUtils.checkIntervalArray( this.width, s );
+
+ }
+
+ public getRoadMarkAt ( s: number ): TvLaneRoadMark {
+
+ return TvUtils.checkIntervalArray( this.roadMark, s );
+
+ }
+
+ getRoadMarks () {
+
+ return this.roadMark;
+
+ }
+
+ addDefaultRoadMark () {
+
+ this.addRoadMarkRecord( 0, TvRoadMarkTypes.SOLID, TvRoadMarkWeights.STANDARD, TvColors.WHITE, 0.15, 'none', 0 );
+
+ }
+
+ addRoadMarkInstance ( roadmark: TvLaneRoadMark ) {
+
+ this.roadMark.push( roadmark );
+
+ this.roadMark.sort( ( a, b ) => a.sOffset > b.sOffset ? 1 : -1 );
+
+ // console.log( this.roadMark );
+
+ // this.lastAddedLaneRoadMark = index;
+ //
+ // return index;
+ }
+
+ addWidthRecordInstance ( laneWidth: TvLaneWidth ) {
+
+ this.width.push( laneWidth );
+
+ this.width.sort( ( a, b ) => a.s > b.s ? 1 : -1 );
+
+ }
+}
+
diff --git a/src/app/modules/tv-map/models/tv-lateral-profile-crossfall.ts b/src/app/modules/tv-map/models/tv-lateral-profile-crossfall.ts
new file mode 100644
index 00000000..fbd2c69b
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-lateral-profile-crossfall.ts
@@ -0,0 +1,6 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+export class TvLateralProfileCrossfall {
+}
\ No newline at end of file
diff --git a/src/app/modules/tv-map/models/tv-lateral-profile-shape.ts b/src/app/modules/tv-map/models/tv-lateral-profile-shape.ts
new file mode 100644
index 00000000..df1727c6
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-lateral-profile-shape.ts
@@ -0,0 +1,6 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+export class TvLateralProfileShape {
+}
\ No newline at end of file
diff --git a/src/app/modules/tv-map/models/tv-lateral-profile-super-elevation.ts b/src/app/modules/tv-map/models/tv-lateral-profile-super-elevation.ts
new file mode 100644
index 00000000..0c4b5923
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-lateral-profile-super-elevation.ts
@@ -0,0 +1,6 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+export class TvLateralProfileSuperElevation {
+}
\ No newline at end of file
diff --git a/src/app/modules/tv-map/models/tv-lateral.profile.ts b/src/app/modules/tv-map/models/tv-lateral.profile.ts
new file mode 100644
index 00000000..47aa5740
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-lateral.profile.ts
@@ -0,0 +1,13 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvLateralProfileSuperElevation } from './tv-lateral-profile-super-elevation';
+import { TvLateralProfileCrossfall } from './tv-lateral-profile-crossfall';
+import { TvLateralProfileShape } from './tv-lateral-profile-shape';
+
+export class TvLateralProfile {
+ public superelelevation: TvLateralProfileSuperElevation[] = [];
+ public crossfall: TvLateralProfileCrossfall[] = [];
+ public shape: TvLateralProfileShape[] = [];
+}
\ No newline at end of file
diff --git a/src/app/modules/tv-map/models/tv-map-header.ts b/src/app/modules/tv-map/models/tv-map-header.ts
new file mode 100644
index 00000000..7f1f0646
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-map-header.ts
@@ -0,0 +1,81 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+export class TvMapHeader {
+
+ public attr_revMajor: number;
+ public attr_revMinor: number;
+ public attr_name: string;
+ public attr_version: number;
+ public attr_date: string;
+ public attr_north: number;
+ public attr_south: number;
+ public attr_east: number;
+ public attr_west: number;
+ public attr_vendor: string;
+
+ constructor (
+ revMajor: number,
+ revMinor: number,
+ name: string,
+ version: number,
+ date: string,
+ north: number,
+ south: number,
+ east: number,
+ west: number,
+ vendor: string
+ ) {
+ this.attr_revMajor = revMajor;
+ this.attr_revMinor = revMinor;
+ this.attr_name = name;
+ this.attr_version = version;
+ this.attr_date = date;
+ this.attr_north = north;
+ this.attr_south = south;
+ this.attr_east = east;
+ this.attr_west = west;
+ this.attr_vendor = vendor;
+ }
+
+ get revMajor (): number {
+ return this.attr_revMajor;
+ }
+
+ get revMinor (): number {
+ return this.attr_revMinor;
+ }
+
+ get name (): string {
+ return this.attr_name;
+ }
+
+ get version (): number {
+ return this.attr_version;
+ }
+
+ get date (): string {
+ return this.attr_date;
+ }
+
+ get north (): number {
+ return this.attr_north;
+ }
+
+ get south (): number {
+ return this.attr_south;
+ }
+
+ get east (): number {
+ return this.attr_east;
+ }
+
+ get west (): number {
+ return this.attr_west;
+ }
+
+ get vendor (): string {
+ return this.attr_vendor;
+ }
+}
\ No newline at end of file
diff --git a/src/app/modules/tv-map/models/tv-map.model.spec.ts b/src/app/modules/tv-map/models/tv-map.model.spec.ts
new file mode 100644
index 00000000..2b341bb2
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-map.model.spec.ts
@@ -0,0 +1,118 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvLaneSide, TvLaneType } from './tv-common';
+import { TvLaneSection } from './tv-lane-section';
+import { TvLane } from './tv-lane';
+import { TvMap } from './tv-map.model';
+import { TvRoad } from './tv-road.model';
+import { TvPosTheta } from './tv-pos-theta';
+import { TvMapQueries } from '../queries/tv-map-queries';
+import { TvMapSourceFile } from '../services/tv-map-source-file';
+
+describe( 'OpenDrive Model', () => {
+
+ let openDrive: TvMap;
+ let road: TvRoad;
+ let laneSection: TvLaneSection;
+
+ let leftOne: TvLane;
+ let leftTwo: TvLane;
+ let leftThree: TvLane;
+ let rightOne: TvLane;
+ let rightTwo: TvLane;
+ let rightThree: TvLane;
+ let rightFour: TvLane;
+
+ beforeEach( () => {
+
+ openDrive = new TvMap();
+
+ TvMapSourceFile.openDrive = openDrive;
+
+ road = openDrive.addRoad( '', 10, 1, -1 );
+
+ road.addPlanView();
+
+ laneSection = new TvLaneSection( 1, 0, true, 1 );
+
+ road.getLaneSections().push( laneSection );
+
+ laneSection.addLane( TvLaneSide.LEFT, 2, TvLaneType.driving, true, true );
+ leftTwo = laneSection.getLastAddedLane();
+
+ laneSection.addLane( TvLaneSide.LEFT, 3, TvLaneType.driving, true, true );
+ leftThree = laneSection.getLastAddedLane();
+
+ laneSection.addLane( TvLaneSide.LEFT, 1, TvLaneType.driving, true, true );
+ leftOne = laneSection.getLastAddedLane();
+
+ laneSection.addLane( TvLaneSide.CENTER, 0, TvLaneType.driving, true, true );
+
+ laneSection.addLane( TvLaneSide.RIGHT, -1, TvLaneType.driving, true, true );
+ rightOne = laneSection.getLastAddedLane();
+
+ laneSection.addLane( TvLaneSide.RIGHT, -3, TvLaneType.driving, true, true );
+ rightThree = laneSection.getLastAddedLane();
+
+ laneSection.addLane( TvLaneSide.RIGHT, -2, TvLaneType.driving, true, true );
+ rightTwo = laneSection.getLastAddedLane();
+
+ laneSection.addLane( TvLaneSide.RIGHT, -4, TvLaneType.driving, true, true );
+ rightFour = laneSection.getLastAddedLane();
+
+ laneSection.getLaneVector().forEach( lane => {
+
+ if ( lane.side !== TvLaneSide.CENTER ) {
+
+ lane.addWidthRecord( 0, 2, 0, 0, 0 );
+
+ }
+
+ } );
+
+ } );
+
+ it( 'should give correct road id ', () => {
+
+ road.addGeometryLine( 0, 0, 0, 0, 10 );
+
+ const result = TvMapQueries.getRoadByCoords( 1, 1 );
+
+ expect( result.id ).toBe( 1 );
+
+ } );
+
+ it( 'should give correct lane id for left lane', () => {
+
+ road.addGeometryLine( 0, 0, 0, 0, 10 );
+
+ const posTheta = new TvPosTheta();
+
+ const result = TvMapQueries.getLaneByCoords( 1, 1, posTheta );
+
+ expect( result.road.id ).toBe( 1 );
+ expect( result.lane.id ).toBe( 1 );
+
+ const result2 = TvMapQueries.getLaneByCoords( 1, 3, posTheta );
+
+ expect( result2.road.id ).toBe( 1 );
+ expect( result2.lane.id ).toBe( 2 );
+
+ } );
+
+ it( 'should give correct lane id for right lane', () => {
+
+ road.addGeometryLine( 0, 0, 0, 0, 10 );
+
+ const posTheta = new TvPosTheta();
+
+ const result = TvMapQueries.getLaneByCoords( 1, -1, posTheta );
+
+ expect( result.road.id ).toBe( 1 );
+ expect( result.lane.id ).toBe( -1 );
+
+ } );
+
+} );
diff --git a/src/app/modules/tv-map/models/tv-map.model.ts b/src/app/modules/tv-map/models/tv-map.model.ts
new file mode 100644
index 00000000..58c7a755
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-map.model.ts
@@ -0,0 +1,355 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvJunction } from './tv-junction';
+import { TvRoad } from './tv-road.model';
+import { TvController } from './tv-controller';
+import { GameObject } from 'app/core/game-object';
+import { TvMapHeader } from './tv-map-header';
+import { TvRoadLinkChild } from './tv-road-link-child';
+import { TvJunctionConnection } from './tv-junction-connection';
+import { PropInstance } from 'app/core/models/prop-instance.model';
+import { TvSurface } from './tv-surface.model';
+import { TvLaneSide, TvLaneType, TvRoadType } from './tv-common';
+import { RoadStyleService } from 'app/services/road-style.service';
+import { PropCurve } from './prop-curve';
+import { SceneService } from 'app/core/services/scene.service';
+import { PropPolygon } from './prop-polygons';
+
+export class TvMap {
+
+ public props: PropInstance[] = [];
+ public propCurves: PropCurve[] = [];
+ public propPolygons: PropPolygon[] = [];
+ public surfaces: TvSurface[] = [];
+
+ public gameObject: GameObject = new GameObject( 'OpenDrive' );
+ public header: TvMapHeader = new TvMapHeader( 1, 4, 'Untitled', 1, Date(), 1, 0, 0, 0, 'truevision.ai' );
+
+ private _roads: Map = new Map();
+ private _junctions: Map = new Map();
+ private _controllers: Map = new Map();
+
+
+ get junctions (): Map {
+ return this._junctions;
+ }
+
+ set junctions ( value: Map ) {
+ this._junctions = value;
+ }
+
+ get roads (): Map {
+ return this._roads;
+ }
+
+ set roads ( value: Map ) {
+ this._roads = value;
+ }
+
+ get controllers (): Map {
+ return this._controllers;
+ }
+
+ set controllers ( value: Map ) {
+ this._controllers = value;
+ }
+
+ update () {
+
+
+ }
+
+ public getHeader (): TvMapHeader {
+ return this.header;
+ }
+
+ public addRoad ( name: string, length: number, id: number, junction: number ): TvRoad {
+
+ const index = this.getRoadCount();
+
+ const road = new TvRoad( name, length, id, junction );
+
+ this.addRoadInstance( road );
+
+
+ return road;
+ }
+
+ addDefaultRoadWithType ( type: TvRoadType, maxSpeed = 40 ) {
+
+ const road = this.addDefaultRoad();
+
+ road.setType( type, maxSpeed );
+
+ return road;
+ }
+
+ addDefaultRoad (): TvRoad {
+
+ const road = this.addRoad( `${ this.roads.size + 1 }`, 0, this.roads.size + 1, -1 );
+
+ const roadStyle = RoadStyleService.getRoadStyle( road.id );
+
+ // const laneOffset = road.addLaneOffset( 0, 0, 0, 0, 0 );
+ const laneOffset = road.addLaneOffsetInstance( roadStyle.laneOffset );
+
+ // const laneSection = road.addGetLaneSection( 0 );
+ const laneSection = road.addLaneSectionInstance( roadStyle.laneSection );
+
+ // const leftLane3 = laneSection.addLane( TvLaneSide.LEFT, 3, TvLaneType.sidewalk, true, true );
+ // const leftLane2 = laneSection.addLane( TvLaneSide.LEFT, 2, TvLaneType.shoulder, true, true );
+ // const leftLane1 = laneSection.addLane( TvLaneSide.LEFT, 1, TvLaneType.driving, true, true );
+ // const centerLane = laneSection.addLane( TvLaneSide.CENTER, 0, TvLaneType.driving, true, true );
+ // const rightLane1 = laneSection.addLane( TvLaneSide.RIGHT, -1, TvLaneType.driving, true, true );
+ // const rightLane2 = laneSection.addLane( TvLaneSide.RIGHT, -2, TvLaneType.shoulder, true, true );
+ // const rightLane3 = laneSection.addLane( TvLaneSide.RIGHT, -3, TvLaneType.sidewalk, true, true );
+
+ // leftLane1.addRoadMarkRecord( 0, TvRoadMarkTypes.NONE, TvRoadMarkWeights.STANDARD, TvColors.STANDARD, 0.15, 'none', 0 );
+ // centerLane.addRoadMarkRecord( 0, TvRoadMarkTypes.BROKEN, TvRoadMarkWeights.STANDARD, TvColors.STANDARD, 0.15, 'none', 0 );
+ // rightLane1.addRoadMarkRecord( 0, TvRoadMarkTypes.NONE, TvRoadMarkWeights.STANDARD, TvColors.STANDARD, 0.15, 'none', 0 );
+
+ // laneSection.getLaneVector().forEach( lane => {
+
+ // if ( lane.side !== TvLaneSide.CENTER ) {
+
+ // if ( lane.type == TvLaneType.driving ) lane.addWidthRecord( 0, 3.6, 0, 0, 0 );
+
+ // else if ( lane.type == TvLaneType.sidewalk ) lane.addWidthRecord( 0, 2, 0, 0, 0 );
+
+ // else lane.addWidthRecord( 0, 0.5, 0, 0, 0 );
+
+ // }
+
+ // } );
+
+ return road;
+ }
+
+ addConnectingRoad ( side: TvLaneSide, width: number, junctionId: number ): TvRoad {
+
+ const road = this.addRoad( `${ this.roads.size + 1 }`, 0, this.roads.size + 1, junctionId );
+
+
+ const laneSection = road.addGetLaneSection( 0 );
+
+ if ( side === TvLaneSide.LEFT )
+ laneSection.addLane( TvLaneSide.LEFT, 1, TvLaneType.driving, true, true );
+
+ if ( side === TvLaneSide.RIGHT )
+ laneSection.addLane( TvLaneSide.RIGHT, -1, TvLaneType.driving, true, true );
+
+ laneSection.addLane( TvLaneSide.CENTER, 0, TvLaneType.driving, true, true );
+
+ laneSection.getLaneVector().forEach( lane => {
+
+ if ( lane.side !== TvLaneSide.CENTER ) {
+
+ if ( lane.type === TvLaneType.driving ) lane.addWidthRecord( 0, width, 0, 0, 0 );
+
+ }
+
+ } );
+
+ return road;
+ }
+
+ addConnectingRoadLane () {
+
+ }
+
+
+ addRoadInstance ( road: TvRoad ) {
+
+ this._roads.set( road.id, road );
+
+ }
+
+ public addNewJunction ( junctionName?: string ): TvJunction {
+
+ const id = this.junctions.size + 1;
+
+ const name = junctionName || `${ id }`;
+
+
+ return this.junctions.get( id );
+ }
+
+ public addJunction ( name, id ): number {
+
+ const index = this.getJunctionCount();
+
+ this._junctions.set( id, new TvJunction( name, id ) );
+
+
+ return index;
+ }
+
+ public addController ( id: number, name: string, sequence: number ): TvController {
+
+ const controller = new TvController( id, name, sequence );
+
+ this._controllers.set( id, controller );
+
+ return controller;
+ }
+
+ public getControllerCount (): number {
+
+ return this._controllers.size;
+
+ }
+
+ public getController ( index: number ) {
+
+ return this._controllers.get( index );
+
+ }
+
+ public removeRoad ( road: TvRoad ) {
+
+ road.remove( this.gameObject );
+
+ this.roads.delete( road.id );
+ }
+
+ public deleteRoad ( id: number ): void {
+
+ this.roads.delete( id );
+
+ }
+
+ public deleteJunction ( id ): void {
+
+ this._junctions.delete( id );
+
+ }
+
+ getRoadById ( roadId: number ): TvRoad {
+
+ if ( this._roads.has( roadId ) ) {
+
+ return this._roads.get( roadId );
+
+ } else {
+
+ console.error( `${ roadId } road-id not found` );
+ // throw new Error( 'RoadID not found.' );
+
+ }
+
+ }
+
+ public getRoadCount (): number {
+
+ return this._roads.size;
+
+ }
+
+ public getJunctionById ( id ): TvJunction {
+
+ return this._junctions.get( id );
+
+ }
+
+ public getJunctionCount (): number {
+
+ return this._junctions.size;
+
+ }
+
+ /**
+ * Clears the OpenDrive structure, could be used to start a new document
+ */
+ public clear () {
+
+ this._roads.clear();
+
+ this._junctions.clear();
+
+ this.props.splice( 0, this.props.length );
+
+ this.propCurves.splice( 0, this.propCurves.length );
+
+ this.surfaces.splice( 0, this.surfaces.length );
+ }
+
+ public setHeader (
+ revMajor: number,
+ revMinor: number,
+ name: string,
+ version: number,
+ date: string,
+ north: number,
+ south: number,
+ east: number,
+ west: number,
+ vendor: string
+ ) {
+
+ this.header = new TvMapHeader( revMajor, revMinor, name, version, date, north, south, east, west, vendor );
+
+ }
+
+ addJunctionInstance ( junction: TvJunction ) {
+
+ this._junctions.set( junction.id, junction );
+
+ }
+
+ addControllerInstance ( odController: TvController ) {
+ this.controllers.set( odController.id, odController );
+ }
+
+ destroy () {
+
+ this.roads.forEach( road => road.remove( this.gameObject ) );
+
+ this.surfaces.forEach( surface => this.gameObject.remove( surface.mesh ) );
+
+ this.propCurves.forEach( curve => {
+
+ this.gameObject.remove( curve.spline.mesh );
+
+ curve.props.forEach( prop => SceneService.remove( prop ) );
+
+ } );
+
+ this.propPolygons.forEach( polygon => {
+
+ this.gameObject.remove( polygon.spline.mesh );
+
+ polygon.props.forEach( prop => SceneService.remove( prop ) );
+
+ } );
+
+ this.props.forEach( prop => this.gameObject.remove( prop.object ) );
+
+ this.clear();
+ }
+
+ private getNextRoad ( road: TvRoad, connection: TvJunctionConnection, child: TvRoadLinkChild ) {
+
+ if ( child.elementType == 'road' ) {
+
+ connection = null;
+
+ return this.getRoadById( child.elementId );
+
+ } else if ( child.elementType == 'junction' ) {
+
+ const junction = this.getJunctionById( child.elementId );
+
+ connection = junction.getRandomConnectionFor( road.id );
+
+ return this.getRoadById( connection.connectingRoad );
+
+ } else {
+
+ console.error( 'unknown successor type' );
+
+ }
+
+ }
+}
diff --git a/src/app/modules/tv-map/models/tv-plane-view.ts b/src/app/modules/tv-map/models/tv-plane-view.ts
new file mode 100644
index 00000000..2c04b8eb
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-plane-view.ts
@@ -0,0 +1,168 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvAbstractRoadGeometry } from './geometries/tv-abstract-road-geometry';
+import { TvSpiralGeometry } from './geometries/tv-spiral-geometry';
+import { TvPosTheta } from './tv-pos-theta';
+import { Vector2 } from 'three';
+import { TvLineGeometry } from './geometries/tv-line-geometry';
+import { TvArcGeometry } from './geometries/tv-arc-geometry';
+import { TvPoly3Geometry } from './geometries/tv-poly3-geometry';
+import { TvParamPoly3Geometry } from './geometries/tv-param-poly3-geometry';
+
+export class TvPlaneView {
+
+ // public geometry: ODGeometry[] = [];
+
+ public geometries: TvAbstractRoadGeometry[] = [];
+
+
+ constructor () {
+
+ }
+
+ addGeometry ( geometry: TvAbstractRoadGeometry ) {
+
+ this.geometries.push( geometry );
+
+ }
+
+ addGeometryLine ( s: number, x: number, y: number, hdg: number, length: number ) {
+
+ const geometry = new TvLineGeometry( s, x, y, hdg, length );
+
+ this.geometries.push( geometry );
+
+ return geometry;
+ }
+
+ addGeometrySpiral ( s, x, y, hdg, length, curvStart, curvEnd ) {
+
+ // const geometry = new ODGeometry();
+ // geometry.spiral = new OdSpiralGeometry( s, x, y, hdg, length, curvStart, curvEnd );
+
+ this.geometries.push( new TvSpiralGeometry( s, x, y, hdg, length, curvStart, curvEnd ) );
+ }
+
+ addGeometryArc ( s, x, y, hdg, length, curvature: number ) {
+
+ const geometry = new TvArcGeometry( s, x, y, hdg, length, curvature );
+
+ this.geometries.push( geometry );
+
+ return geometry;
+ }
+
+ addGeometryPoly3 ( s, x, y, hdg, length, a, b, c, d ) {
+
+ this.geometries.push( new TvPoly3Geometry( s, x, y, hdg, length, a, b, c, d ) );
+
+ }
+
+ addGeometryParamPoly3 ( s, x, y, hdg, length, aU, bU, cU, dU, aV, bV, cV, dV ) {
+
+ this.geometries.push( new TvParamPoly3Geometry( s, x, y, hdg, length, aU, bU, cU, dU, aV, bV, cV, dV ) );
+
+ }
+
+ /**
+ * Getter for the overall planView/ road length (sum of all geometry record lengths)
+ */
+ getBlockLength () {
+
+ let total = 0;
+
+ for ( let i = 0; i < this.geometries.length; i++ ) {
+
+ total += this.geometries[ i ].length;
+
+ }
+
+ return total;
+ }
+
+ recalculate ( s, x, y, hdg ) {
+
+
+ for ( let i = 0; i < this.geometries.length; i++ ) {
+
+ // const element = this.geometry[i];
+
+ // TODO:
+
+ }
+ }
+
+
+ getLastS2 () {
+
+ if ( this.geometries.length > 0 ) {
+
+ return this.geometries[ this.geometries.length - 1 ].s2;
+
+ } else {
+
+ return 0;
+
+ }
+ }
+
+ /**
+ * Gets the coordinates and heading at the end of the last geometry
+ * @param sCheck
+ * @param posTheta
+ */
+ getCoords ( sCheck: number, posTheta: TvPosTheta ): number {
+
+ // go through all the geometries
+ for ( let i = 0; i < this.geometries.length; i++ ) {
+
+ const geometry = this.geometries[ i ];
+
+ // if the sCheck belongs to one of the geometries
+ if ( geometry.checkInterval( sCheck ) ) {
+
+ geometry.getCoords( sCheck, posTheta );
+
+ return geometry.geometryType;
+ }
+ }
+ }
+
+ /**
+ * Gets the nearest point on reference line from the given co-ordinates
+ * @param x
+ * @param y
+ */
+ getNearestPointFrom ( x: number, y: number ): Vector2 {
+
+ let curentMinDistance = 100000000;
+ let nearestPoint: Vector2;
+
+ // go through all the geometries
+ for ( let i = 0; i < this.geometries.length; i++ ) {
+
+ const geometry = this.geometries[ i ];
+
+ const point = geometry.getNearestPointFrom( x, y );
+ const distance = this.distance( point, x, y );
+
+ // check if the distance is smaller then curent min distance
+ if ( distance < curentMinDistance ) {
+
+ curentMinDistance = distance;
+
+ nearestPoint = point;
+
+ }
+ }
+
+ return nearestPoint;
+ }
+
+ distance ( pos: Vector2, x, y ): number {
+ return Math.sqrt( ( x - pos.x ) * ( x - pos.x ) + ( y - pos.y ) * ( y - pos.y ) );
+ }
+
+}
diff --git a/src/app/modules/tv-map/models/tv-pos-theta.ts b/src/app/modules/tv-map/models/tv-pos-theta.ts
new file mode 100644
index 00000000..c02e2326
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-pos-theta.ts
@@ -0,0 +1,138 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+// Class representing a position in space plus a direction.
+import { Vector2, Vector3 } from 'three';
+import { Maths } from '../../../utils/maths';
+
+export class TvPosTheta {
+
+ private _x: number;
+ private _s: number;
+ private _t: number;
+ private _y: number;
+ private _hdg: number;
+
+ constructor ( x?: number, y?: number, hdg?: number, s?: number, t?: number ) {
+ this._x = x;
+ this._y = y;
+ this._hdg = hdg;
+ this._s = s;
+ this._t = t;
+ }
+
+ get x () {
+ return this._x;
+ }
+
+ set x ( value ) {
+ this._x = value;
+ }
+
+ get s () {
+ return this._s;
+ }
+
+ set s ( value ) {
+ this._s = value;
+ }
+
+ get t () {
+ return this._t;
+ }
+
+ set t ( value ) {
+ this._t = value;
+ }
+
+ get y () {
+ return this._y;
+ }
+
+ set y ( value ) {
+ this._y = value;
+ }
+
+ get hdg () {
+ return this._hdg;
+ }
+
+ set hdg ( value ) {
+ this._hdg = value;
+ }
+
+ toVector3 (): Vector3 {
+ return new Vector3( this.x, this.y, 0 );
+ }
+
+ toVector2 (): Vector2 {
+ return new Vector2( this.x, this.y );
+ }
+
+ toDirectionVector (): Vector3 {
+
+ const direction = new Vector3();
+
+ direction.x = Math.cos( this.hdg ) * Math.cos( 0 );
+ direction.y = Math.sin( this.hdg ) * Math.cos( 0 );
+ direction.z = 0; // Math.sin( pose.hdg );
+
+ return direction;
+ }
+
+ rotateDegree ( degree: number ) {
+
+ this.hdg = this.hdg + Maths.Deg2Rad * degree;
+
+ return this;
+ }
+
+ rotateRadian ( radians: number ) {
+
+ this.hdg = this.hdg + radians;
+
+ return this;
+ }
+
+ moveForward ( s: number ): TvPosTheta {
+
+ const x = this.x + Math.cos( this.hdg ) * s;
+ const y = this.y + Math.sin( this.hdg ) * s;
+
+ return this.clone( x, y, this.hdg, this.s + s, this.t );
+ }
+
+ // offset means t
+ addLateralOffset ( offset: number ) {
+
+ // // find the end of the chord line
+ // this.x = this.x + Math.cos( this.hdg ) * laneOffset;
+ // this.y = this.y + Math.sin( this.hdg ) * laneOffset;
+
+ // // cosine and sine for the tangent (lateral position in track coords)
+ // double cosHdgPlusPiO2 = -1 * Math.Cos(hdg + OpenDriveXmlParserV2.M_PI_2);
+ // double sinHdgPlusPiO2 = -1 * Math.Sin(hdg + OpenDriveXmlParserV2.M_PI_2);
+ //
+ // x += cosHdgPlusPiO2 * (cumulativeWidth + 0);
+ // y += sinHdgPlusPiO2 * (cumulativeWidth + 0);
+
+ // changed after testing
+ // let cosHdgPlusPiO2 = -1 * Math.cos( this.hdg + Maths.M_PI_2 );
+ // let sinHdgPlusPiO2 = -1 * Math.sin( this.hdg + Maths.M_PI_2 );
+
+ let cosHdgPlusPiO2 = Math.cos( this.hdg + Maths.M_PI_2 );
+ let sinHdgPlusPiO2 = Math.sin( this.hdg + Maths.M_PI_2 );
+
+ this.x += cosHdgPlusPiO2 * ( offset );
+ this.y += sinHdgPlusPiO2 * ( offset );
+
+ return this;
+ }
+
+ clone ( x?: number, y?: number, hdg?: number, s?: number, t?: number ) {
+
+ return new TvPosTheta( x || this.x, y || this.y, hdg || this.hdg, s || this.s, t || this.t );
+
+ }
+}
diff --git a/src/app/modules/tv-map/models/tv-road-lane-offset.ts b/src/app/modules/tv-map/models/tv-road-lane-offset.ts
new file mode 100644
index 00000000..9af2f34b
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-road-lane-offset.ts
@@ -0,0 +1,18 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { ThirdOrderPolynom } from './third-order-polynom';
+import { Object3D } from 'three';
+
+export class TvRoadLaneOffset extends ThirdOrderPolynom {
+
+ public mesh?: Object3D;
+
+ clone ( s?: number ) {
+
+ return new TvRoadLaneOffset( s || this.s, this.a, this.b, this.c, this.d );
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/app/modules/tv-map/models/tv-road-lane-section-container.ts b/src/app/modules/tv-map/models/tv-road-lane-section-container.ts
new file mode 100644
index 00000000..5de0c6a7
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-road-lane-section-container.ts
@@ -0,0 +1,9 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvLane } from './tv-lane';
+
+export class TvRoadLaneSectionContainer {
+ public lane: TvLane[];
+}
\ No newline at end of file
diff --git a/src/app/modules/tv-map/models/tv-road-lane-section-lane-link.ts b/src/app/modules/tv-map/models/tv-road-lane-section-lane-link.ts
new file mode 100644
index 00000000..6166ecb7
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-road-lane-section-lane-link.ts
@@ -0,0 +1,6 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+export class TvRoadLaneSectionLaneLink {
+}
\ No newline at end of file
diff --git a/src/app/modules/tv-map/models/tv-road-lanes.spec.ts b/src/app/modules/tv-map/models/tv-road-lanes.spec.ts
new file mode 100644
index 00000000..9736721f
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-road-lanes.spec.ts
@@ -0,0 +1,49 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvRoad } from './tv-road.model';
+import { TvPosTheta } from './tv-pos-theta';
+
+describe( 'OdRoadLanes', () => {
+
+ let road: TvRoad;
+ let pose: TvPosTheta;
+
+ beforeEach( () => {
+
+ pose = new TvPosTheta();
+
+ road = new TvRoad( '', 30, 1, -1 );
+
+ road.addPlanView();
+
+ const s = 0;
+ const x = 0;
+ const y = 0;
+ const hdg = 0;
+ const length = 30;
+
+ road.addGeometryLine( s, x, y, hdg, length );
+
+ } );
+
+ it( 'should give correct getLaneOffsetAt s', () => {
+
+ road.addLaneSection( 0, true );
+ road.lanes.addLaneOffsetRecord( 0, 0, 0, 0, 0 );
+ road.lanes.addLaneOffsetRecord( 10, 1, 0, 0, 0 );
+ road.lanes.addLaneOffsetRecord( 20, 2, 0, 0, 0 );
+
+ expect( road.lanes.getLaneOffsetValue( 0 ) ).toBe( 0 );
+ expect( road.lanes.getLaneOffsetValue( 1 ) ).toBe( 0 );
+ expect( road.lanes.getLaneOffsetValue( 10 ) ).toBe( 1 );
+ expect( road.lanes.getLaneOffsetValue( 11 ) ).toBe( 1 );
+ expect( road.lanes.getLaneOffsetValue( 20 ) ).toBe( 2 );
+ expect( road.lanes.getLaneOffsetValue( 21 ) ).toBe( 2 );
+ expect( road.lanes.getLaneOffsetValue( 30 ) ).toBe( 2 );
+
+
+ } );
+
+} );
diff --git a/src/app/modules/tv-map/models/tv-road-lanes.ts b/src/app/modules/tv-map/models/tv-road-lanes.ts
new file mode 100644
index 00000000..e1a91abd
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-road-lanes.ts
@@ -0,0 +1,146 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvRoadLaneOffset } from './tv-road-lane-offset';
+import { TvLaneSection } from './tv-lane-section';
+import { TvRoad } from './tv-road.model';
+import { TvUtils } from './tv-utils';
+import { isNullOrUndefined } from 'util';
+
+export class TvRoadLanes {
+
+ public laneSections: TvLaneSection[] = [];
+
+ private laneOffsets: TvRoadLaneOffset[] = [];
+
+ getLaneSections (): TvLaneSection[] {
+ return this.laneSections;
+ }
+
+ getLaneOffsets (): TvRoadLaneOffset[] {
+ return this.laneOffsets;
+ }
+
+ addLaneOffsetRecord ( s: number, a: number, b: number, c: number, d: number ): any {
+
+ this.laneOffsets.push( new TvRoadLaneOffset( s, a, b, c, d ) );
+
+ this.laneOffsets.sort( ( a, b ) => a.s > b.s ? 1 : -1 );
+
+ }
+
+ addLaneOffsetInstance ( laneOffset: TvRoadLaneOffset ): void {
+
+ this.laneOffsets.push( laneOffset );
+
+ this.laneOffsets.sort( ( a, b ) => a.s > b.s ? 1 : -1 );
+ }
+
+ updateLaneOffsetValues ( roadLength: number ) {
+
+ for ( let i = 0; i < this.laneOffsets.length && this.laneOffsets.length > 1; i++ ) {
+
+ const current = this.laneOffsets[ i ];
+
+ let pp0, pp1, pd0, pd1, length;
+
+ if ( ( i + 1 ) < this.laneOffsets.length ) {
+
+ const next = this.laneOffsets[ i + 1 ];
+
+ // next s cannot be less than current so we need to clamp it
+ if ( next.s <= current.s ) {
+
+ next.s = current.s + 0.1;
+
+ }
+
+ length = next.s - current.s;
+
+ pp0 = current.a; // offset at start
+ pp1 = next.a; // offset at end
+ pd0 = current.b; // tangent at start
+ pd1 = next.b; // tangent at end
+
+ } else {
+
+ // take lane section length
+ length = roadLength;
+
+ pp0 = current.a; // offset at start
+ pp1 = current.a; // offset at end
+ pd0 = current.b; // tangent at start
+ pd1 = current.b; // tangent at end
+
+ }
+
+ let a = pp0;
+ let b = pd0;
+ let c = ( -3 * pp0 ) + ( 3 * pp1 ) + ( -2 * pd0 ) + ( -1 * pd1 );
+ let d = ( 2 * pp0 ) + ( -2 * pp1 ) + ( 1 * pd0 ) + ( 1 * pd1 );
+
+ b /= length;
+ c /= length * length;
+ d /= length * length * length;
+
+ current.a = a;
+ current.b = b;
+ current.c = c;
+ current.d = d;
+
+ }
+
+ }
+
+ computeLaneSectionEnd ( road: TvRoad ) {
+
+ // Compute lastSCoordinate for all laneSections
+ for ( let i = 0; i < this.laneSections.length; i++ ) {
+
+ const currentLaneSection = this.laneSections[ i ];
+
+ // by default it is equal to road lenght
+ let lastSCoordinate = 0;
+
+ // if next laneSection exists lets use its sCoordinate
+ if ( i + 1 < this.laneSections.length ) {
+
+ const nextLaneSection = this.laneSections[ i + 1 ];
+ lastSCoordinate = nextLaneSection.attr_s;
+
+ } else {
+
+
+ lastSCoordinate = road.length;
+ }
+
+ currentLaneSection.lastSCoordinate = lastSCoordinate;
+ }
+ }
+
+ getLaneOffsetValue ( s: number ): number {
+
+ if ( isNullOrUndefined( s ) ) console.error( "s is undefined" );
+
+ let offset = 0;
+
+ const hasEntry = this.getLaneOffsetEntryAt( s );
+
+ if ( hasEntry ) offset = hasEntry.getValue( s );
+
+ return offset;
+ }
+
+ getLaneOffsetEntryAt ( s: number ): TvRoadLaneOffset {
+
+ return TvUtils.checkIntervalArray( this.laneOffsets, s );
+
+ }
+
+ getLaneSectionAt ( s: number ): TvLaneSection {
+
+ return TvUtils.checkIntervalArray( this.laneSections, s );
+
+ }
+}
diff --git a/src/app/modules/tv-map/models/tv-road-link-child.ts b/src/app/modules/tv-map/models/tv-road-link-child.ts
new file mode 100644
index 00000000..3960ec10
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-road-link-child.ts
@@ -0,0 +1,42 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import { TvContactPoint } from './tv-common';
+
+export class TvRoadLinkChild {
+
+ public attr_elementType: string;
+ public attr_elementId: number;
+ public attr_contactPoint: TvContactPoint;
+
+ constructor ( elementType: string, elementId: number, contactPoint: TvContactPoint ) {
+ this.attr_elementType = elementType;
+ this.attr_elementId = elementId;
+ this.attr_contactPoint = contactPoint;
+ }
+
+ get elementType () {
+ return this.attr_elementType;
+ }
+
+ set elementType ( value ) {
+ this.attr_elementType = value;
+ }
+
+ get elementId () {
+ return this.attr_elementId;
+ }
+
+ set elementId ( value ) {
+ this.attr_elementId = value;
+ }
+
+ get contactPoint () {
+ return this.attr_contactPoint;
+ }
+
+ set contactPoint ( value ) {
+ this.attr_contactPoint = value;
+ }
+}
diff --git a/src/app/modules/tv-map/models/tv-road-link-neighbor.ts b/src/app/modules/tv-map/models/tv-road-link-neighbor.ts
new file mode 100644
index 00000000..2b8e6a7d
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-road-link-neighbor.ts
@@ -0,0 +1,15 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+export class TvRoadLinkNeighbor {
+ public attr_side;
+ public attr_elementId;
+ public attr_direction;
+
+ constructor ( side: string, elementId: string, direction: string ) {
+ this.attr_side = side;
+ this.attr_elementId = elementId;
+ this.attr_direction = direction;
+ }
+}
\ No newline at end of file
diff --git a/src/app/modules/tv-map/models/tv-road-object.ts b/src/app/modules/tv-map/models/tv-road-object.ts
new file mode 100644
index 00000000..60d0a4b5
--- /dev/null
+++ b/src/app/modules/tv-map/models/tv-road-object.ts
@@ -0,0 +1,452 @@
+/*
+ * Copyright Truesense AI Solutions Pvt Ltd, All Rights Reserved.
+ */
+
+import {
+ TvBridgeTypes,
+ TvColors,
+ TvOrientation,
+ TvParkingSpaceAccess,
+ TvParkingSpaceMarkingSides,
+ TvRoadMarkTypes,
+ TvTunnelTypes,
+ TvUserData
+} from './tv-common';
+import { Mesh } from 'three';
+
+export class TvObjectContainer {
+ public object: TvRoadObject[] = [];
+ public objectReference: TvRoadObjectReference[] = [];
+ public tunnel: TvRoadTunnel[] = [];
+ public bridge: TvRoadBridge[] = [];
+}
+
+export class TvRoadObject {
+
+ public attr_type: string;
+ public attr_name: string;
+ public attr_id: number;
+ public attr_s: number;
+ public attr_t: number;
+ public attr_zOffset: number;
+ public attr_validLength: number;
+ public attr_orientation: any;
+ public attr_length: number;
+ public attr_width: number;
+ public attr_radius: number;
+ public attr_height: number;
+ public attr_hdg: number;
+ public attr_pitch: number;
+ public attr_roll: number;
+
+ public repeat: TvObjectRepeat[] = [];
+ public outline: TvObjectOutline;
+ public material: TvObjectMaterial;
+ public validity: TvLaneValidity[] = [];
+ public parkingSpace: TvParkingSpace;
+ public userData: TvUserData[] = [];
+ // public gameObject: Object3D;
+ private lastAddedRepeatObjectIndex: number;
+
+ public mesh: Mesh;
+
+ constructor (
+ type: string,
+ name: string,
+ id: number,
+ s: number,
+ t: number,
+ zOffset: number,
+ validLength: number,
+ orientation: TvOrientation,
+ length: number = null,
+ width: number = null,
+ radius: number = null,
+ height: number = null,
+ hdg: number = null,
+ pitch: number = null,
+ roll: number = null
+ ) {
+ this.attr_type = type;
+ this.attr_name = name;
+ this.attr_id = id;
+ this.attr_s = s;
+ this.attr_t = t;
+ this.attr_zOffset = zOffset;
+ this.attr_validLength = validLength;
+ this.attr_orientation = orientation;
+ this.attr_length = length;
+ this.attr_width = width;
+ this.attr_radius = radius;
+ this.attr_height = height;
+ this.attr_hdg = hdg;
+ this.attr_pitch = pitch;
+ this.attr_roll = roll;
+ }
+
+ get type (): string {
+ return this.attr_type;
+ }
+
+ get name (): string {
+ return this.attr_name;
+ }
+
+ get id (): number {
+ return this.attr_id;
+ }
+
+ get s (): number {
+ return this.attr_s;
+ }
+
+ get t (): number {
+ return this.attr_t;
+ }
+
+ get zOffset (): number {
+ return this.attr_zOffset;
+ }
+
+ get validLength (): number {
+ return this.attr_validLength;
+ }
+
+ get orientation (): any {
+ return this.attr_orientation;
+ }
+
+ get length (): number {
+ return this.attr_length;
+ }
+
+ get width (): number {
+ return this.attr_width;
+ }
+
+ get radius (): number {
+ return this.attr_radius;
+ }
+
+ get height (): number {
+ return this.attr_height;
+ }
+
+ get hdg (): number {
+ return this.attr_hdg;
+ }
+
+ get pitch (): number {
+ return this.attr_pitch;
+ }
+
+ get roll (): number {
+ return this.attr_roll;
+ }
+
+ getRepeatCount (): number {
+ return this.repeat.length;
+ }
+
+ getRepeatList (): TvObjectRepeat[] {
+ return this.repeat;
+ }
+
+ getRepeat ( i: number ): TvObjectRepeat {
+ return this.repeat[ i ];
+ }
+
+ getValidityCount (): number {
+ return this.validity.length;
+ }
+
+ getValidityList (): TvLaneValidity[] {
+ return this.validity;
+ }
+
+ getValidity ( i: number ): TvLaneValidity {
+ return this.validity[ i ];
+ }
+
+ addRepeat (
+ s: number, length: number, distance: number, tStart: number, tEnd: number,
+ widthStart: number, widthEnd: number, heightStart: number, heightEnd: number,
+ zOffsetStart: number, zOffsetEnd: number
+ ): void {
+
+ this.repeat.push(
+ new TvObjectRepeat( s, length, distance, tStart, tEnd, widthStart, widthEnd, heightStart, heightEnd, zOffsetStart, zOffsetEnd )
+ );
+
+ this.lastAddedRepeatObjectIndex = this.repeat.length - 1;
+
+ }
+
+
+}
+
+export class TvObjectOutline {
+ public cornerRoad: TvCornerRoad[] = [];
+ public cornerLocal: TvCornerLocal[] = [];
+
+ getCornerLocal ( i: number ): TvCornerLocal {
+ return this.cornerLocal[ i ];
+ }
+
+ getCornerLocalCount (): number {
+ return this.cornerLocal.length;
+ }
+
+ getCornerRoad ( i: number ): TvCornerRoad {
+ return this.cornerRoad[ i ];
+ }
+
+ getCornerRoadCount (): number {
+ return this.cornerRoad.length;
+ }
+}
+
+export class TvParkingSpace {
+ public attr_access: TvParkingSpaceAccess;
+ public attr_restriction: string;
+
+ // MAX 4 ENTRIES ALLOWED
+ public marking: TvParkingSpaceMarking[] = [];
+
+ getMarkingCount (): number {
+ return this.marking.length;
+ }
+
+ getMarkingList (): TvParkingSpaceMarking[] {
+ return this.marking;
+ }
+
+ getMarking ( i: number ): TvParkingSpaceMarking {
+ return this.marking[ i ];
+ }
+}
+
+export class TvParkingSpaceMarking {
+ public attr_side: TvParkingSpaceMarkingSides;
+ public attr_type: TvRoadMarkTypes;
+ public attr_width: number;
+ public attr_color: TvColors;
+}
+
+/**
+ * For objects like patches which are within the road surface (and, typically, coplanar to the surface) and
+ * which represent a local deviation from the standard road material, a description of the material
+ * properties is required. This description supercedes the one provided by the Road Material record and,
+ * again, is valid only within the outline of the parent road object.
+ */
+export class TvObjectMaterial {
+ public attr_surface: string;
+ public attr_friction: number;
+ public attr_roughness: number;
+}
+
+/**
+ * Defines a corner point on the object’s outline in road co-ordinates..
+ */
+export class TvCornerRoad {
+ public attr_s: number;
+ public attr_t: number;
+ public attr_dz: number;
+ public attr_height: number;
+}
+
+/**
+ * Defines a corner point on the object’s outline relative to the object's pivot point in local u/v coordinates.
+ * The pivot point and the orientation of the object are given by the s/t/heading arguments
+ * of the