diff --git a/CHANGELOG.md b/CHANGELOG.md
index b0711759..aa709255 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -23,10 +23,30 @@ and this project adheres to
- Add infrastructure `collisions` boolean drawing parameter, enabled by
default. When enabled, RacksDB highlights equipment colliding with other
equipment with red frames in infrastructure diagrams.
+ - Generate 3D representations of infrastructures in glTF 2.0 JSON (`gltf`)
+ and binary (`glb`) formats, with racks and equipment in their datacenter
+ rooms on raised floors with perforated tiles in cold aisles. Nodes of the
+ scene are named after database objects with metadata in their extras
+ (#164).
+- cli: Support `gltf` and `glb` formats in `draw infrastructure` command (#164).
+- web: Support `gltf` and `glb` formats in infrastructures draw endpoint
+ (#164).
+- ui: Add 2D/3D toggle in infrastructure details page, with an interactive 3D
+ viewer of infrastructures based on three.js, with a turntable mode with the
+ camera orbiting automatically around the racks (#164).
+- pkgs:
+ - Install floor textures of 3D representations with the Python package.
+ - Add dependency on three.js in web UI.
- docs:
- Mention support of Fedora 44.
- Document `equipment` attribute of `RacksDBRack` objects in Python library
API reference.
+ - Add page about 3D representations of infrastructures with glTF scenes
+ conventions and nodes metadata.
+ - Mention `gltf` and `glb` formats in `racksdb` manpage.
+ - Mention 3D view of infrastructures in web UI page.
+ - Mention 3D representations of infrastructures in diagrams section of
+ overview page.
### Fixed
- lib:
diff --git a/README.md b/README.md
index 2963f6c9..41b96731 100644
--- a/README.md
+++ b/README.md
@@ -73,8 +73,8 @@ RacksDB provides multiple interfaces with its
RacksDB can generate graphical representations of your datacenter rooms and your
-infrastructures with their racks and equipment, including axonometric views for
-3D representations:
+infrastructures with their racks and equipment, including axonometric views and
+[3D representations](https://docs.rackslab.io/racksdb/usage/3d.html):
-
-
+
diff --git a/docs/modules/overview/pages/overview.adoc b/docs/modules/overview/pages/overview.adoc
index bef0bf38..66426383 100644
--- a/docs/modules/overview/pages/overview.adoc
+++ b/docs/modules/overview/pages/overview.adoc
@@ -74,17 +74,24 @@ synchronized with database content.
== Diagrams
RacksDB can be used to generate diagrams of your datacenter rooms and the racks
-with all their equipments, including _axonometric views_ for 3D representations.
+with all their equipments, including _axonometric views_, and
+xref:usage:3d.adoc[3D representations] of your infrastructures.
image::overview:racksdb_diagrams.png[RacksDB Diagrams,xref=image$racksdb_diagrams.svg]
These diagrams can be generated in various formats (PNG, SVG, PDF) and be
customized with many xref:usage:drawparams.adoc[drawing parameters].
+The 3D representations of infrastructures are generated in standard glTF 2.0
+format, with your racks and equipment in their datacenter rooms. They can be
+explored interactively in the xref:usage:ui.adoc[Web UI] or imported in any
+3D software.
+
[sidebar]
--
.More links
* xref:usage:racksdb.adoc#draw[`racksdb` CLI drawing feature]
* xref:usage:drawparams.adoc[Drawing parameters howto and reference
documentation]
+* xref:usage:3d.adoc[3D representations of infrastructures]
--
diff --git a/docs/modules/usage/attachments/openapi.yml b/docs/modules/usage/attachments/openapi.yml
index 204146f1..162bdb6c 100644
--- a/docs/modules/usage/attachments/openapi.yml
+++ b/docs/modules/usage/attachments/openapi.yml
@@ -1143,7 +1143,8 @@ paths:
description: successful operation
/v0.7.0/draw//.:
get:
- description: Draw an entity
+ description: Draw an entity. Infrastructures can also be represented in 3D with
+ glTF 2.0 formats (gltf and glb).
parameters:
- description: Type of entity to draw
in: path
@@ -1160,7 +1161,8 @@ paths:
required: true
schema:
type: string
- - description: Format of the generated image
+ - description: Format of the generated image. Formats gltf and glb are supported
+ for infrastructures only.
in: path
name: format
required: true
@@ -1170,6 +1172,8 @@ paths:
- png
- svg
- pdf
+ - gltf
+ - glb
type: string
- description: Control if entities are represented perfectly aligned on pixels
with concession on relative dimensions correctess. This parameter has no
@@ -1485,6 +1489,13 @@ paths:
image/svg+xml:
schema:
type: string
+ model/gltf+json:
+ schema:
+ type: string
+ model/gltf-binary:
+ schema:
+ format: binary
+ type: string
multipart/form-data:
schema:
format: binary
@@ -1496,8 +1507,8 @@ paths:
schema:
$ref: '#/components/schemas/Error'
description: Unsupported entity, unable to load drawing parameters, unsupported
- image format, unsupported coordinates format or unable to load requested
- entity in database.
+ image format, unsupported coordinates format, coordinates requested with
+ 3D format or unable to load requested entity in database.
'415':
content:
application/json:
@@ -1511,7 +1522,8 @@ paths:
$ref: '#/components/schemas/Error'
description: Unable to load drawing parameters schema.
post:
- description: Draw an entity
+ description: Draw an entity. Infrastructures can also be represented in 3D with
+ glTF 2.0 formats (gltf and glb).
parameters:
- description: Type of entity to draw
in: path
@@ -1528,7 +1540,8 @@ paths:
required: true
schema:
type: string
- - description: Format of the generated image
+ - description: Format of the generated image. Formats gltf and glb are supported
+ for infrastructures only.
in: path
name: format
required: true
@@ -1538,6 +1551,8 @@ paths:
- png
- svg
- pdf
+ - gltf
+ - glb
type: string
- allowEmptyValue: true
description: Dump equipments and racks coordinates in diagrams. When this
@@ -1579,6 +1594,13 @@ paths:
image/svg+xml:
schema:
type: string
+ model/gltf+json:
+ schema:
+ type: string
+ model/gltf-binary:
+ schema:
+ format: binary
+ type: string
multipart/form-data:
schema:
format: binary
@@ -1590,8 +1612,8 @@ paths:
schema:
$ref: '#/components/schemas/Error'
description: Unsupported entity, unable to load drawing parameters, unsupported
- image format, unsupported coordinates format or unable to load requested
- entity in database.
+ image format, unsupported coordinates format, coordinates requested with
+ 3D format or unable to load requested entity in database.
'415':
content:
application/json:
diff --git a/docs/modules/usage/nav.adoc b/docs/modules/usage/nav.adoc
index b2b2d0a2..9950cafc 100644
--- a/docs/modules/usage/nav.adoc
+++ b/docs/modules/usage/nav.adoc
@@ -2,6 +2,7 @@
** xref:racksdb.adoc[`racksdb`]
** xref:racksdb-web.adoc[`racksdb-web`]
* xref:drawparams.adoc[]
+* xref:3d.adoc[]
* xref:lib.adoc[]
* xref:rest.adoc[]
* xref:ui.adoc[]
diff --git a/docs/modules/usage/pages/3d.adoc b/docs/modules/usage/pages/3d.adoc
new file mode 100644
index 00000000..71ba986b
--- /dev/null
+++ b/docs/modules/usage/pages/3d.adoc
@@ -0,0 +1,149 @@
+= 3D Representations
+
+RacksDB can generate 3D representations of infrastructures in
+https://www.khronos.org/gltf/[glTF 2.0] format, the standard format for 3D
+scenes supported by most 3D software (_ex:_ Blender) and web 3D libraries
+(_ex:_ three.js, Babylon.js). These representations contain all the rooms and
+racks used by the infrastructure, with its equipment at their positions in the
+racks.
+
+image::shadowed/3d_fullscreen.webp[3D Representation of Infrastructure,800]
+
+== Formats
+
+Two variants of glTF format are supported:
+
+`glb`::
+ Binary format, in a single file with all the data. This is the most compact
+ variant, it is recommended in most cases. Its media type is
+ `model/gltf-binary`.
+
+`gltf`::
+ JSON format, with all binary data (geometry, textures) embedded in base64
+ encoded URI. Its media type is `model/gltf+json`.
+
+These formats are supported for infrastructures only. Coordinates cannot be
+requested with these formats.
+
+== Usage
+
+With xref:racksdb.adoc#draw[`racksdb` CLI], select the format with `--format`
+option. For example:
+
+[source,console]
+----
+$ racksdb draw infrastructure --name=mercury --format=glb
+----
+
+This command generates file `mercury.glb` in current directory.
+
+With xref:rest.adoc[REST API], use the format as extension of the requested
+resource. For example:
+
+[source,console,subs=attributes]
+----
+$ curl -X POST http://localhost:5000/{api-version}/draw/infrastructure/mercury.glb \
+ --output mercury.glb
+----
+
+The xref:ui.adoc#_infrastructure_details[web UI] also displays these 3D
+representations in an interactive viewer.
+
+== Drawing Parameters
+
+The following xref:drawparams.adoc[drawing parameters] are honored in 3D
+representations:
+
+* `infrastructure.equipment_tags`
+* `infrastructure.ghost_unselected`, ghosted equipment are semi-transparent
+* `infrastructure.discard_empty_racks`
+* `infrastructure.other_racks`, racks not used by the infrastructure are
+ represented with closed doors
+* `infrastructure.collisions`, edges of colliding equipment are red
+* `rack.u_height`
+* `rack.pane_width`
+* `colors.equipments`, with `background`, `border` and `ghost` colors
+* `colors.racks`, with `frame` color
+
+Other drawing parameters are specific to images and they are ignored.
+
+== Scene
+
+The scene uses meters as unit, with Y axis pointing up. In rooms, the X axis
+follows room width and the Z axis follows room depth, so that a scene viewed
+from above matches room maps. Racks doors face +Z in racks local coordinates.
+
+The rooms have raised floors with 600mm tiles. Every other tile in front of
+racks doors is perforated, to represent cold aisles. When the infrastructure is
+spread over multiple rooms, the rooms are placed side by side along the X axis,
+separated by 2 meters.
+
+Equipment depth is not defined in database. Equipment are represented as deep
+as their rack, minus 10cm at the rear.
+
+The glTF scene graph follows the structure of the database. Every glTF node
+representing a database object (room, row, rack or equipment) is named after
+this object and has metadata in its `extras` property, with the `category` key
+giving the type of object. For equipment, this key contains the category of
+equipment in RacksDB (`node`, `storage`, `network` or `misc`).
+
+[cols="1,2,1,4"]
+|===
+|Object|Category|Parent|Extras
+
+|Room
+|`room`
+|_scene_
+|`datacenter`: name of datacenter +
+`size`: room width, height (always 0) and depth in meters
+
+|Row
+|`row`
+|Room
+|`reversed`: boolean, true if racks doors face the top of room maps
+
+|Rack
+|`rack`
+|Row
+|`type`: rack type +
+`tags`: list of rack tags +
+`infrastructure`: boolean, false if the rack is represented only because of
+`other_racks` drawing parameter +
+`size`: rack width, height and depth in meters
+
+|Equipment
+|`node`, `storage`, `network` or `misc`
+|Rack
+|`type`: equipment type +
+`tags`: list of equipment tags +
+`rack`: name of rack +
+`slot`: object with `height` and `width` keys, the position of equipment in
+rack +
+`selected`: boolean, false for ghosted equipment +
+`colliding`: boolean, true if equipment collides with other equipment +
+`size`: equipment width, height and depth in meters
+|===
+
+The geometry of database objects is attached to unnamed child glTF nodes, which
+have a `part` key in their `extras` instead of `category`. For example, this is
+an excerpt of the scene graph of `mercury` infrastructure, with the `extras` of
+the glTF nodes:
+
+----
+noisy {"category": "room", …}
+├── (unnamed) {"part": "floor"}
+├── R1 {"category": "row", …}
+│ └── R1-A01 {"category": "rack", …}
+│ ├── (unnamed) {"part": "left"}
+│ ├── …
+│ └── mecn0001 {"category": "node", …}
+│ ├── (unnamed) {"part": "body"}
+│ └── (unnamed) {"part": "edges"}
+└── …
+----
+
+NOTE: Some glTF loaders modify the names of glTF nodes. For example, three.js
+removes characters `[]./:` and replaces whitespaces with underscores, so
+equipment `sw1.example.org` is named `sw1exampleorg`. Original names can be
+retrieved with `parser.associations` and `parser.json` properties of the loaded
+glTF object.
diff --git a/docs/modules/usage/pages/drawparams.adoc b/docs/modules/usage/pages/drawparams.adoc
index d9d7ed28..13635c2f 100644
--- a/docs/modules/usage/pages/drawparams.adoc
+++ b/docs/modules/usage/pages/drawparams.adoc
@@ -2,8 +2,9 @@
RacksDB offers the possibity to generate graphical representations of datacenter
room and infrastructures with their racks and equipments in various images
-formats. The images are generated with various default drawing parameters that
-user are able to customize for their requirements.
+formats, and xref:3d.adoc[3D representations] of infrastructures. The images
+are generated with various default drawing parameters that user are able to
+customize for their requirements.
The xref:#usage[first section] of this page describes how to specify drawing
parameters with both RacksDB xref:racksdb.adoc[CLI] and
diff --git a/docs/modules/usage/pages/racksdb.adoc b/docs/modules/usage/pages/racksdb.adoc
index 78c81370..cbe49d41 100644
--- a/docs/modules/usage/pages/racksdb.adoc
+++ b/docs/modules/usage/pages/racksdb.adoc
@@ -280,8 +280,9 @@ Get all tags associated to racks in datacenter _paris_.
=== Draw commands
The [.cli-opt]#*draw*# command is used to generate image file with graphical
-representations of database entities. This command is used in combination with a
-sub-command to indicate the type of database entity.
+representations of database entities, or 3D representations of
+infrastructures. This command is used in combination with a sub-command to
+indicate the type of database entity.
This command accepts the following options:
@@ -289,8 +290,11 @@ This command accepts the following options:
Name of the entity to represent. This option is required.
[.cli-opt]#*--format*=#[.cli-optval]##_FORMAT_##::
- File format of the generated image file. Possible values are _png_, _svg_ and
- _pdf_. Default value is _png_.
+ File format of the generated image file. Possible values are _png_, _svg_,
+ _pdf_, _gltf_ and _glb_. Default value is _png_. Formats _gltf_ and _glb_
+ produce xref:3d.adoc[3D representations] in glTF 2.0 format, they are
+ supported for infrastructures only and they cannot be combined with
+ [.cli-opt]#*--coordinates*# option.
[.cli-opt]#*--parameters*=#[.cli-optval]##_PARAMETERS_##::
Path to YAML xref:drawparams.adoc[drawing parameters] file. With special value
@@ -316,7 +320,8 @@ The following sub-commands are available:
[.cli-opt]#*infrastructure*#::
Draw racks of an infrastructure, grouped by rows. The front side of the racks
- are represented with the equipments of the infrastructure.
+ are represented with the equipments of the infrastructure. With _gltf_ and
+ _glb_ formats, the racks are represented in 3D in their datacenter rooms.
[.cli-opt]#*room*#::
@@ -347,6 +352,13 @@ Generate PDF image file `tiger.pdf` with racks and equipments used in _tiger_
infrastructure with custom xref:drawparams.adoc[drawing parameters] defined in
`custom.yaml`.
+[source,console]
+$ racksdb draw infrastructure --name=tiger --format=glb
+
+[.cli-example-desc]
+Generate 3D representation of _tiger_ infrastructure in binary glTF file
+`tiger.glb`.
+
[source,console]
$ racksdb draw room --name=noisy --parameters=- < custom.yaml
diff --git a/docs/modules/usage/pages/ui.adoc b/docs/modules/usage/pages/ui.adoc
index 5acfdf28..bfe31c39 100644
--- a/docs/modules/usage/pages/ui.adoc
+++ b/docs/modules/usage/pages/ui.adoc
@@ -57,6 +57,17 @@ In *Infrastructure Details* page, a graphical representation of the
infrastructure is displayed at the top. Users can *click on this thumbnail* to
display a larger image.
+Users can switch to the xref:3d.adoc[3D representation] of the infrastructure
+with the *2D/3D toggle* above the image. In the 3D view, users can rotate the
+camera around the racks with mouse left button, pan with right button and zoom
+with mouse wheel. The name, type and rack of equipment are displayed when the
+mouse hovers it. Buttons are available to reset the view, to switch to
+fullscreen mode and to enable the *turntable mode*. In turntable mode, the
+camera orbits automatically around the racks until users click in the 3D view,
+giving them back the control of the camera.
+
+image::shadowed/3d_fullscreen.webp[UI Infrastructure 3D View,800]
+
The page contains a table of all infrastructure equipment grouped by rack,
including their _name_, _category_, _model_ and _tags_.
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 312a921e..0f7d5a27 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -12,6 +12,7 @@
"@heroicons/vue": "^2.1.1",
"axios": "^1.6.2",
"leaflet": "^1.9.4",
+ "three": "^0.186.1",
"vue": "^3.4",
"vue-router": "^4.2.4"
},
@@ -22,6 +23,7 @@
"@types/jsdom": "^21.1.3",
"@types/leaflet": "^1.9.8",
"@types/node": "^18.17.17",
+ "@types/three": "^0.186.0",
"@vitejs/plugin-vue": "^5.2.3",
"@vue/eslint-config-prettier": "^8.0.0",
"@vue/eslint-config-typescript": "^12.0.0",
@@ -428,6 +430,13 @@
"node": ">=20.19.0"
}
},
+ "node_modules/@dimforge/rapier3d-compat": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz",
+ "integrity": "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
"node_modules/@esbuild/aix-ppc64": {
"version": "0.25.1",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.1.tgz",
@@ -981,18 +990,6 @@
"node": ">=6.0.0"
}
},
- "node_modules/@jridgewell/source-map": {
- "version": "0.3.5",
- "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz",
- "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==",
- "dev": true,
- "optional": true,
- "peer": true,
- "dependencies": {
- "@jridgewell/gen-mapping": "^0.3.0",
- "@jridgewell/trace-mapping": "^0.3.9"
- }
- },
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
@@ -1435,17 +1432,12 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@types/eslint": {
- "version": "8.44.7",
- "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.7.tgz",
- "integrity": "sha512-f5ORu2hcBbKei97U73mf+l9t4zTGl74IqZ0GQk4oVea/VS8tQZYkUveSYojk+frraAVYId0V2WC9O4PTNru2FQ==",
+ "node_modules/@tweenjs/tween.js": {
+ "version": "23.1.3",
+ "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz",
+ "integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==",
"dev": true,
- "optional": true,
- "peer": true,
- "dependencies": {
- "@types/estree": "*",
- "@types/json-schema": "*"
- }
+ "license": "MIT"
},
"node_modules/@types/estree": {
"version": "1.0.8",
@@ -1498,11 +1490,40 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@types/stats.js": {
+ "version": "0.17.4",
+ "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz",
+ "integrity": "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/three": {
+ "version": "0.186.0",
+ "resolved": "https://registry.npmjs.org/@types/three/-/three-0.186.0.tgz",
+ "integrity": "sha512-mxYSBpDC+D0pLfSP6sW4WZTcT+nrtmZcimMqnVmy36Hte3XpeYSrvgg4TRdaM1GemGog1AWzI5qL2VoIfMXbJQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@dimforge/rapier3d-compat": "~0.12.0",
+ "@tweenjs/tween.js": "~23.1.3",
+ "@types/stats.js": "*",
+ "@types/webxr": ">=0.5.17",
+ "fflate": "~0.8.3",
+ "meshoptimizer": "~1.1.1"
+ }
+ },
"node_modules/@types/tough-cookie": {
"version": "4.0.3",
"dev": true,
"license": "MIT"
},
+ "node_modules/@types/webxr": {
+ "version": "0.5.24",
+ "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz",
+ "integrity": "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "6.7.4",
"dev": true,
@@ -2310,14 +2331,6 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
- "node_modules/buffer-from": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
- "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
- "dev": true,
- "optional": true,
- "peer": true
- },
"node_modules/bundle-name": {
"version": "3.0.0",
"dev": true,
@@ -3180,6 +3193,13 @@
"reusify": "^1.0.4"
}
},
+ "node_modules/fflate": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz",
+ "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/file-entry-cache": {
"version": "6.0.1",
"dev": true,
@@ -3749,17 +3769,6 @@
"dev": true,
"license": "ISC"
},
- "node_modules/jiti": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz",
- "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==",
- "dev": true,
- "optional": true,
- "peer": true,
- "bin": {
- "jiti": "lib/jiti-cli.mjs"
- }
- },
"node_modules/js-beautify": {
"version": "1.14.9",
"dev": true,
@@ -4025,6 +4034,13 @@
"node": ">= 8"
}
},
+ "node_modules/meshoptimizer": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-1.1.1.tgz",
+ "integrity": "sha512-oRFNWJRDA/WTrVj7NWvqa5HqE1t9MYDj2VaWirQCzCCrAd2GHrqR/sQezCxiWATPNlKTcRaPRHPJwIRoPBAp5g==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/micromatch": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
@@ -5180,17 +5196,6 @@
"node": ">=8"
}
},
- "node_modules/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,
- "peer": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -5199,18 +5204,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/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,
- "optional": true,
- "peer": true,
- "dependencies": {
- "buffer-from": "^1.0.0",
- "source-map": "^0.6.0"
- }
- },
"node_modules/spdx-correct": {
"version": "3.2.0",
"dev": true,
@@ -5430,34 +5423,6 @@
"jiti": "bin/jiti.js"
}
},
- "node_modules/terser": {
- "version": "5.24.0",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.24.0.tgz",
- "integrity": "sha512-ZpGR4Hy3+wBEzVEnHvstMvqpD/nABNelQn/z2r0fjVWGQsN3bpOLzQlqDxmb4CDZnXq5lpjnQ+mHQLAOpfM5iw==",
- "dev": true,
- "optional": true,
- "peer": true,
- "dependencies": {
- "@jridgewell/source-map": "^0.3.3",
- "acorn": "^8.8.2",
- "commander": "^2.20.0",
- "source-map-support": "~0.5.20"
- },
- "bin": {
- "terser": "bin/terser"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/terser/node_modules/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,
- "optional": true,
- "peer": true
- },
"node_modules/text-table": {
"version": "0.2.0",
"dev": true,
@@ -5484,6 +5449,12 @@
"node": ">=0.8"
}
},
+ "node_modules/three": {
+ "version": "0.186.1",
+ "resolved": "https://registry.npmjs.org/three/-/three-0.186.1.tgz",
+ "integrity": "sha512-blFeqb49wRCSGUGj7gtpfnSGHy2lwDk94RhUmS1c/hTby70kvChbWpkJ4Pm1390LqzzvTmzgXKHPEafJwCb8jA==",
+ "license": "MIT"
+ },
"node_modules/tinybench": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 19fb8b95..a3dc6c08 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -17,6 +17,7 @@
"@heroicons/vue": "^2.1.1",
"axios": "^1.6.2",
"leaflet": "^1.9.4",
+ "three": "^0.186.1",
"vue": "^3.4",
"vue-router": "^4.2.4"
},
@@ -27,6 +28,7 @@
"@types/jsdom": "^21.1.3",
"@types/leaflet": "^1.9.8",
"@types/node": "^18.17.17",
+ "@types/three": "^0.186.0",
"@vitejs/plugin-vue": "^5.2.3",
"@vue/eslint-config-prettier": "^8.0.0",
"@vue/eslint-config-typescript": "^12.0.0",
diff --git a/frontend/src/components/InfrastructureScene3D.vue b/frontend/src/components/InfrastructureScene3D.vue
new file mode 100644
index 00000000..2222705d
--- /dev/null
+++ b/frontend/src/components/InfrastructureScene3D.vue
@@ -0,0 +1,166 @@
+
+
+
+
+
+
+
+
+
+ Loading 3D representation…
+
+
+ Unable to load 3D representation of infrastructure {{ infrastructureName }}
+