first commit
This commit is contained in:
18
frontend/.dockerignore
Normal file
18
frontend/.dockerignore
Normal file
@@ -0,0 +1,18 @@
|
||||
# Build context = ./frontend (vedi docker-compose.yml). Il .dockerignore di root
|
||||
# NON si applica qui: serve questo file dedicato.
|
||||
# Evita di copiare nell'immagine artefatti pesanti, locali o incompatibili.
|
||||
|
||||
# node_modules host = glibc, immagine = Alpine/musl: MAI copiarli nel container.
|
||||
node_modules
|
||||
dist
|
||||
coverage
|
||||
stats.html
|
||||
|
||||
.git
|
||||
.gitignore
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
*.log
|
||||
.DS_Store
|
||||
45
frontend/frontend.Dockerfile
Normal file
45
frontend/frontend.Dockerfile
Normal file
@@ -0,0 +1,45 @@
|
||||
# --- STAGE 1: Sviluppo (Node LTS) ---
|
||||
FROM node:lts-alpine3.24 AS development
|
||||
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# In sviluppo il sorgente arriva dal bind-mount (./frontend:/app) e i node_modules
|
||||
# dall'immagine via volume anonimo (/app/node_modules): NON si copia il sorgente qui.
|
||||
# Vantaggi: niente build fragile su index.html/src mancanti, nessun COPY ricorsivo
|
||||
# (docker:S6470) e i node_modules musl dell'immagine schermano quelli glibc dell'host.
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
|
||||
EXPOSE 5173
|
||||
CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"]
|
||||
|
||||
# --- STAGE 2: Build (Node LTS) ---
|
||||
FROM node:lts-alpine3.24 AS build
|
||||
|
||||
LABEL maintainer="Giuseppe Naponiello"
|
||||
LABEL version="3.0"
|
||||
LABEL description="Dynamic Collections Plus, frontend container (build stage)."
|
||||
|
||||
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
|
||||
# npm ci richiede package-lock.json (deterministico). Fallback a install se assente.
|
||||
# Servono le devDependencies (Vite, TypeScript) per compilare: NON usare --omit=dev.
|
||||
RUN if [ -f package-lock.json ]; then npm ci; else npm install; fi
|
||||
|
||||
# Copie esplicite (no `COPY . .` — docker:S6470): non sovrascrivere i node_modules
|
||||
# musl appena installati con quelli glibc dell'host, né copiare segreti/file locali.
|
||||
COPY vite.config.ts tsconfig.json index.html ./
|
||||
COPY src/ ./src/
|
||||
COPY public/ ./public/
|
||||
RUN npm run build
|
||||
|
||||
# --- STAGE 3: Produzione (Nginx stable, patchato) ---
|
||||
FROM nginx:1.30-alpine AS production
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
COPY --from=build /app/dist /usr/share/nginx/html
|
||||
|
||||
EXPOSE 80
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
11
frontend/index.html
Normal file
11
frontend/index.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=3.0">
|
||||
<title>Dynamic Collection - Index</title>
|
||||
</head>
|
||||
<body>
|
||||
<script type="module" src="./src/pages/index/index.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
70
frontend/nginx.conf
Normal file
70
frontend/nginx.conf
Normal file
@@ -0,0 +1,70 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# URL puliti — /mappa trova mappa.html
|
||||
location / {
|
||||
try_files $uri $uri.html $uri/ =404;
|
||||
}
|
||||
|
||||
# Pagine "scheda" con slug nel path: /<page>/<slug> -> <page>.html
|
||||
# Lo slug resta leggibile lato client da location.pathname.
|
||||
# Elencare qui le pagine path-style reali quando esistono (deve esistere il relativo .html).
|
||||
# Esempio:
|
||||
# location ~ ^/(?<page>artifact|institution)/[^/]+/?$ {
|
||||
# try_files /$page.html =404;
|
||||
# }
|
||||
|
||||
# Proxy per API: inoltra /api/* al backend
|
||||
location /api/ {
|
||||
proxy_pass http://backend:8000/api/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
# Documentazione utente (MkDocs)
|
||||
location /documentation/ {
|
||||
proxy_pass http://docs:8000/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
# Documentazione OpenAPI (Scramble) — /api-docs/* → backend /docs/*
|
||||
location /api-docs/ {
|
||||
rewrite ^/api-docs/(.*)$ /docs/$1 break;
|
||||
proxy_pass http://backend:8000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
# Cache aggressiva per asset con hash (Vite li genera con hash nel nome)
|
||||
location ~* \.(js|css|woff2|png|jpg|ico|svg)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
# Blocca file sensibili
|
||||
location ~ /\. {
|
||||
deny all;
|
||||
}
|
||||
|
||||
# Storage per file statici
|
||||
location /storage/ {
|
||||
alias /usr/share/nginx/html/storage/;
|
||||
access_log off;
|
||||
expires 30d;
|
||||
}
|
||||
|
||||
access_log /dev/stdout;
|
||||
error_log /dev/stderr warn;
|
||||
}
|
||||
2481
frontend/package-lock.json
generated
Normal file
2481
frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
30
frontend/package.json
Normal file
30
frontend/package.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "dyncoll-frontend",
|
||||
"private": true,
|
||||
"version": "3.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"type-check": "tsc --noEmit",
|
||||
"test": "vitest run --coverage",
|
||||
"test:watch": "vitest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/vite": "^4.3.1",
|
||||
"@types/leaflet": "^1.9.21",
|
||||
"@types/node": "^25.9.3",
|
||||
"@vitest/coverage-v8": "^4.1.8",
|
||||
"daisyui": "^5.5.23",
|
||||
"rollup-plugin-visualizer": "^7.0.1",
|
||||
"tailwindcss": "^4.3.1",
|
||||
"typescript": "^6.0.3",
|
||||
"vite": "^8.0.16",
|
||||
"vitest": "^4.1.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.17.0",
|
||||
"leaflet": "^1.9.4"
|
||||
}
|
||||
}
|
||||
216
frontend/public/vendor/3dhop/corto.em.js
vendored
Normal file
216
frontend/public/vendor/3dhop/corto.em.js
vendored
Normal file
File diff suppressed because one or more lines are too long
961
frontend/public/vendor/3dhop/meco.js
vendored
Normal file
961
frontend/public/vendor/3dhop/meco.js
vendored
Normal file
@@ -0,0 +1,961 @@
|
||||
/*
|
||||
Nexus
|
||||
Copyright (c) 2012-2020, Visual Computing Lab, ISTI - CNR
|
||||
All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
onmessage = function(job) {
|
||||
if(typeof(job.data) == "string") return;
|
||||
var node = job.data.node;
|
||||
var signature = job.data.signature;
|
||||
var patches = job.data.patches;
|
||||
// var now =new Date().getTime();
|
||||
|
||||
var size;
|
||||
if(!node.buffer) return;
|
||||
else size = node.buffer.byteLength;
|
||||
var buffer;
|
||||
for(var i =0 ; i < 1; i++) {
|
||||
var coder = new MeshCoder(signature, node, patches);
|
||||
buffer = coder.decode(node.buffer);
|
||||
}
|
||||
node.buffer = buffer;
|
||||
node.owner = job.owner;
|
||||
// var elapsed = new Date().getTime() - now;
|
||||
// var t = node.nface;
|
||||
// console.log("Z Time: " + elapsed + " Size: " + size + " KT/s " + (t/(elapsed)) + " Mbps " + (8*1000*node.buffer.byteLength/elapsed)/(1<<20));
|
||||
postMessage(node);
|
||||
}
|
||||
|
||||
// actually bitstreams expects a little endian uin64 type. convert it to 2 uint32
|
||||
|
||||
BitStream = function(array) {
|
||||
this.a = array;
|
||||
for(var i = 0; i < array.length; i += 2) {
|
||||
var s = array[i];
|
||||
array[i] = array[i+1];
|
||||
array[i+1] = s;
|
||||
}
|
||||
this.position = 0;
|
||||
this.bitsPending = 0;
|
||||
};
|
||||
|
||||
BitStream.prototype = {
|
||||
read: function(bits) {
|
||||
var bitBuffer = 0;
|
||||
while(bits > 0) {
|
||||
var partial;
|
||||
var bitsConsumed;
|
||||
if (this.bitsPending > 0) {
|
||||
var byte = (this.a[this.position - 1] & (0xffffffff >>> (32 - this.bitsPending)))>>>0;
|
||||
bitsConsumed = Math.min(this.bitsPending, bits);
|
||||
this.bitsPending -= bitsConsumed;
|
||||
partial = byte >>> this.bitsPending;
|
||||
} else {
|
||||
bitsConsumed = Math.min(32, bits);
|
||||
this.bitsPending = 32 - bitsConsumed;
|
||||
partial = this.a[this.position++] >>> this.bitsPending;
|
||||
}
|
||||
bits -= bitsConsumed;
|
||||
bitBuffer = ((bitBuffer << bitsConsumed) | partial)>>>0;
|
||||
}
|
||||
return bitBuffer;
|
||||
},
|
||||
replace: function(bits, value) {
|
||||
//zero last part
|
||||
value = (value & (0xffffffff >>> 32 - bits)) >>> 0;
|
||||
value = (value | read(bits)) >>> 0;
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
Stream = function(buffer) {
|
||||
this.data = buffer;
|
||||
this.buffer = new Uint8Array(buffer);
|
||||
this.pos = 0;
|
||||
}
|
||||
|
||||
Stream.prototype = {
|
||||
readChar: function() {
|
||||
var c = this.buffer[this.pos++];
|
||||
if(c > 127) c -= 256;
|
||||
return c;
|
||||
},
|
||||
readUChar: function() {
|
||||
return this.buffer[this.pos++];
|
||||
},
|
||||
readInt: function() {
|
||||
var c = this.buffer[this.pos + 3]
|
||||
c <<= 8;
|
||||
c |= this.buffer[this.pos + 2];
|
||||
c <<= 8;
|
||||
c |= this.buffer[this.pos + 1];
|
||||
c <<= 8;
|
||||
c |= this.buffer[this.pos + 0];
|
||||
this.pos += 4;
|
||||
return c;
|
||||
},
|
||||
readArray: function(n) {
|
||||
var a = this.buffer.subarray(this.pos, this.pos+n);
|
||||
this.pos += n;
|
||||
return a;
|
||||
},
|
||||
readBitStream:function() {
|
||||
var n = this.readInt();
|
||||
var pad = this.pos & 0x3;
|
||||
if(pad != 0)
|
||||
this.pos += 4 - pad;
|
||||
var b = new BitStream(new Uint32Array(this.data, this.pos, n*2));
|
||||
this.pos += n*8;
|
||||
return b;
|
||||
}
|
||||
};
|
||||
|
||||
function Tunstall(wordsize, lookup_size) {
|
||||
this.wordsize = wordsize? wordsize : 8;
|
||||
this.lookup_size = lookup_size? lookup_size : 8;
|
||||
}
|
||||
|
||||
Tunstall.prototype = {
|
||||
decompress: function(stream) {
|
||||
var nsymbols = stream.readUChar();
|
||||
this.probabilities = stream.readArray(nsymbols*2);
|
||||
this.createDecodingTables();
|
||||
var size = stream.readInt();
|
||||
var data = new Uint8Array(size);
|
||||
var compressed_size = stream.readInt();
|
||||
var compressed_data = stream.readArray(compressed_size);
|
||||
if(size)
|
||||
this._decompress(compressed_data, compressed_size, data, size);
|
||||
return data;
|
||||
},
|
||||
|
||||
createDecodingTables: function() {
|
||||
//read symbol,prob,symbol,prob as uchar.
|
||||
//Here probabilities will range from 0 to 0xffff for better precision
|
||||
|
||||
var n_symbols = this.probabilities.length/2;
|
||||
if(n_symbols <= 1) return;
|
||||
|
||||
var queues = []; //array of arrays
|
||||
var buffer = [];
|
||||
|
||||
//initialize adding all symbols to queues
|
||||
for(var i = 0; i < n_symbols; i++) {
|
||||
var symbol = this.probabilities[i*2];
|
||||
var s = [(this.probabilities[i*2+1])<<8, buffer.length, 1]; //probability, position in the buffer, length
|
||||
queues[i] = [s];
|
||||
buffer.push(this.probabilities[i*2]); //symbol
|
||||
}
|
||||
var dictionary_size = 1<<this.wordsize;
|
||||
var n_words = n_symbols;
|
||||
var table_length = n_symbols;
|
||||
|
||||
//at each step we grow all queues using the most probable sequence
|
||||
while(n_words < dictionary_size - n_symbols +1) {
|
||||
//Should use a stack or something to be faster, but we have few symbols
|
||||
//find highest probability word
|
||||
var best = 0;
|
||||
var max_prob = 0;
|
||||
for(var i = 0; i < n_symbols; i++) {
|
||||
var p = queues[i][0][0]; //front of queue probability.
|
||||
if(p > max_prob) {
|
||||
best = i;
|
||||
max_prob = p;
|
||||
}
|
||||
}
|
||||
var symbol = queues[best][0];
|
||||
var pos = buffer.length;
|
||||
|
||||
for(var i = 0; i < n_symbols; i++) {
|
||||
var sym = this.probabilities[i*2];
|
||||
var prob = this.probabilities[i*2+1]<<8;
|
||||
var s = [((prob*symbol[0])>>>16), pos, symbol[2]+1]; //combine probabilities, keep track of buffer, keep length of queue
|
||||
|
||||
for(var k = 0; k < symbol[2]; k++)
|
||||
buffer[pos+k] = buffer[symbol[1] + k]; //copy sequence of symbols
|
||||
|
||||
pos += symbol[2];
|
||||
buffer[pos++] = sym; //append symbol
|
||||
queues[i].push(s);
|
||||
}
|
||||
table_length += (n_symbols-1)*(symbol[2] + 1) +1;
|
||||
n_words += n_symbols -1;
|
||||
queues[best].shift(); //remove first thing
|
||||
}
|
||||
|
||||
this.index = new Uint32Array(n_words);
|
||||
this.lengths = new Uint32Array(n_words);
|
||||
this.table = new Uint8Array(table_length);
|
||||
var word = 0;
|
||||
var pos = 0;
|
||||
for(i = 0; i < queues.length; i++) {
|
||||
var queue = queues[i];
|
||||
for(var k = 0; k < queue.length; k++) {
|
||||
var s = queue[k];
|
||||
this.index[word] = pos;
|
||||
this.lengths[word] = s[2]; //length
|
||||
word++;
|
||||
|
||||
for(var j = 0; j < s[2]; j++)
|
||||
this.table[pos + j] = buffer[s[1] + j]; //buffer of offset
|
||||
pos += s[2]; //length
|
||||
}
|
||||
}
|
||||
},
|
||||
_decompress: function(input, input_size, output, output_size) {
|
||||
var input_pos = 0;
|
||||
var output_pos = 0;
|
||||
if(this.probabilities.length == 2) {
|
||||
var symbol = this.probabilities[0];
|
||||
for(var i = 0; i < output_size; i++)
|
||||
output[i] = symbol;
|
||||
return;
|
||||
}
|
||||
|
||||
while(input_pos < input_size-1) {
|
||||
var symbol = input[input_pos++];
|
||||
var start = this.index[symbol];
|
||||
var end = start + this.lengths[symbol];
|
||||
for(var i = start; i < end; i++)
|
||||
output[output_pos++] = this.table[i];
|
||||
}
|
||||
|
||||
//last symbol might override so we check.
|
||||
var symbol = input[input_pos];
|
||||
var start = this.index[symbol];
|
||||
var end = start + output_size - output_pos;
|
||||
var length = output_size - output_pos;
|
||||
for(var i = start; i < end; i++)
|
||||
output[output_pos++] = this.table[i];
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
ZPoint = function(h, l) {
|
||||
this.lo = l;
|
||||
this.hi = h;
|
||||
}
|
||||
|
||||
ZPoint.prototype = {
|
||||
copy: function(z) {
|
||||
this.lo = z.lo;
|
||||
this.hi = z.hi;
|
||||
},
|
||||
setBit: function(d) {
|
||||
if(d < 32)
|
||||
this.lo = (this.lo | (1<<d))>>>0;
|
||||
else
|
||||
this.hi = (this.hi | (1<<(d-32)))>>>0;
|
||||
},
|
||||
toPoint: function(min, step, buffer, pos) {
|
||||
var x = this.morton3(this.lo, this.hi>>>1);
|
||||
var y = this.morton3(this.lo>>>1, this.hi>>>2);
|
||||
var z = this.morton3((this.lo>>>2 | (this.hi & 0x1)<<30 )>>>0, this.hi>>>3); //first hi bit needs to go into low.
|
||||
|
||||
buffer[pos+0] = (x + min[0])*step;
|
||||
buffer[pos+1] = (y + min[1])*step;
|
||||
buffer[pos+2] = (z + min[2])*step;
|
||||
},
|
||||
morton3: function(lo, hi) {
|
||||
lo = ( lo & 0x49249249)>>>0;
|
||||
lo = ((lo | (lo >>> 2 )) & 0xc30c30c3)>>>0;
|
||||
lo = ((lo | (lo >>> 4 )) & 0x0f00f00f)>>>0;
|
||||
lo = ((lo | (lo >>> 8 )) & 0xff0000ff)>>>0;
|
||||
lo = ((lo | (lo >>> 16)) & 0x0000ffff)>>>0;
|
||||
|
||||
hi = ( hi & 0x49249249)>>>0;
|
||||
hi = ((hi | (hi >> 2 )) & 0xc30c30c3)>>>0;
|
||||
hi = ((hi | (hi >> 4 )) & 0x0f00f00f)>>>0;
|
||||
hi = ((hi | (hi >> 8 )) & 0xff0000ff)>>>0;
|
||||
hi = ((hi | (hi >> 16)) & 0x0000ffff)>>>0;
|
||||
|
||||
return ((hi<<11) | lo)>>>0;
|
||||
}
|
||||
};
|
||||
|
||||
//node is an object with nvert, nface
|
||||
//patches is an array of offsets in the index, triangle are grouped by those offsets
|
||||
//signature tells wether mesh has indices, normals, colors, etc. {'colors': true, 'normals':true, 'indices': true }
|
||||
|
||||
function MeshCoder(signature, node, patches) {
|
||||
this.sig = signature;
|
||||
this.node = node;
|
||||
this.patches = patches;
|
||||
|
||||
this.last = new Int32Array(this.node.nvert);
|
||||
this.last_count = 0;
|
||||
}
|
||||
|
||||
MeshCoder.prototype = {
|
||||
//assumes input is an ArrayBuffer
|
||||
decode: function(input) {
|
||||
var t = this;
|
||||
|
||||
t.buffer = new ArrayBuffer(t.node.nvert*(12 + t.sig.texcoords*8 + t.sig.normals*6 + t.sig.colors*4) + t.node.nface*t.sig.indices*6);
|
||||
|
||||
var size = t.node.nvert*12; //float
|
||||
t.coords = new Float32Array(t.buffer, 0, t.node.nvert*3);
|
||||
|
||||
if(t.sig.texcoords) {
|
||||
t.texcoords = new Float32Array(t.buffer, size, t.node.nvert*2);
|
||||
size += t.node.nvert*8; //float
|
||||
}
|
||||
if(t.sig.normals) {
|
||||
t.normals = new Int16Array(t.buffer, size, t.node.nvert*3);
|
||||
size += t.node.nvert*6; //short
|
||||
}
|
||||
if(t.sig.colors) {
|
||||
t.colors = new Uint8ClampedArray(t.buffer, size, t.node.nvert*4);
|
||||
size += t.node.nvert*4; //chars
|
||||
}
|
||||
if(t.sig.indices) {
|
||||
t.faces = new Uint16Array(t.buffer, size, t.node.nface*3);
|
||||
size += t.node.nface*6; //short
|
||||
}
|
||||
|
||||
t.stream = new Stream(input);
|
||||
|
||||
t.stack = new Float32Array(12); //min0, min1, min2, step, tmin0, tmin1, tstep
|
||||
|
||||
t.stack[3] = t.stream.readInt();
|
||||
t.stack[4] = t.stream.readInt();
|
||||
t.stack[5] = t.stream.readInt();
|
||||
|
||||
t.coord_q = t.stream.readChar();
|
||||
t.coord_bits = t.stream.readChar()*3;
|
||||
|
||||
t.stack[6] = Math.pow(2.0, t.coord_q);
|
||||
|
||||
if(t.sig.texcoords) {
|
||||
t.stack[9] = t.stream.readInt();
|
||||
t.stack[10] = t.stream.readInt();
|
||||
|
||||
t.texcoord_q = t.stream.readChar();
|
||||
t.texcoord_bits = t.stream.readChar()*2;
|
||||
t.stack[11] = Math.pow(2.0, t.texcoord_q);
|
||||
}
|
||||
|
||||
if(t.sig.indices) {
|
||||
t.decodeFaces();
|
||||
|
||||
// var faces = window.performance.now() - start;
|
||||
// start += faces;
|
||||
} else {
|
||||
t.decodeCoordinates();
|
||||
|
||||
// var coords = window.performance.now() - start;
|
||||
// start += coords;
|
||||
}
|
||||
|
||||
if(t.sig.normals)
|
||||
t.decodeNormals();
|
||||
// var normals = window.performance.now() - start;
|
||||
// start += normals;
|
||||
if(t.sig.colors)
|
||||
t.decodeColors();
|
||||
// var colors = window.performance.now() - start;
|
||||
// start += colors;
|
||||
// console.log("Decode " + (faces + coords + normals + colors) + "ms. C: " + coords + " F: " + faces + " N: " + normals + " C: " + colors);
|
||||
|
||||
return t.buffer;
|
||||
},
|
||||
|
||||
decodeCoordinates: function() {
|
||||
var t = this;
|
||||
t.min = [t.stack[3], t.stack[4], t.stack[5]];
|
||||
|
||||
var step = Math.pow(2.0, t.coord_q);
|
||||
|
||||
var hi_bits = Math.max(t.coord_bits - 32, 0);
|
||||
var lo_bits = Math.min(t.coord_bits, 32);
|
||||
|
||||
var bitstream = t.stream.readBitStream();
|
||||
|
||||
var tunstall = new Tunstall;
|
||||
var diffs = tunstall.decompress(t.stream);
|
||||
|
||||
var hi = bitstream.read(hi_bits);
|
||||
var lo = bitstream.read(lo_bits);
|
||||
var p = new ZPoint(hi, lo);
|
||||
var count = 0;
|
||||
p.toPoint(t.min, step, t.coords, count);
|
||||
count += 3;
|
||||
for(var i = 1; i < t.node.nvert; i++) {
|
||||
var d = diffs[i-1];
|
||||
p.setBit(d, 1);
|
||||
if(d > 32) {
|
||||
p.hi = (p.hi & ~((1<<(d-32))-1))>>>0;
|
||||
var e = bitstream.read(d - 32);
|
||||
p.hi = (p.hi | e)>>>0;
|
||||
p.lo = bitstream.read(32);
|
||||
} else {
|
||||
|
||||
if(d == 32) {
|
||||
p.lo = bitstream.read(d);
|
||||
} else {
|
||||
var e = bitstream.read(d);
|
||||
p.lo = (p.lo & ~((1<<d) -1))>>>0;
|
||||
p.lo = (p.lo | e)>>>0;
|
||||
}
|
||||
}
|
||||
p.toPoint(t.min, step, t.coords, count);
|
||||
count += 3;
|
||||
}
|
||||
},
|
||||
|
||||
decodeFaces: function() {
|
||||
if(!this.node.nface) return;
|
||||
|
||||
this.vertex_count = 0;
|
||||
var start = 0;
|
||||
for(var p = 0; p < this.patches.length; p++) {
|
||||
var end = this.patches[p];
|
||||
this.decodeConnectivity(end - start, start*3);
|
||||
start = end;
|
||||
}
|
||||
//dequantize positions
|
||||
var tot = this.node.nvert*3;
|
||||
var coords = this.coords;
|
||||
var stack = this.stack;
|
||||
for(var i = 0; i < tot; ) {
|
||||
coords[i] = (coords[i] + stack[3])*stack[6]; i++;
|
||||
coords[i] = (coords[i] + stack[4])*stack[6]; i++;
|
||||
coords[i] = (coords[i] + stack[5])*stack[6]; i++;
|
||||
}
|
||||
if(this.sig.texcoords) {
|
||||
var t_tot = this.node.nvert*2;
|
||||
var t_coords = this.texcoords;
|
||||
for(var i = 0; i < tot; ) {
|
||||
t_coords[i] = (t_coords[i] + stack[9])*stack[11]; i++;
|
||||
t_coords[i] = (t_coords[i] + stack[10])*stack[11]; i++;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
decodeNormals: function() {
|
||||
var norm_q = this.stream.readChar();
|
||||
|
||||
var dtunstall = new Tunstall;
|
||||
var diffs = dtunstall.decompress(this.stream);
|
||||
|
||||
var stunstall = new Tunstall;
|
||||
var signs = stunstall.decompress(this.stream);
|
||||
var bitstream = this.stream.readBitStream();
|
||||
|
||||
var side = (1<<(16 - norm_q))>>>0;
|
||||
var diffcount = 0;
|
||||
var signcount = 0;
|
||||
|
||||
if(!this.sig.indices) {
|
||||
for(var k = 0; k < 2; k++) {
|
||||
var on = 0;
|
||||
for(var i = 0; i < this.node.nvert; i++) {
|
||||
var d = this.decodeDiff(diffs[diffcount++], bitstream);
|
||||
on = on + d;
|
||||
this.normals[3*i + k] = on*side;
|
||||
}
|
||||
}
|
||||
for(var i = 0; i < this.node.nvert; i++) {
|
||||
var offset = i*3;
|
||||
var x = this.normals[offset + 0];
|
||||
var y = this.normals[offset + 1];
|
||||
var z = 32767.0*32767.0 - x*x - y*y;
|
||||
|
||||
if(z < 0) z = 0;
|
||||
z = Math.sqrt(z);
|
||||
if(z > 32767) z = 32767;
|
||||
if(signs[i] == 0)
|
||||
z = -z;
|
||||
this.normals[offset + 2] = z;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var boundary = this.markBoundary();
|
||||
this.computeNormals();
|
||||
|
||||
if(this.sig.texcoords) //hack, fixing normals makes it worse actually
|
||||
return;
|
||||
|
||||
var stat = 0;
|
||||
//get difference between original and predicted
|
||||
for(var i = 0; i < this.node.nvert; i++) {
|
||||
if(!boundary[i]) continue;
|
||||
var offset = i*3;
|
||||
var x = (this.normals[offset + 0]/side);
|
||||
var y = (this.normals[offset + 1]/side);
|
||||
var dx = this.decodeDiff(diffs[diffcount++], bitstream);
|
||||
var dy = this.decodeDiff(diffs[diffcount++], bitstream);
|
||||
x = (x + dx)*side;
|
||||
y = (y + dy)*side;
|
||||
|
||||
var z = 32767.0*32767.0 - x*x - y*y;
|
||||
|
||||
if(z < 0) z = 0;
|
||||
z = Math.sqrt(z);
|
||||
//sign
|
||||
if(z > 32767.0) z = 32767.0;
|
||||
var signbit = signs[signcount++];
|
||||
// if(this.normals[offset+2] < 0 != signbit)
|
||||
if((this.normals[offset+2] < 0 && signbit == 0) || (this.normals[offset+2] > 0 && signbit == 1))
|
||||
z = -z;
|
||||
this.normals[offset + 0] = x;
|
||||
this.normals[offset + 1] = y;
|
||||
this.normals[offset + 2] = z;
|
||||
}
|
||||
},
|
||||
|
||||
decodeColors: function() {
|
||||
var color_q = [];
|
||||
for(var k = 0; k < 4; k++)
|
||||
color_q[k] = this.stream.readChar();
|
||||
|
||||
var diffs = [];
|
||||
for(var k = 0; k < 4; k++) {
|
||||
var tunstall = new Tunstall;;
|
||||
diffs[k] = tunstall.decompress(this.stream);
|
||||
}
|
||||
var bitstream = this.stream.readBitStream();
|
||||
|
||||
var count = 0;
|
||||
if(this.sig.indices) {
|
||||
for(var i = 0; i < this.node.nvert; i++) {
|
||||
var last = this.last[i]*4;
|
||||
var offset = i*4;
|
||||
|
||||
for(var k = 0; k < 4; k++) {
|
||||
var c = this.decodeDiff(diffs[k][count], bitstream);
|
||||
|
||||
if(last >= 0)
|
||||
c += this.colors[last + k];
|
||||
this.colors[offset] = c;
|
||||
offset++;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
} else {
|
||||
for(var k = 0; k < 4; k++)
|
||||
this.colors[k] = this.decodeDiff(diffs[k][count], bitstream);
|
||||
count++;
|
||||
|
||||
var offset = 4;
|
||||
for(var i = 1; i < this.node.nvert; i++) {
|
||||
for(var k = 0; k < 4; k++) {
|
||||
var d = this.decodeDiff(diffs[k][count], bitstream);
|
||||
this.colors[offset] = this.colors[offset-4] + d;
|
||||
offset ++;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
var steps = [];
|
||||
for(var k = 0; k < 4; k++)
|
||||
steps[k] = (1<<(8 - color_q[k]));
|
||||
|
||||
//convert to rgb
|
||||
for(var i = 0; i < this.node.nvert; i++) {
|
||||
var offset = i*4;
|
||||
|
||||
var e0 = this.colors[offset + 0] * steps[0];
|
||||
var e1 = this.colors[offset + 1] * steps[1];
|
||||
var e2 = this.colors[offset + 2] * steps[2];
|
||||
var e3 = this.colors[offset + 3] * steps[3];
|
||||
|
||||
this.colors[offset + 0] = (e2 + e0)&0xff;
|
||||
this.colors[offset + 1] = e0;
|
||||
this.colors[offset + 2] = (e1 + e0)&0xff;
|
||||
this.colors[offset + 3] = e3;
|
||||
}
|
||||
},
|
||||
|
||||
//how to determine if a vertex is a boundary without topology:
|
||||
//for each edge a vertex is in, add or subtract the id of the other vertex depending on order
|
||||
//for internal vertices sum is zero.
|
||||
//unless we have strange configurations and a lot of sfiga, zero wont happen. //TODO think about this
|
||||
markBoundary: function() {
|
||||
// var boundary = new Uint8Array(this.node.nvert);
|
||||
var count = new Uint32Array(this.node.nvert);
|
||||
|
||||
var offset = 0;
|
||||
for(var i = 0; i < this.node.nface; i++) {
|
||||
count[this.faces[offset + 0]] += this.faces[offset + 1] - this.faces[offset + 2];
|
||||
count[this.faces[offset + 1]] += this.faces[offset + 2] - this.faces[offset + 0];
|
||||
count[this.faces[offset + 2]] += this.faces[offset + 0] - this.faces[offset + 1];
|
||||
offset += 3;
|
||||
}
|
||||
return count;
|
||||
// for(var i = 0; i < this.node.nvert; i++)
|
||||
// if(count[i] != 0)
|
||||
// boundary[i] = true;
|
||||
// return boundary;
|
||||
},
|
||||
|
||||
norm: function(buffer, a, b, c) { //a b c offsets in the buffer
|
||||
var ba0 = buffer[b+0] - buffer[a+0];
|
||||
var ba1 = buffer[b+1] - buffer[a+1];
|
||||
var ba2 = buffer[b+2] - buffer[a+2];
|
||||
|
||||
var ca0 = buffer[c+0] - buffer[a+0];
|
||||
var ca1 = buffer[c+1] - buffer[a+1];
|
||||
var ca2 = buffer[c+2] - buffer[a+2];
|
||||
|
||||
var p = [];
|
||||
p[0] = ba1*ca2 - ba2*ca1;
|
||||
p[1] = ba2*ca0 - ba0*ca2;
|
||||
p[2] = ba0*ca1 - ba1*ca0;
|
||||
return p;
|
||||
},
|
||||
|
||||
normalize: function(buffer, offset) {
|
||||
var x = buffer[offset + 0];
|
||||
var y = buffer[offset + 1];
|
||||
var z = buffer[offset + 2];
|
||||
var n = Math.sqrt(x*x + y*y + z*z);
|
||||
if(n > 0) {
|
||||
buffer[offset + 0] = x/n;
|
||||
buffer[offset + 1] = y/n;
|
||||
buffer[offset + 2] = z/n;
|
||||
}
|
||||
},
|
||||
|
||||
computeNormals:function() {
|
||||
var tmp_normals = new Float32Array(this.node.nvert*3);
|
||||
|
||||
var offset = 0;
|
||||
for(var i = 0; i < this.node.nface; i++) {
|
||||
var a = 3*this.faces[offset + 0];
|
||||
var b = 3*this.faces[offset + 1];
|
||||
var c = 3*this.faces[offset + 2];
|
||||
|
||||
var buffer = this.coords;
|
||||
var ba0 = buffer[b+0] - buffer[a+0];
|
||||
var ba1 = buffer[b+1] - buffer[a+1];
|
||||
var ba2 = buffer[b+2] - buffer[a+2];
|
||||
|
||||
var ca0 = buffer[c+0] - buffer[a+0];
|
||||
var ca1 = buffer[c+1] - buffer[a+1];
|
||||
var ca2 = buffer[c+2] - buffer[a+2];
|
||||
|
||||
var n0 = ba1*ca2 - ba2*ca1;
|
||||
var n1 = ba2*ca0 - ba0*ca2;
|
||||
var n2 = ba0*ca1 - ba1*ca0;
|
||||
|
||||
tmp_normals[a + 0] += n0;
|
||||
tmp_normals[a + 1] += n1;
|
||||
tmp_normals[a + 2] += n2;
|
||||
tmp_normals[b + 0] += n0;
|
||||
tmp_normals[b + 1] += n1;
|
||||
tmp_normals[b + 2] += n2;
|
||||
tmp_normals[c + 0] += n0;
|
||||
tmp_normals[c + 1] += n1;
|
||||
tmp_normals[c + 2] += n2;
|
||||
offset += 3;
|
||||
}
|
||||
|
||||
//normalize
|
||||
var offset = 0;
|
||||
for(var i = 0; i < this.node.nvert; i++) {
|
||||
var x = tmp_normals[offset + 0];
|
||||
var y = tmp_normals[offset + 1];
|
||||
var z = tmp_normals[offset + 2];
|
||||
var n = Math.sqrt(x*x + y*y + z*z);
|
||||
if(n > 0) {
|
||||
tmp_normals[offset + 0] = x/n;
|
||||
tmp_normals[offset + 1] = y/n;
|
||||
tmp_normals[offset + 2] = z/n;
|
||||
}
|
||||
this.normals[offset + 0] = tmp_normals[offset + 0]*32767;
|
||||
this.normals[offset + 1] = tmp_normals[offset + 1]*32767;
|
||||
this.normals[offset + 2] = tmp_normals[offset + 2]*32767;
|
||||
offset += 3;
|
||||
}
|
||||
},
|
||||
|
||||
decodeDiff: function(diff, bitstream) {
|
||||
var val;
|
||||
if(diff == 0) {
|
||||
val = 1;
|
||||
} else {
|
||||
val = 1<<(diff);
|
||||
val |= bitstream.read(diff);
|
||||
};
|
||||
val--; //vall is always >= 1
|
||||
if(val & 0x1)
|
||||
val = -((val+1)>>1);
|
||||
else
|
||||
val = val>>1;
|
||||
return val;
|
||||
},
|
||||
|
||||
/* an edge is: uint16_t face, uint16_t side, uint32_t prev, next, bool deleted
|
||||
I do not want to create millions of small objects, I will use aUint32Array.
|
||||
Problem is how long, sqrt(nface) we will over blow using nface.
|
||||
*/
|
||||
|
||||
decodeConnectivity: function(length, start) {
|
||||
|
||||
var t = this;
|
||||
var ctunstall = new Tunstall;
|
||||
var clers = ctunstall.decompress(this.stream);
|
||||
var cler_count = 0;
|
||||
|
||||
var dtunstall = new Tunstall;
|
||||
var diffs = dtunstall.decompress(this.stream);
|
||||
var diff_count = 0;
|
||||
|
||||
var tdiffs;
|
||||
var tdiff_count = 0;
|
||||
if(t.sig.texcoords) {
|
||||
var ttunstall = new Tunstall;
|
||||
tdiffs = ttunstall.decompress(this.stream);
|
||||
}
|
||||
|
||||
var bitstream = this.stream.readBitStream(bitstream);
|
||||
|
||||
var current_face = 0; //keep track of connected component start
|
||||
//t.vertex_count = 0;
|
||||
var front = new Uint32Array(this.node.nface*18);
|
||||
var front_count = 0; //count each integer so it's front_back*5
|
||||
function addFront(_v0, _v1, _v2, _prev, _next) {
|
||||
front[front_count++] = _v0;
|
||||
front[front_count++] = _v1;
|
||||
front[front_count++] = _v2;
|
||||
front[front_count++] = _prev;
|
||||
front[front_count++] = _next;
|
||||
front[front_count++] = 0; //deleted
|
||||
}
|
||||
function _next(t) {
|
||||
t++;
|
||||
if(t == 3) t = 0;
|
||||
return t;
|
||||
}
|
||||
function _prev(t) {
|
||||
t--;
|
||||
if(t == -1) t = 2;
|
||||
return t;
|
||||
}
|
||||
|
||||
var delayed = [];
|
||||
var faceorder = [];
|
||||
|
||||
var faces_count = start; //count indices in this.faces array
|
||||
var totfaces = length;
|
||||
// var estimated = [0, 0, 0]; //no! use stack.
|
||||
var stack = this.stack;
|
||||
var coords = this.coords;
|
||||
var texcoords = this.texcoords;
|
||||
var hasTexCoords = t.sig.texcoords;
|
||||
|
||||
while(totfaces > 0) {
|
||||
if(!faceorder.length && !delayed.length) {
|
||||
if(current_face == this.node.nface) break; //no more faces to encode exiting
|
||||
|
||||
stack[0] = stack[1] = stack[2] = 0;
|
||||
stack[7] = stack[8] = 0; //texcoords
|
||||
var last_index = -1;
|
||||
var index = [];
|
||||
for(var k = 0; k < 3; k++) {
|
||||
this.last[this.last_count++] = last_index;
|
||||
var diff = diffs[diff_count++];
|
||||
var tdiff = diff && hasTexCoords? tdiffs[tdiff_count++] : 0;
|
||||
var v = this.decodeVertex(bitstream, diff, tdiff);
|
||||
index[k] = v;
|
||||
this.faces[faces_count++] = v;
|
||||
stack[0] = coords[v*3];
|
||||
stack[1] = coords[v*3+1];
|
||||
stack[2] = coords[v*3+2];
|
||||
if(t.sig.texcoords) {
|
||||
stack[7] = texcoords[v*2];
|
||||
stack[8] = texcoords[v*2+1];
|
||||
}
|
||||
last_index = v;
|
||||
}
|
||||
var current_edge = front_count;
|
||||
for(var k = 0; k < 3; k++) {
|
||||
faceorder.push(front_count);
|
||||
front[front_count++] = index[_next(k)];
|
||||
front[front_count++] = index[_prev(k)];
|
||||
front[front_count++] = index[k];
|
||||
front[front_count++] = current_edge + _prev(k)*6;
|
||||
front[front_count++] = current_edge + _next(k)*6;
|
||||
front_count++;
|
||||
// addFront(index[_next(k)], index[_prev(k)], index[k], current_edge + _prev(k)*6, current_edge + _next(k)*6);
|
||||
}
|
||||
current_face++;
|
||||
totfaces--;
|
||||
continue;
|
||||
}
|
||||
var f;
|
||||
if(faceorder.length)
|
||||
f = faceorder.shift();
|
||||
else
|
||||
f = delayed.pop();
|
||||
|
||||
var edge_start = f;
|
||||
|
||||
if(front[edge_start + 5]) continue; //deleted
|
||||
front[edge_start + 5] = 1; //set edge as deleted anyway
|
||||
|
||||
var c = clers[cler_count++];
|
||||
if(c == 4) continue; //BOUNDARY
|
||||
|
||||
var v0 = front[edge_start + 0];
|
||||
var v1 = front[edge_start + 1];
|
||||
var v2 = front[edge_start + 2];
|
||||
var prev = front[edge_start + 3];
|
||||
var next = front[edge_start + 4];
|
||||
|
||||
var first_edge = front_count; //points to new edge to be inserted
|
||||
var opposite = -1;
|
||||
if(c == 0) { //VERTEX
|
||||
//predict position based on v0, v1 and v2
|
||||
for(var k = 0; k < 3; k++)
|
||||
stack[k] = coords[v0*3 + k] + coords[v1*3 + k] - coords[v2*3 + k];
|
||||
|
||||
if(hasTexCoords)
|
||||
for(var k = 0; k < 2; k++)
|
||||
stack[7+k] = texcoords[v0*2 + k] + texcoords[v1*2 + k] - texcoords[v2*2 + k];
|
||||
|
||||
var diff = diffs[diff_count++];
|
||||
var tdiff = diff && hasTexCoords? tdiffs[tdiff_count++] : 0;
|
||||
opposite = this.decodeVertex(bitstream, diff, tdiff);
|
||||
if(diff != 0)
|
||||
this.last[this.last_count++] = v1;
|
||||
|
||||
front[prev + 4] = first_edge;
|
||||
front[next + 3] = first_edge + 6;
|
||||
faceorder.unshift(front_count);
|
||||
|
||||
front[front_count++] = v0;
|
||||
front[front_count++] = opposite;
|
||||
front[front_count++] = v1;
|
||||
front[front_count++] = prev;
|
||||
front[front_count++] = first_edge+6;
|
||||
front_count++;
|
||||
// addFront(v0, opposite, v1, prev, first_edge + 6);
|
||||
|
||||
faceorder.push(front_count);
|
||||
|
||||
front[front_count++] = opposite;
|
||||
front[front_count++] = v1;
|
||||
front[front_count++] = v0;
|
||||
front[front_count++] = first_edge;
|
||||
front[front_count++] = next;
|
||||
front_count++;
|
||||
// addFront(opposite, v1, v0, first_edge, next);
|
||||
|
||||
} else if(c == 3) { //END
|
||||
front[prev + 5] = 1;
|
||||
front[next + 5] = 1;
|
||||
front[front[prev + 3] + 4] = front[next + 4];
|
||||
front[front[next + 4] + 3] = front[prev + 3];
|
||||
opposite = front[prev + 0];
|
||||
|
||||
} else if(c == 1) { //LEFT
|
||||
front[prev + 5] = 1; //deleted
|
||||
front[front[prev + 3] + 4] = first_edge;
|
||||
front[next + 3] = first_edge;
|
||||
opposite = front[prev + 0];
|
||||
|
||||
faceorder.unshift(front_count);
|
||||
|
||||
front[front_count++] = opposite;
|
||||
front[front_count++] = v1;
|
||||
front[front_count++] = v0;
|
||||
front[front_count++] = front[prev +3];
|
||||
front[front_count++] = next;
|
||||
front_count++;
|
||||
// addFront(opposite, v1, v0, front[prev + 3], next);
|
||||
|
||||
} else if(c == 2) { //RIGHT
|
||||
front[next + 5] = 1;
|
||||
front[front[next + 4] + 3] = first_edge;
|
||||
front[prev + 4] = first_edge;
|
||||
opposite = front[next + 1];
|
||||
|
||||
|
||||
faceorder.unshift(front_count);
|
||||
|
||||
front[front_count++] = v0;
|
||||
front[front_count++] = opposite;
|
||||
front[front_count++] = v1;
|
||||
front[front_count++] = prev;
|
||||
front[front_count++] = front[next+4];
|
||||
front_count++;
|
||||
// addFront(v0, opposite, v1, prev, front[next + 4]);
|
||||
|
||||
} else if(c == 5) { //DELAY
|
||||
front[edge_start + 5] = 0;
|
||||
delayed.push(edge_start);
|
||||
continue;
|
||||
}
|
||||
this.faces[faces_count++] = v1;
|
||||
this.faces[faces_count++] = v0;
|
||||
this.faces[faces_count++] = opposite;
|
||||
totfaces--;
|
||||
}
|
||||
},
|
||||
|
||||
decodeVertex: function(bitstream, diff, tdiff) {
|
||||
if(diff == 0)
|
||||
return bitstream.read(16);
|
||||
|
||||
var v = this.vertex_count++;
|
||||
|
||||
var max = 1<<(diff-1);
|
||||
|
||||
for(var k = 0; k < 3; k++) {
|
||||
var d = bitstream.read(diff) - max;
|
||||
this.coords[v*3+k] = this.stack[k] + d; //stack 0-3 is used as extimated
|
||||
}
|
||||
if(this.sig.texcoords) {
|
||||
var tmax = 1<<(tdiff-1);
|
||||
for(var k = 0; k < 2; k++) {
|
||||
var d = bitstream.read(tdiff) - tmax;
|
||||
this.texcoords[v*2+k] = this.stack[7+k] + d; //stack 7-9 is used as extimated
|
||||
}
|
||||
}
|
||||
return v;
|
||||
},
|
||||
|
||||
decodeDiff: function(diff, bitstream) {
|
||||
var val;
|
||||
if(diff == 0) {
|
||||
return 0;
|
||||
}
|
||||
val = 1<<diff;
|
||||
val += bitstream.read(diff);
|
||||
|
||||
|
||||
if(val & 0x1)
|
||||
val >>>= 1;
|
||||
else
|
||||
val = -(val>>>1);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
var tot = 0;
|
||||
1323
frontend/public/vendor/3dhop/nexus.js
vendored
Normal file
1323
frontend/public/vendor/3dhop/nexus.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1042
frontend/public/vendor/3dhop/ply.js
vendored
Normal file
1042
frontend/public/vendor/3dhop/ply.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
4232
frontend/public/vendor/3dhop/presenter.js
vendored
Executable file
4232
frontend/public/vendor/3dhop/presenter.js
vendored
Executable file
File diff suppressed because it is too large
Load Diff
29
frontend/public/vendor/3dhop/spidergl.js
vendored
Normal file
29
frontend/public/vendor/3dhop/spidergl.js
vendored
Normal file
File diff suppressed because one or more lines are too long
383
frontend/public/vendor/3dhop/trackball_pantilt.js
vendored
Executable file
383
frontend/public/vendor/3dhop/trackball_pantilt.js
vendored
Executable file
@@ -0,0 +1,383 @@
|
||||
/*
|
||||
3DHOP - 3D Heritage Online Presenter
|
||||
Copyright (c) 2014-2023, Visual Computing Lab, ISTI - CNR
|
||||
All rights reserved.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Constructs a PanTiltTrackball object.
|
||||
* @class Interactor which implements a pan-tilt trackball controller with bounds.
|
||||
*/
|
||||
function PanTiltTrackball() {
|
||||
}
|
||||
|
||||
PanTiltTrackball.prototype = {
|
||||
|
||||
setup : function (options,myPresenter) {
|
||||
options = options || {};
|
||||
var opt = sglGetDefaultObject({
|
||||
startCenter : [ 0.0, 0.0, 0.0 ],
|
||||
startPanX : 0.0,
|
||||
startPanY : 0.0,
|
||||
startAngleX : 0.0,
|
||||
startAngleY : 0.0,
|
||||
startDistance : 2.0,
|
||||
minMaxDist : [0.2, 4.0],
|
||||
minMaxPanX : [-0.7, 0.7],
|
||||
minMaxPanY : [-0.7, 0.7],
|
||||
minMaxAngleX : [-70.0, 70.0],
|
||||
minMaxAngleY : [-70.0, 70.0],
|
||||
pathStates : [ ], // path points array
|
||||
animationLocked : false,// if true disable trackball interactions during animation
|
||||
animationTime : null // when single position navigation, each to point navigation is # seconds (if null, automatically computed)
|
||||
}, options);
|
||||
|
||||
this._action = SGL_TRACKBALL_NO_ACTION;
|
||||
this._new_action = true;
|
||||
this._matrix = SglMat4.identity();
|
||||
|
||||
this.myPresenter = myPresenter;// parent presenter
|
||||
|
||||
// path
|
||||
this._pathStates = opt.pathStates;
|
||||
this._animationLocked = opt.animationLocked;
|
||||
this._pathPosNum = 0;
|
||||
|
||||
// trackball center
|
||||
this._center = opt.startCenter;
|
||||
|
||||
// starting/default parameters
|
||||
this._startPanX = opt.startPanX; //pan X
|
||||
this._startPanY = opt.startPanY; //pan Y
|
||||
this._startAngleX = sglDegToRad(opt.startAngleX); //angle X
|
||||
this._startAngleY = sglDegToRad(opt.startAngleY); //angle Y
|
||||
this._startDistance = opt.startDistance; //distance
|
||||
|
||||
// current parameters
|
||||
this._panX = this._startPanX;
|
||||
this._panY = this._startPanY;
|
||||
this._angleX = this._startAngleX;
|
||||
this._angleY = this._startAngleY;
|
||||
this._distance = this._startDistance;
|
||||
|
||||
// target paramenters
|
||||
this._targetPanX = this._startPanX;
|
||||
this._targetPanY = this._startPanY;
|
||||
this._targetAngleX = this._startAngleX;
|
||||
this._targetAngleY = this._startAngleY;
|
||||
this._targetDistance = this._startDistance;
|
||||
|
||||
//animation data
|
||||
this._isAnimating = false;
|
||||
this._speedPanX = 0.0;
|
||||
this._speedPanY = 0.0;
|
||||
this._speedAngleX = 0.0;
|
||||
this._speedAngleY = 0.0;
|
||||
this._speedDistance = 0.0;
|
||||
this._isAutoWalking = false;
|
||||
this._animationTime = opt.animationTime;
|
||||
|
||||
//limits
|
||||
this._minMaxDist = opt.minMaxDist;
|
||||
this._minMaxPanX = opt.minMaxPanX;
|
||||
this._minMaxPanY = opt.minMaxPanY;
|
||||
this._minMaxAngleX = opt.minMaxAngleX;
|
||||
this._minMaxAngleX[0] = sglDegToRad(this._minMaxAngleX[0]);
|
||||
this._minMaxAngleX[1] = sglDegToRad(this._minMaxAngleX[1]);
|
||||
this._minMaxAngleY = opt.minMaxAngleY;
|
||||
this._minMaxAngleY[0] = sglDegToRad(this._minMaxAngleY[0]);
|
||||
this._minMaxAngleY[1] = sglDegToRad(this._minMaxAngleY[1]);
|
||||
|
||||
this._start = [0.0, 0.0];
|
||||
this.reset();
|
||||
},
|
||||
|
||||
_clamp : function(value, low, high) {
|
||||
if(value < low) return low;
|
||||
if(value > high) return high;
|
||||
return value;
|
||||
},
|
||||
|
||||
_computeMatrix: function() {
|
||||
var m = SglMat4.identity();
|
||||
|
||||
// centering
|
||||
m = SglMat4.mul(m, SglMat4.translation([-this._center[0], -this._center[1], -this._center[2]]));
|
||||
// zoom
|
||||
m = SglMat4.mul(m, SglMat4.translation([0.0, 0.0, -this._distance]));
|
||||
// tilt
|
||||
m = SglMat4.mul(m, SglMat4.rotationAngleAxis(this._angleY, [1.0, 0.0, 0.0]));
|
||||
m = SglMat4.mul(m, SglMat4.rotationAngleAxis(this._angleX, [0.0, -1.0, 0.0]));
|
||||
// pan
|
||||
m = SglMat4.mul(m, SglMat4.translation([this._panX, this._panY, 0.0]));
|
||||
|
||||
this._matrix = m;
|
||||
|
||||
if(typeof onTrackballUpdate != "undefined")
|
||||
onTrackballUpdate(this.getState());
|
||||
},
|
||||
|
||||
getState : function () {
|
||||
return [this._panX, this._panY, sglRadToDeg(this._angleX), sglRadToDeg(this._angleY), this._distance];
|
||||
},
|
||||
|
||||
setState : function (newstate) {
|
||||
// stop animation
|
||||
this._isAnimating = this._isAutoWalking = false;
|
||||
|
||||
this._panX = newstate[0];
|
||||
this._panY = newstate[1];
|
||||
this._angleX = sglDegToRad(newstate[2]);
|
||||
this._angleY = sglDegToRad(newstate[3]);
|
||||
this._distance = newstate[4];
|
||||
|
||||
//check limits
|
||||
this._panX = this._clamp(this._panX, this._minMaxPanX[0], this._minMaxPanX[1]);
|
||||
this._panY = this._clamp(this._panY, this._minMaxPanY[0], this._minMaxPanY[1]);
|
||||
this._angleX = this._clamp(this._angleX, this._minMaxAngleX[0], this._minMaxAngleX[1]);
|
||||
this._angleY = this._clamp(this._angleY, this._minMaxAngleY[0], this._minMaxAngleY[1]);
|
||||
this._distance = this._clamp(this._distance, this._minMaxDist[0], this._minMaxDist[1]);
|
||||
|
||||
this._computeMatrix();
|
||||
},
|
||||
|
||||
animateToState : function (newstate, newtime) {
|
||||
// stop animation
|
||||
this._isAnimating = false;
|
||||
|
||||
if(newstate)
|
||||
{
|
||||
// stop autoWalking
|
||||
this._isAutoWalking = false;
|
||||
|
||||
// setting targets
|
||||
this._targetPanX = newstate[0];
|
||||
this._targetPanY = newstate[1];
|
||||
this._targetAngleX = sglDegToRad(newstate[2]);
|
||||
this._targetAngleY = sglDegToRad(newstate[3]);
|
||||
this._targetDistance = newstate[4];
|
||||
|
||||
//check limits
|
||||
this._targetPanX = this._clamp(this._targetPanX, this._minMaxPanX[0], this._minMaxPanX[1]);
|
||||
this._targetPanY = this._clamp(this._targetPanY, this._minMaxPanY[0], this._minMaxPanY[1]);
|
||||
this._targetAngleX = this._clamp(this._targetAngleX, this._minMaxAngleX[0], this._minMaxAngleX[1]);
|
||||
this._targetAngleY = this._clamp(this._targetAngleY, this._minMaxAngleY[0], this._minMaxAngleY[1]);
|
||||
this._targetDistance = this._clamp(this._targetDistance, this._minMaxDist[0], this._minMaxDist[1]);
|
||||
|
||||
// setting base velocities
|
||||
this._speedPanX = 2.0;
|
||||
this._speedPanY = 2.0;
|
||||
this._speedAngleX = Math.PI;
|
||||
this._speedAngleY = Math.PI;
|
||||
this._speedDistance = 2.0;
|
||||
|
||||
// find max animation time to set a time limit and then synchronize all movements
|
||||
var timePanX = Math.abs((this._targetPanX - this._panX) / this._speedPanX);
|
||||
var timePanY = Math.abs((this._targetPanY - this._panY) / this._speedPanY);
|
||||
var timeAngleX = Math.abs((this._targetAngleX - this._angleX) / this._speedAngleX);
|
||||
var timeAngleY = Math.abs((this._targetAngleY - this._angleY) / this._speedAngleY);
|
||||
var timeDistance = Math.abs((this._targetDistance - this._distance) / this._speedDistance);
|
||||
|
||||
var maxtime = Math.max( timePanX, Math.max( timePanY, Math.max( timeAngleX, Math.max( timeAngleY, timeDistance ) ) ));
|
||||
var animationtime = this._clamp(maxtime, 0.5, 2.0);
|
||||
|
||||
if(newtime) animationtime = newtime;
|
||||
else if (this._animationTime) animationtime = this._animationTime;
|
||||
|
||||
this._speedPanX *= timePanX / animationtime;
|
||||
this._speedPanY *= timePanY / animationtime;
|
||||
this._speedAngleX *= timeAngleX / animationtime;
|
||||
this._speedAngleY *= timeAngleY / animationtime;
|
||||
this._speedDistance *= timeDistance / animationtime;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(this._pathPosNum == this._pathStates.length){
|
||||
this._isAutoWalking = false;
|
||||
this._pathPosNum = 0;
|
||||
}
|
||||
else {
|
||||
var state = this._pathStates[this._pathPosNum][0];
|
||||
var time = this._animationTime;
|
||||
if(!Array.isArray(state)) state = this._pathStates[this._pathPosNum];
|
||||
else if (this._pathStates[this._pathPosNum][1]) time = this._pathStates[this._pathPosNum][1];
|
||||
if(!this._isAutoWalking) this.animateToState(state, time);
|
||||
this._isAutoWalking = true;
|
||||
}
|
||||
}
|
||||
|
||||
// start animation
|
||||
this._isAnimating = true;
|
||||
},
|
||||
|
||||
recenter : function (newpoint) {
|
||||
// stop animation
|
||||
this._isAnimating = this._isAutoWalking = false;
|
||||
|
||||
var newpanX = -(newpoint[0]-this.myPresenter.sceneCenter[0]) * this.myPresenter.sceneRadiusInv;
|
||||
var newpanY = -(newpoint[1]-this.myPresenter.sceneCenter[1]) * this.myPresenter.sceneRadiusInv;
|
||||
|
||||
this.animateToState([newpanX, newpanY, sglRadToDeg(this._angleX), sglRadToDeg(this._angleY), (this._distance * 0.6)]);
|
||||
},
|
||||
|
||||
tick : function (dt) {
|
||||
if(!this._isAnimating) return false;
|
||||
|
||||
var deltaPanX = this._speedPanX * dt;
|
||||
var deltaPanY = this._speedPanY * dt;
|
||||
var deltaAngleX = this._speedAngleX * dt;
|
||||
var deltaAngleY = this._speedAngleY * dt;
|
||||
var deltaDistance = this._speedDistance * dt;
|
||||
|
||||
var diffPanX = this._targetPanX - this._panX;
|
||||
var diffPanY = this._targetPanY - this._panY;
|
||||
var diffAngleX = this._targetAngleX - this._angleX;
|
||||
var diffAngleY = this._targetAngleY - this._angleY;
|
||||
var diffDistance = this._targetDistance - this._distance;
|
||||
|
||||
if (diffPanX > deltaPanX)
|
||||
this._panX += deltaPanX;
|
||||
else if (diffPanX < -deltaPanX)
|
||||
this._panX -= deltaPanX;
|
||||
else
|
||||
this._panX = this._targetPanX;
|
||||
|
||||
if (diffPanY > deltaPanY)
|
||||
this._panY += deltaPanY;
|
||||
else if (diffPanY < -deltaPanY)
|
||||
this._panY -= deltaPanY;
|
||||
else
|
||||
this._panY = this._targetPanY;
|
||||
|
||||
if (diffAngleX > deltaAngleX)
|
||||
this._angleX += deltaAngleX;
|
||||
else if (diffAngleX < -deltaAngleX)
|
||||
this._angleX -= deltaAngleX;
|
||||
else
|
||||
this._angleX = this._targetAngleX;
|
||||
|
||||
if (diffAngleY > deltaAngleY)
|
||||
this._angleY += deltaAngleY;
|
||||
else if (diffAngleY < -deltaAngleY)
|
||||
this._angleY -= deltaAngleY;
|
||||
else
|
||||
this._angleY = this._targetAngleY;
|
||||
|
||||
if (diffDistance > deltaDistance)
|
||||
this._distance += deltaDistance;
|
||||
else if (diffDistance < -deltaDistance)
|
||||
this._distance -= deltaDistance;
|
||||
else
|
||||
this._distance = this._targetDistance;
|
||||
|
||||
if(this._panX == this._targetPanX)
|
||||
if(this._panY == this._targetPanY)
|
||||
if(this._angleX == this._targetAngleX)
|
||||
if(this._angleY == this._targetAngleY)
|
||||
if(this._distance == this._targetDistance){
|
||||
this._isAnimating = false;
|
||||
if(typeof onTrackballArrived != "undefined")
|
||||
onTrackballArrived(this.getState());
|
||||
if(this._isAutoWalking) { this._pathPosNum++; this._isAutoWalking = false; this.animateToState(); }
|
||||
}
|
||||
|
||||
this._computeMatrix();
|
||||
return true;
|
||||
},
|
||||
|
||||
set action(a) { if(this._action != a) { this._new_action = true; this._action = a; } },
|
||||
|
||||
get action() { return this._action; },
|
||||
|
||||
get matrix() { this._computeMatrix(); return this._matrix; },
|
||||
|
||||
get distance() { return this._distance; },
|
||||
|
||||
reset : function () {
|
||||
this._matrix = SglMat4.identity();
|
||||
this._action = SGL_TRACKBALL_NO_ACTION;
|
||||
this._new_action = true;
|
||||
|
||||
this._panX = this._startPanX;
|
||||
this._panY = this._startPanY;
|
||||
this._angleX = this._startAngleX;
|
||||
this._angleY = this._startAngleY;
|
||||
this._distance = this._startDistance;
|
||||
|
||||
this._pathPosNum = 0;
|
||||
|
||||
this._isAutoWalking = false;
|
||||
this._isAnimating = false;
|
||||
|
||||
this._computeMatrix();
|
||||
},
|
||||
|
||||
track : function(m, x, y, z) {
|
||||
if(this._animationLocked && this._isAnimating) this._action = SGL_TRACKBALL_NO_ACTION;
|
||||
if(this._new_action) {
|
||||
this._start[0] = x;
|
||||
this._start[1] = y;
|
||||
this._new_action = false;
|
||||
}
|
||||
|
||||
var dx = this._start[0] - x;
|
||||
var dy = this._start[1] - y;
|
||||
this._start[0] = x;
|
||||
this._start[1] = y;
|
||||
|
||||
switch (this._action) {
|
||||
case SGL_TRACKBALL_ROTATE:
|
||||
this._isAnimating = this._isAutoWalking = false; //stopping animation
|
||||
this.rotate(m, dx, dy);
|
||||
break;
|
||||
|
||||
case SGL_TRACKBALL_PAN:
|
||||
this._isAnimating = this._isAutoWalking = false; //stopping animation
|
||||
this.pan(m, dx, dy);
|
||||
break;
|
||||
|
||||
case SGL_TRACKBALL_SCALE:
|
||||
this._isAnimating = this._isAutoWalking = false; //stopping animation
|
||||
this.scale(m, z);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return this._computeMatrix();
|
||||
},
|
||||
|
||||
rotate: function(m, dx, dy) {
|
||||
this._angleX += dx;
|
||||
this._angleY += dy;
|
||||
this._angleX = this._clamp(this._angleX, this._minMaxAngleX[0], this._minMaxAngleX[1]);
|
||||
this._angleY = this._clamp(this._angleY, this._minMaxAngleY[0], this._minMaxAngleY[1]);
|
||||
},
|
||||
|
||||
pan: function(m, dx, dy) {
|
||||
var panSpeed = Math.max(Math.min(1.5, this._distance),0.05);
|
||||
this._panX -= dx/2.0 * panSpeed;
|
||||
this._panY -= dy/2.0 * panSpeed;
|
||||
this._panX = this._clamp(this._panX, this._minMaxPanX[0], this._minMaxPanX[1]);
|
||||
this._panY = this._clamp(this._panY, this._minMaxPanY[0], this._minMaxPanY[1]);
|
||||
},
|
||||
|
||||
scale : function(m, s) {
|
||||
this._distance *= s;
|
||||
this._distance = this._clamp(this._distance, this._minMaxDist[0], this._minMaxDist[1]);
|
||||
}
|
||||
};
|
||||
/***********************************************************************/
|
||||
241
frontend/public/vendor/3dhop/trackball_sphere.js
vendored
Normal file
241
frontend/public/vendor/3dhop/trackball_sphere.js
vendored
Normal file
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
3DHOP - 3D Heritage Online Presenter
|
||||
Copyright (c) 2014-2020, Visual Computing Lab, ISTI - CNR
|
||||
All rights reserved.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Constructs a SphereTrackball object.
|
||||
* @class Interactor which implements a full spherical trackball controller.
|
||||
*/
|
||||
function SphereTrackball() {
|
||||
}
|
||||
|
||||
SphereTrackball.prototype = {
|
||||
|
||||
setup : function (options) {
|
||||
options = options || {};
|
||||
var opt = sglGetDefaultObject({
|
||||
startCenter : [ 0.0, 0.0, 0.0 ],
|
||||
startDistance : 2.0,
|
||||
minMaxDist : [0.2, 4.0],
|
||||
}, options);
|
||||
|
||||
this._action = SGL_TRACKBALL_NO_ACTION;
|
||||
this._new_action = true;
|
||||
this._matrix = SglMat4.identity();
|
||||
this._sphereMatrix = SglMat4.identity();
|
||||
|
||||
// starting/default parameters
|
||||
this._startDistance = opt.startDistance; //distance
|
||||
|
||||
// current parameters
|
||||
this._distance = this._startDistance;
|
||||
|
||||
//limits
|
||||
this._minMaxDist = opt.minMaxDist;
|
||||
|
||||
this._pts = [ [0.0, 0.0], [0.0, 0.0] ];
|
||||
this._start = [0.0, 0.0];
|
||||
this.reset();
|
||||
},
|
||||
|
||||
clamp : function(value, low, high) {
|
||||
if(value < low) return low;
|
||||
if(value > high) return high;
|
||||
return value;
|
||||
},
|
||||
|
||||
_computeMatrix: function() {
|
||||
var m = SglMat4.identity();
|
||||
|
||||
// zoom
|
||||
m = SglMat4.mul(m, SglMat4.translation([0.0, 0.0, -this._distance]));
|
||||
// spheretrack
|
||||
m = SglMat4.mul(m, this._sphereMatrix);
|
||||
|
||||
this._matrix = m;
|
||||
|
||||
if(typeof onTrackballUpdate != "undefined")
|
||||
onTrackballUpdate(this.getState());
|
||||
},
|
||||
|
||||
_projectOnSphere : function(x, y) {
|
||||
var r = 1.0;
|
||||
|
||||
var z = 0.0;
|
||||
var d = sglSqrt(x*x + y*y);
|
||||
if (d < (r * 0.70710678118654752440)) {
|
||||
/* Inside sphere */
|
||||
z = sglSqrt(r*r - d*d);
|
||||
}
|
||||
else {
|
||||
/* On hyperbola */
|
||||
t = r / 1.41421356237309504880;
|
||||
z = t*t / d;
|
||||
}
|
||||
return z;
|
||||
},
|
||||
|
||||
_transform : function(m, x, y, z) {
|
||||
return SglMat4.mul4(m, [x, y, z, 0.0]);
|
||||
},
|
||||
|
||||
_transformOnSphere : function(m, x, y) {
|
||||
var z = this._projectOnSphere(x, y); //get z value
|
||||
return this._transform(m, x, y, z);
|
||||
},
|
||||
|
||||
_translate : function(offset, f) {
|
||||
var invMat = SglMat4.inverse(this._sphereMatrix);
|
||||
var t = SglVec3.to4(offset, 0.0);
|
||||
t = SglMat4.mul4(invMat, t);
|
||||
t = SglVec4.muls(t, f);
|
||||
var trMat = SglMat4.translation(t);
|
||||
this._sphereMatrix = SglMat4.mul(this._sphereMatrix, trMat);
|
||||
},
|
||||
|
||||
getState : function () {
|
||||
return this._sphereMatrix;
|
||||
},
|
||||
|
||||
setState : function (newstate) {
|
||||
this._sphereMatrix = newstate;
|
||||
this._computeMatrix();
|
||||
},
|
||||
|
||||
animateToState : function (newstate) {
|
||||
this._sphereMatrix = newstate;
|
||||
this._computeMatrix();
|
||||
},
|
||||
|
||||
recenter : function (newpoint) {
|
||||
var newpanX = (newpoint[0]-presenter.sceneCenter[0]) * presenter.sceneRadiusInv;
|
||||
var newpanY = (newpoint[1]-presenter.sceneCenter[1]) * presenter.sceneRadiusInv;
|
||||
var newpanZ = (newpoint[2]-presenter.sceneCenter[2]) * presenter.sceneRadiusInv;
|
||||
|
||||
this._sphereMatrix[12] = -newpanX;
|
||||
this._sphereMatrix[13] = -newpanY;
|
||||
this._sphereMatrix[14] = -newpanZ;
|
||||
this._distance *= 0.6;
|
||||
this._distance = this.clamp(this._distance, this._minMaxDist[0], this._minMaxDist[1]);
|
||||
this._computeMatrix();
|
||||
},
|
||||
|
||||
tick : function (dt) {
|
||||
return false;
|
||||
},
|
||||
|
||||
set action(a) { if(this._action != a) this._new_action = true; this._action = a; },
|
||||
|
||||
get action() { return this._action; },
|
||||
|
||||
get matrix() { this._computeMatrix(); return this._matrix; },
|
||||
|
||||
get distance() { return this._distance; },
|
||||
|
||||
reset : function () {
|
||||
this._matrix = SglMat4.identity();
|
||||
this._sphereMatrix = SglMat4.identity();
|
||||
this._action = SGL_TRACKBALL_NO_ACTION;
|
||||
this._new_action = true;
|
||||
|
||||
this._distance = this._startDistance;
|
||||
|
||||
this._pts = [ [0.0, 0.0], [0.0, 0.0] ];
|
||||
|
||||
this._computeMatrix();
|
||||
},
|
||||
|
||||
track : function(m, x, y, z) {
|
||||
|
||||
if(this._new_action) {
|
||||
this._start[0] = x;
|
||||
this._start[1] = y;
|
||||
this._new_action = false;
|
||||
}
|
||||
|
||||
var dx = this._start[0] - x;
|
||||
var dy = this._start[1] - y;
|
||||
this._start[0] = x;
|
||||
this._start[1] = y;
|
||||
|
||||
this._pts[0][0] = this._pts[1][0] + dx;
|
||||
this._pts[0][1] = this._pts[1][1] + dy;
|
||||
this._pts[1][0] = dx;
|
||||
this._pts[1][1] = dy;
|
||||
|
||||
switch (this._action) {
|
||||
case SGL_TRACKBALL_ROTATE:
|
||||
this.rotate(m);
|
||||
break;
|
||||
|
||||
case SGL_TRACKBALL_PAN:
|
||||
this.pan(m);
|
||||
break;
|
||||
|
||||
case SGL_TRACKBALL_DOLLY:
|
||||
this.dolly(m, z);
|
||||
break;
|
||||
|
||||
case SGL_TRACKBALL_SCALE:
|
||||
this.scale(m, z);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
rotate : function(m) {
|
||||
if ((this._pts[0][0] == this._pts[1][0]) && (this._pts[0][1] == this._pts[1][1])) return; //if Xold == Xnew && Yold ==Ynew return
|
||||
|
||||
var mInv = SglMat4.inverse(m);
|
||||
|
||||
var v0 = this._transformOnSphere(mInv, this._pts[0][0], this._pts[0][1]); //project on sphere (Xold, Yold)
|
||||
var v1 = this._transformOnSphere(mInv, this._pts[1][0], this._pts[1][1]); //project on sphere (Xnew, Ynew)
|
||||
|
||||
var axis = SglVec3.cross(v0, v1); //axis of rotation
|
||||
var angle = SglVec3.length(axis); //angle of rotation
|
||||
var rotMat = SglMat4.rotationAngleAxis(angle, axis);
|
||||
|
||||
this._sphereMatrix = SglMat4.mul(rotMat, this._sphereMatrix);
|
||||
this._computeMatrix();
|
||||
},
|
||||
|
||||
pan : function(m) {
|
||||
var mInv = SglMat4.inverse(m);
|
||||
var v0 = this._transform(mInv, this._pts[0][0], this._pts[0][1], -1.0);
|
||||
var v1 = this._transform(mInv, this._pts[1][0], this._pts[1][1], -1.0);
|
||||
var offset = SglVec3.sub(v1, v0);
|
||||
this._translate(offset, 2.0);
|
||||
this._computeMatrix();
|
||||
},
|
||||
|
||||
dolly : function(m, dz) {
|
||||
var mInv = SglMat4.inverse(m);
|
||||
var offset = this._transform(mInv, 0.0, 0.0, dz);
|
||||
this._translate(offset, 1.0);
|
||||
this._computeMatrix();
|
||||
},
|
||||
|
||||
scale : function(m, s) {
|
||||
this._distance *= s;
|
||||
this._distance = this.clamp(this._distance, this._minMaxDist[0], this._minMaxDist[1]);
|
||||
this._computeMatrix();
|
||||
}
|
||||
};
|
||||
/***********************************************************************/
|
||||
354
frontend/public/vendor/3dhop/trackball_turntable.js
vendored
Normal file
354
frontend/public/vendor/3dhop/trackball_turntable.js
vendored
Normal file
@@ -0,0 +1,354 @@
|
||||
/*
|
||||
3DHOP - 3D Heritage Online Presenter
|
||||
Copyright (c) 2014-2020, Visual Computing Lab, ISTI - CNR
|
||||
All rights reserved.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Constructs a TurntableTrackball object.
|
||||
* @class Interactor which implements a Turntable controller with bounds.
|
||||
*/
|
||||
function TurnTableTrackball() {
|
||||
}
|
||||
|
||||
TurnTableTrackball.prototype = {
|
||||
|
||||
setup : function (options) {
|
||||
options = options || {};
|
||||
var opt = sglGetDefaultObject({
|
||||
startCenter : [ 0.0, 0.0, 0.0 ],
|
||||
startPhi : 0.0,
|
||||
startTheta : 0.0,
|
||||
startDistance : 2.0,
|
||||
minMaxDist : [0.2, 4.0],
|
||||
minMaxPhi : [-180, 180],
|
||||
minMaxTheta : [-80.0, 80.0],
|
||||
pathStates : [ ], // path points array
|
||||
animationLocked : false,// if true disable trackball interactions during animation
|
||||
animationTime : null // when single position navigation, each to point navigation is # seconds (if null, automatically computed)
|
||||
}, options);
|
||||
|
||||
this._action = SGL_TRACKBALL_NO_ACTION;
|
||||
this._new_action = true;
|
||||
this._matrix = SglMat4.identity();
|
||||
|
||||
// path
|
||||
this._pathStates = opt.pathStates;
|
||||
this._animationLocked = opt.animationLocked;
|
||||
this._pathPosNum = 0;
|
||||
|
||||
// trackball center
|
||||
this._center = opt.startCenter;
|
||||
|
||||
// starting/default parameters
|
||||
this._startPhi = sglDegToRad(opt.startPhi); //phi (horizontal rotation)
|
||||
this._startTheta = sglDegToRad(opt.startTheta); //theta (vertical rotation)
|
||||
this._startDistance = opt.startDistance; //distance
|
||||
|
||||
// current parameters
|
||||
this._phi = this._startPhi;
|
||||
this._theta = this._startTheta;
|
||||
this._distance = this._startDistance;
|
||||
|
||||
// target paramenters
|
||||
this._targetPhi = this._startPhi;
|
||||
this._targetTheta = this._startTheta;
|
||||
this._targetDistance = this._startDistance;
|
||||
|
||||
//animation data
|
||||
this._isAnimating = false;
|
||||
this._speedPhi = 0.0;
|
||||
this._speedTheta = 0.0;
|
||||
this._speedDistance = 0.0;
|
||||
this._isAutoWalking = false;
|
||||
this._animationTime = opt.animationTime;
|
||||
|
||||
// limits
|
||||
this._minMaxDist = opt.minMaxDist;
|
||||
if((opt.minMaxPhi[0] == -180)&&(opt.minMaxPhi[1] == 180))
|
||||
this._limitPhi = false;
|
||||
else
|
||||
this._limitPhi = true;
|
||||
this._minMaxPhi = opt.minMaxPhi;
|
||||
this._minMaxPhi[0] = sglDegToRad(this._minMaxPhi[0]);
|
||||
this._minMaxPhi[1] = sglDegToRad(this._minMaxPhi[1]);
|
||||
this._minMaxTheta = opt.minMaxTheta;
|
||||
this._minMaxTheta[0] = sglDegToRad(this._minMaxTheta[0]);
|
||||
this._minMaxTheta[1] = sglDegToRad(this._minMaxTheta[1]);
|
||||
|
||||
this._start = [0.0, 0.0];
|
||||
this.reset();
|
||||
},
|
||||
|
||||
_clamp : function(value, low, high) {
|
||||
if(value < low) return low;
|
||||
if(value > high) return high;
|
||||
return value;
|
||||
},
|
||||
|
||||
_computeMatrix: function() {
|
||||
var m = SglMat4.identity();
|
||||
|
||||
// centering
|
||||
m = SglMat4.mul(m, SglMat4.translation([-this._center[0], -this._center[1], -this._center[2]]));
|
||||
// zoom
|
||||
m = SglMat4.mul(m, SglMat4.translation([0.0, 0.0, -this._distance]));
|
||||
// rotation
|
||||
m = SglMat4.mul(m, SglMat4.rotationAngleAxis(this._theta, [1.0, 0.0, 0.0]));
|
||||
// tilt
|
||||
m = SglMat4.mul(m, SglMat4.rotationAngleAxis(this._phi, [0.0, -1.0, 0.0]));
|
||||
|
||||
this._matrix = m;
|
||||
|
||||
if(typeof onTrackballUpdate != "undefined")
|
||||
onTrackballUpdate(this.getState());
|
||||
},
|
||||
|
||||
getState : function () {
|
||||
return [sglRadToDeg(this._phi), sglRadToDeg(this._theta), this._distance];
|
||||
},
|
||||
|
||||
setState : function (newstate) {
|
||||
// stop animation
|
||||
this._isAnimating = this._isAutoWalking = false;
|
||||
|
||||
this._phi = sglDegToRad(newstate[0]);
|
||||
this._theta = sglDegToRad(newstate[1]);
|
||||
this._distance = newstate[2];
|
||||
|
||||
//check limits
|
||||
if(this._limitPhi)
|
||||
this._phi = this._clamp(this._phi, this._minMaxPhi[0], this._minMaxPhi[1]);
|
||||
this._theta = this._clamp(this._theta, this._minMaxTheta[0], this._minMaxTheta[1]);
|
||||
this._distance = this._clamp(this._distance, this._minMaxDist[0], this._minMaxDist[1]);
|
||||
|
||||
this._computeMatrix();
|
||||
},
|
||||
|
||||
animateToState : function (newstate, newtime) {
|
||||
// stop animation
|
||||
this._isAnimating = false;
|
||||
|
||||
if(newstate)
|
||||
{
|
||||
// stop autoWalking
|
||||
this._isAutoWalking = false;
|
||||
|
||||
// setting targets
|
||||
this._targetPhi = sglDegToRad(newstate[0]);
|
||||
this._targetTheta = sglDegToRad(newstate[1]);
|
||||
this._targetDistance = newstate[2];
|
||||
|
||||
//check limits
|
||||
if(this._limitPhi)
|
||||
this._targetPhi = this._clamp(this._targetPhi, this._minMaxPhi[0], this._minMaxPhi[1]);
|
||||
this._targetPhi = this._targetPhi % (2*Math.PI);
|
||||
this._targetTheta = this._clamp(this._targetTheta, this._minMaxTheta[0], this._minMaxTheta[1]);
|
||||
this._targetDistance = this._clamp(this._targetDistance, this._minMaxDist[0], this._minMaxDist[1]);
|
||||
|
||||
// setting base velocities
|
||||
this._speedPhi = Math.PI;
|
||||
this._speedTheta = Math.PI;
|
||||
this._speedDistance = 2.0;
|
||||
|
||||
//if phi unconstrained rotation, it is necessary to find a good rotation direction
|
||||
if(!this._limitPhi){
|
||||
// normalize (-2pi 2pi) current phi angle, to prevent endless unwinding
|
||||
this._phi = this._phi % (2*Math.PI);
|
||||
|
||||
// determine minimal, normalized target phi angle, to prevent endless unwinding
|
||||
var clampedangle = this._targetPhi;
|
||||
clampedangle = clampedangle % (2*Math.PI);
|
||||
|
||||
if(Math.abs(clampedangle - this._phi) < Math.PI) { // standard rotation
|
||||
if(clampedangle > this._phi){
|
||||
this.speedphi = Math.PI;
|
||||
}
|
||||
else{
|
||||
this.speedphi = -Math.PI;
|
||||
}
|
||||
}
|
||||
else{
|
||||
if(clampedangle > this._phi){
|
||||
clampedangle = (clampedangle - 2*Math.PI)
|
||||
this.speedphi = -Math.PI;
|
||||
}
|
||||
else{
|
||||
clampedangle = (clampedangle + 2*Math.PI)
|
||||
this.speedphi = Math.PI;
|
||||
}
|
||||
}
|
||||
|
||||
this._targetPhi = clampedangle;
|
||||
}
|
||||
|
||||
// find max animation time to set a time limit and then synchronize all movements
|
||||
var timePhi = Math.abs((this._targetPhi - this._phi) / this._speedPhi);
|
||||
var timeTheta = Math.abs((this._targetTheta - this._theta) / this._speedTheta);
|
||||
var timeDistance = Math.abs((this._targetDistance - this._distance) / this._speedDistance);
|
||||
|
||||
var maxtime = Math.max( timePhi, Math.max( timeTheta, timeDistance ));
|
||||
var animationtime = this._clamp(maxtime, 0.5, 2.0);
|
||||
|
||||
if(newtime) animationtime = newtime;
|
||||
else if (this._animationTime) animationtime = this._animationTime;
|
||||
|
||||
this._speedPhi *= timePhi / animationtime;
|
||||
this._speedTheta *= timeTheta / animationtime;
|
||||
this._speedDistance *= timeDistance / animationtime;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(this._pathPosNum == this._pathStates.length){
|
||||
this._isAutoWalking = false;
|
||||
this._pathPosNum = 0;
|
||||
}
|
||||
else {
|
||||
var state = this._pathStates[this._pathPosNum][0];
|
||||
var time = this._animationTime;
|
||||
if(!Array.isArray(state)) state = this._pathStates[this._pathPosNum];
|
||||
else if (this._pathStates[this._pathPosNum][1]) time = this._pathStates[this._pathPosNum][1];
|
||||
if(!this._isAutoWalking) this.animateToState(state, time);
|
||||
this._isAutoWalking = true;
|
||||
}
|
||||
}
|
||||
|
||||
// start animation
|
||||
this._isAnimating = true;
|
||||
},
|
||||
|
||||
tick : function (dt) {
|
||||
if(!this._isAnimating) return false;
|
||||
|
||||
var deltaPhi = this._speedPhi * dt;
|
||||
var deltaTheta = this._speedTheta * dt;
|
||||
var deltaDistance = this._speedDistance * dt;
|
||||
|
||||
var diffPhi = this._targetPhi - this._phi;
|
||||
var diffTheta = this._targetTheta - this._theta;
|
||||
var diffDistance = this._targetDistance - this._distance;
|
||||
|
||||
if (diffPhi > deltaPhi)
|
||||
this._phi += deltaPhi;
|
||||
else if (diffPhi < -deltaPhi)
|
||||
this._phi -= deltaPhi;
|
||||
else
|
||||
this._phi = this._targetPhi;
|
||||
|
||||
if (diffTheta > deltaTheta)
|
||||
this._theta += deltaTheta;
|
||||
else if (diffTheta < -deltaTheta)
|
||||
this._theta -= deltaTheta;
|
||||
else
|
||||
this._theta = this._targetTheta;
|
||||
|
||||
if (diffDistance > deltaDistance)
|
||||
this._distance += deltaDistance;
|
||||
else if (diffDistance < -deltaDistance)
|
||||
this._distance -= deltaDistance;
|
||||
else
|
||||
this._distance = this._targetDistance;
|
||||
|
||||
if(this._phi == this._targetPhi)
|
||||
if(this._theta == this._targetTheta)
|
||||
if(this._distance == this._targetDistance){
|
||||
this._isAnimating = false;
|
||||
if(typeof onTrackballArrived != "undefined")
|
||||
onTrackballArrived(this.getState());
|
||||
if(this._isAutoWalking) { this._pathPosNum++; this._isAutoWalking = false; this.animateToState(); }
|
||||
}
|
||||
|
||||
this._computeMatrix();
|
||||
return true;
|
||||
},
|
||||
|
||||
set action(a) { if(this._action != a) { this._new_action = true; this._action = a; } },
|
||||
|
||||
get action() { return this._action; },
|
||||
|
||||
get matrix() { this._computeMatrix(); return this._matrix; },
|
||||
|
||||
get distance() { return this._distance; },
|
||||
|
||||
reset : function () {
|
||||
this._matrix = SglMat4.identity();
|
||||
this._action = SGL_TRACKBALL_NO_ACTION;
|
||||
this._new_action = true;
|
||||
|
||||
this._phi = this._startPhi;
|
||||
this._theta = this._startTheta;
|
||||
this._distance = this._startDistance;
|
||||
|
||||
this._pathPosNum = 0;
|
||||
|
||||
this._isAutoWalking = false;
|
||||
this._isAnimating = false;
|
||||
|
||||
this._computeMatrix();
|
||||
},
|
||||
|
||||
track : function(m, x, y, z) {
|
||||
if(this._animationLocked && this._isAnimating) this._action = SGL_TRACKBALL_NO_ACTION;
|
||||
if(this._new_action) {
|
||||
this._start[0] = x;
|
||||
this._start[1] = y;
|
||||
this._new_action = false;
|
||||
}
|
||||
|
||||
var dx = this._start[0] - x;
|
||||
var dy = this._start[1] - y;
|
||||
this._start[0] = x;
|
||||
this._start[1] = y;
|
||||
|
||||
switch (this._action) {
|
||||
case SGL_TRACKBALL_ROTATE:
|
||||
this._isAnimating = this._isAutoWalking = false; //stopping animation
|
||||
this.rotate(m, dx, dy);
|
||||
break;
|
||||
|
||||
case SGL_TRACKBALL_PAN:
|
||||
break;
|
||||
|
||||
case SGL_TRACKBALL_SCALE:
|
||||
this._isAnimating = this._isAutoWalking = false; //stopping animation
|
||||
this.scale(m, z);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return this._computeMatrix();
|
||||
},
|
||||
|
||||
rotate: function(m, dx, dy) {
|
||||
this._phi += dx;
|
||||
if(this._limitPhi)
|
||||
this._phi = this._clamp(this._phi, this._minMaxPhi[0], this._minMaxPhi[1]);
|
||||
|
||||
// avoid eternal accumulation of rotation, just for the sake of cleanliness
|
||||
if (this._phi > 10.0) this._phi = this._phi - 10.0;
|
||||
if (this._phi < -10.0) this._phi = this._phi + 10.0;
|
||||
|
||||
this._theta += dy;
|
||||
this._theta = this._clamp(this._theta, this._minMaxTheta[0], this._minMaxTheta[1]);
|
||||
},
|
||||
|
||||
scale : function(m, s) {
|
||||
this._distance *= s;
|
||||
this._distance = this._clamp(this._distance, this._minMaxDist[0], this._minMaxDist[1]);
|
||||
}
|
||||
};
|
||||
/***********************************************************************/
|
||||
467
frontend/public/vendor/3dhop/trackball_turntable_pan.js
vendored
Normal file
467
frontend/public/vendor/3dhop/trackball_turntable_pan.js
vendored
Normal file
@@ -0,0 +1,467 @@
|
||||
/*
|
||||
3DHOP - 3D Heritage Online Presenter
|
||||
Copyright (c) 2014-2020, Visual Computing Lab, ISTI - CNR
|
||||
All rights reserved.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Constructs a TurntableCylTrackball object.
|
||||
* @class Interactor which implements a Turntable controller + cylindrical pan with bounds.
|
||||
*/
|
||||
function TurntablePanTrackball() {
|
||||
}
|
||||
|
||||
TurntablePanTrackball.prototype = {
|
||||
|
||||
setup : function (options) {
|
||||
options = options || {};
|
||||
var opt = sglGetDefaultObject({
|
||||
startCenter : [ 0.0, 0.0, 0.0 ],
|
||||
startPhi : 0.0,
|
||||
startTheta : 0.0,
|
||||
startDistance : 2.0,
|
||||
startPanX : 0.0,
|
||||
startPanY : 0.0,
|
||||
startPanZ : 0.0,
|
||||
minMaxDist : [0.2, 4.0],
|
||||
minMaxPhi : [-180, 180],
|
||||
minMaxTheta : [-80.0, 80.0],
|
||||
minMaxPanX : [-1.0, 1.0],
|
||||
minMaxPanY : [-1.0, 1.0],
|
||||
minMaxPanZ : [-1.0, 1.0],
|
||||
pathStates : [ ], // path points array
|
||||
animationLocked : false,// if true disable trackball interactions during animation
|
||||
animationTime : null // when single position navigation, each to point navigation is # seconds (if null, automatically computed)
|
||||
}, options);
|
||||
|
||||
this._action = SGL_TRACKBALL_NO_ACTION;
|
||||
this._new_action = true;
|
||||
this._matrix = SglMat4.identity();
|
||||
|
||||
// path
|
||||
this._pathStates = opt.pathStates;
|
||||
this._animationLocked = opt.animationLocked;
|
||||
this._pathPosNum = 0;
|
||||
|
||||
// trackball center
|
||||
this._center = opt.startCenter;
|
||||
|
||||
// starting/default parameters
|
||||
this._startPhi = sglDegToRad(opt.startPhi); //phi (horizontal rotation)
|
||||
this._startTheta = sglDegToRad(opt.startTheta); //theta (vertical rotation)
|
||||
this._startPanX = opt.startPanX; //panX
|
||||
this._startPanY = opt.startPanY; //panY
|
||||
this._startPanZ = opt.startPanZ; //panZ
|
||||
this._startDistance = opt.startDistance; //distance
|
||||
|
||||
// current parameters
|
||||
this._phi = this._startPhi;
|
||||
this._theta = this._startTheta;
|
||||
this._panX = this._startPanX;
|
||||
this._panY = this._startPanY;
|
||||
this._panZ = this._startPanZ;
|
||||
this._distance = this._startDistance;
|
||||
|
||||
// target paramenters
|
||||
this._targetPhi = this._startPhi;
|
||||
this._targetTheta = this._startTheta;
|
||||
this._targetPanX = this._startPanX;
|
||||
this._targetPanY = this._startPanY;
|
||||
this._targetPanZ = this._startPanZ;
|
||||
this._targetDistance = this._startDistance;
|
||||
|
||||
//animation data
|
||||
this._isAnimating = false;
|
||||
this._speedPhi = 0.0;
|
||||
this._speedTheta = 0.0;
|
||||
this._speedPanX = 0.0;
|
||||
this._speedPanY = 0.0;
|
||||
this._speedPanZ = 0.0;
|
||||
this._speedDistance = 0.0;
|
||||
this._isAutoWalking = false;
|
||||
this._animationTime = opt.animationTime;
|
||||
|
||||
// limits
|
||||
this._minMaxDist = opt.minMaxDist;
|
||||
if((opt.minMaxPhi[0] == -180)&&(opt.minMaxPhi[1] == 180))
|
||||
this._limitPhi = false;
|
||||
else
|
||||
this._limitPhi = true;
|
||||
this._minMaxPhi = opt.minMaxPhi;
|
||||
this._minMaxPhi[0] = sglDegToRad(this._minMaxPhi[0]);
|
||||
this._minMaxPhi[1] = sglDegToRad(this._minMaxPhi[1]);
|
||||
this._minMaxTheta = opt.minMaxTheta;
|
||||
this._minMaxTheta[0] = sglDegToRad(this._minMaxTheta[0]);
|
||||
this._minMaxTheta[1] = sglDegToRad(this._minMaxTheta[1]);
|
||||
this._minMaxPanX = opt.minMaxPanX;
|
||||
this._minMaxPanY = opt.minMaxPanY;
|
||||
this._minMaxPanZ = opt.minMaxPanZ;
|
||||
|
||||
this._start = [0.0, 0.0];
|
||||
this.reset();
|
||||
},
|
||||
|
||||
_clamp : function(value, low, high) {
|
||||
if(value < low) return low;
|
||||
if(value > high) return high;
|
||||
return value;
|
||||
},
|
||||
|
||||
_computeMatrix: function() {
|
||||
var m = SglMat4.identity();
|
||||
|
||||
// centering
|
||||
m = SglMat4.mul(m, SglMat4.translation([-this._center[0], -this._center[1], -this._center[2]]));
|
||||
// zoom
|
||||
m = SglMat4.mul(m, SglMat4.translation([0.0, 0.0, -this._distance]));
|
||||
// rotation
|
||||
m = SglMat4.mul(m, SglMat4.rotationAngleAxis(this._theta, [1.0, 0.0, 0.0]));
|
||||
// tilt
|
||||
m = SglMat4.mul(m, SglMat4.rotationAngleAxis(this._phi, [0.0, -1.0, 0.0]));
|
||||
// panning
|
||||
m = SglMat4.mul(m, SglMat4.translation([-this._panX, -this._panY, -this._panZ]));
|
||||
|
||||
this._matrix = m;
|
||||
|
||||
if(typeof onTrackballUpdate != "undefined")
|
||||
onTrackballUpdate(this.getState());
|
||||
},
|
||||
|
||||
getState : function () {
|
||||
return [sglRadToDeg(this._phi), sglRadToDeg(this._theta), this._panX, this._panY, this._panZ, this._distance];
|
||||
},
|
||||
|
||||
setState : function (newstate) {
|
||||
// stop animation
|
||||
this._isAnimating = this._isAutoWalking = false;
|
||||
|
||||
this._phi = sglDegToRad(newstate[0]);
|
||||
this._theta = sglDegToRad(newstate[1]);
|
||||
this._panX = newstate[2];
|
||||
this._panY = newstate[3];
|
||||
this._panZ = newstate[4];
|
||||
this._distance = newstate[5];
|
||||
|
||||
//check limits
|
||||
if(this._limitPhi)
|
||||
this._phi = this._clamp(this._phi, this._minMaxPhi[0], this._minMaxPhi[1]);
|
||||
this._theta = this._clamp(this._theta, this._minMaxTheta[0], this._minMaxTheta[1]);
|
||||
this._distance = this._clamp(this._distance, this._minMaxDist[0], this._minMaxDist[1]);
|
||||
this._panX = this._clamp(this._panX, this._minMaxPanX[0], this._minMaxPanX[1]);
|
||||
this._panY = this._clamp(this._panY, this._minMaxPanY[0], this._minMaxPanY[1]);
|
||||
this._panZ = this._clamp(this._panZ, this._minMaxPanZ[0], this._minMaxPanZ[1]);
|
||||
|
||||
this._computeMatrix();
|
||||
},
|
||||
|
||||
animateToState : function (newstate, newtime) {
|
||||
// stop animation
|
||||
this._isAnimating = false;
|
||||
|
||||
if(newstate)
|
||||
{
|
||||
// stop autoWalking
|
||||
this._isAutoWalking = false;
|
||||
|
||||
// setting targets
|
||||
this._targetPhi = sglDegToRad(newstate[0]);
|
||||
this._targetTheta = sglDegToRad(newstate[1]);
|
||||
this._targetPanX = newstate[2];
|
||||
this._targetPanY = newstate[3];
|
||||
this._targetPanZ = newstate[4];
|
||||
this._targetDistance = newstate[5];
|
||||
|
||||
//check limits
|
||||
if(this._limitPhi)
|
||||
this._targetPhi = this._clamp(this._targetPhi, this._minMaxPhi[0], this._minMaxPhi[1]);
|
||||
this._targetPhi = this._targetPhi % (2*Math.PI);
|
||||
this._targetTheta = this._clamp(this._targetTheta, this._minMaxTheta[0], this._minMaxTheta[1]);
|
||||
this._targetPanX = this._clamp(this._targetPanX, this._minMaxPanX[0], this._minMaxPanX[1]);
|
||||
this._targetPanY = this._clamp(this._targetPanY, this._minMaxPanY[0], this._minMaxPanY[1]);
|
||||
this._targetPanZ = this._clamp(this._targetPanZ, this._minMaxPanZ[0], this._minMaxPanZ[1]);
|
||||
this._targetDistance = this._clamp(this._targetDistance, this._minMaxDist[0], this._minMaxDist[1]);
|
||||
|
||||
// setting base velocities
|
||||
this._speedPhi = Math.PI;
|
||||
this._speedTheta = Math.PI;
|
||||
this._speedPanX = 1.0;
|
||||
this._speedPanY = 1.0;
|
||||
this._speedPanZ = 1.0;
|
||||
this._speedDistance = 2.0;
|
||||
|
||||
//if phi unconstrained rotation, it is necessary to find a good rotation direction
|
||||
if(!this._limitPhi){
|
||||
// normalize (-2pi 2pi) current phi angle, to prevent endless unwinding
|
||||
this._phi = this._phi % (2*Math.PI);
|
||||
|
||||
// determine minimal, normalized target phi angle, to prevent endless unwinding
|
||||
var clampedangle = this._targetPhi;
|
||||
clampedangle = clampedangle % (2*Math.PI);
|
||||
|
||||
if(Math.abs(clampedangle - this._phi) < Math.PI) { // standard rotation
|
||||
if(clampedangle > this._phi){
|
||||
this.speedphi = Math.PI;
|
||||
}
|
||||
else{
|
||||
this.speedphi = -Math.PI;
|
||||
}
|
||||
}
|
||||
else{
|
||||
if(clampedangle > this._phi){
|
||||
clampedangle = (clampedangle - 2*Math.PI)
|
||||
this.speedphi = -Math.PI;
|
||||
}
|
||||
else{
|
||||
clampedangle = (clampedangle + 2*Math.PI)
|
||||
this.speedphi = Math.PI;
|
||||
}
|
||||
}
|
||||
|
||||
this._targetPhi = clampedangle;
|
||||
}
|
||||
|
||||
// find max animation time to set a time limit and then synchronize all movements
|
||||
var timePhi = Math.abs((this._targetPhi - this._phi) / this._speedPhi);
|
||||
var timeTheta = Math.abs((this._targetTheta - this._theta) / this._speedTheta);
|
||||
var timeDistance = Math.abs((this._targetDistance - this._distance) / this._speedDistance);
|
||||
var timePanX = Math.abs((this._targetPanX - this._panX) / this._speedPanX);
|
||||
var timePanY = Math.abs((this._targetPanY - this._panY) / this._speedPanY);
|
||||
var timePanZ = Math.abs((this._targetPanZ - this._panZ) / this._speedPanZ);
|
||||
|
||||
var maxtime = Math.max( timePhi, Math.max( timeTheta, Math.max( timeDistance, Math.max( timePanX, Math.max( timePanY, timePanZ )))));
|
||||
var animationtime = this._clamp(maxtime, 0.5, 2.0);
|
||||
|
||||
if(newtime) animationtime = newtime;
|
||||
else if (this._animationTime) animationtime = this._animationTime;
|
||||
|
||||
this._speedPhi *= timePhi / animationtime;
|
||||
this._speedTheta *= timeTheta / animationtime;
|
||||
this._speedDistance *= timeDistance / animationtime;
|
||||
this._speedPanX *= timePanX / animationtime;
|
||||
this._speedPanY *= timePanY / animationtime;
|
||||
this._speedPanZ *= timePanZ / animationtime;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(this._pathPosNum == this._pathStates.length){
|
||||
this._isAutoWalking = false;
|
||||
this._pathPosNum = 0;
|
||||
}
|
||||
else {
|
||||
var state = this._pathStates[this._pathPosNum][0];
|
||||
var time = this._animationTime;
|
||||
if(!Array.isArray(state)) state = this._pathStates[this._pathPosNum];
|
||||
else if (this._pathStates[this._pathPosNum][1]) time = this._pathStates[this._pathPosNum][1];
|
||||
if(!this._isAutoWalking) this.animateToState(state, time);
|
||||
this._isAutoWalking = true;
|
||||
}
|
||||
}
|
||||
|
||||
// start animation
|
||||
this._isAnimating = true;
|
||||
},
|
||||
|
||||
recenter : function (newpoint) {
|
||||
// stop animation
|
||||
this._isAnimating = this._isAutoWalking = false;
|
||||
|
||||
var newpanX = (newpoint[0]-presenter.sceneCenter[0]) * presenter.sceneRadiusInv;
|
||||
var newpanY = (newpoint[1]-presenter.sceneCenter[1]) * presenter.sceneRadiusInv;
|
||||
var newpanZ = (newpoint[2]-presenter.sceneCenter[2]) * presenter.sceneRadiusInv;
|
||||
|
||||
this.animateToState([sglRadToDeg(this._phi), sglRadToDeg(this._theta), newpanX, newpanY, newpanZ, (this._distance * 0.6)]);
|
||||
},
|
||||
|
||||
tick : function (dt) {
|
||||
if(!this._isAnimating) return false;
|
||||
|
||||
var deltaPhi = this._speedPhi * dt;
|
||||
var deltaTheta = this._speedTheta * dt;
|
||||
var deltaDistance = this._speedDistance * dt;
|
||||
var deltaPanX = this._speedPanX * dt;
|
||||
var deltaPanY = this._speedPanY * dt;
|
||||
var deltaPanZ = this._speedPanZ * dt;
|
||||
|
||||
var diffPhi = this._targetPhi - this._phi;
|
||||
var diffTheta = this._targetTheta - this._theta;
|
||||
var diffDistance = this._targetDistance - this._distance;
|
||||
var diffPanX = this._targetPanX - this._panX;
|
||||
var diffPanY = this._targetPanY - this._panY;
|
||||
var diffPanZ = this._targetPanZ - this._panZ;
|
||||
|
||||
if (diffPhi > deltaPhi)
|
||||
this._phi += deltaPhi;
|
||||
else if (diffPhi < -deltaPhi)
|
||||
this._phi -= deltaPhi;
|
||||
else
|
||||
this._phi = this._targetPhi;
|
||||
|
||||
if (diffTheta > deltaTheta)
|
||||
this._theta += deltaTheta;
|
||||
else if (diffTheta < -deltaTheta)
|
||||
this._theta -= deltaTheta;
|
||||
else
|
||||
this._theta = this._targetTheta;
|
||||
|
||||
if (diffDistance > deltaDistance)
|
||||
this._distance += deltaDistance;
|
||||
else if (diffDistance < -deltaDistance)
|
||||
this._distance -= deltaDistance;
|
||||
else
|
||||
this._distance = this._targetDistance;
|
||||
|
||||
if (diffPanX > deltaPanX)
|
||||
this._panX += deltaPanX;
|
||||
else if (diffPanX < -deltaPanX)
|
||||
this._panX -= deltaPanX;
|
||||
else
|
||||
this._panX = this._targetPanX;
|
||||
|
||||
if (diffPanY > deltaPanY)
|
||||
this._panY += deltaPanY;
|
||||
else if (diffPanY < -deltaPanY)
|
||||
this._panY -= deltaPanY;
|
||||
else
|
||||
this._panY = this._targetPanY;
|
||||
|
||||
if (diffPanZ > deltaPanZ)
|
||||
this._panZ += deltaPanZ;
|
||||
else if (diffPanZ < -deltaPanZ)
|
||||
this._panZ -= deltaPanZ;
|
||||
else
|
||||
this._panZ = this._targetPanZ;
|
||||
|
||||
if(this._phi == this._targetPhi)
|
||||
if(this._theta == this._targetTheta)
|
||||
if(this._distance == this._targetDistance)
|
||||
if(this._panX == this._targetPanX)
|
||||
if(this._panY == this._targetPanY)
|
||||
if(this._panZ == this._targetPanZ){
|
||||
this._isAnimating = false;
|
||||
if(typeof onTrackballArrived != "undefined")
|
||||
onTrackballArrived(this.getState());
|
||||
if(this._isAutoWalking) { this._pathPosNum++; this._isAutoWalking = false; this.animateToState(); }
|
||||
}
|
||||
|
||||
this._computeMatrix();
|
||||
return true;
|
||||
},
|
||||
|
||||
set action(a) { if(this._action != a) { this._new_action = true; this._action = a; } },
|
||||
|
||||
get action() { return this._action; },
|
||||
|
||||
get matrix() { this._computeMatrix(); return this._matrix; },
|
||||
|
||||
get distance() { return this._distance; },
|
||||
|
||||
reset : function () {
|
||||
this._matrix = SglMat4.identity();
|
||||
this._action = SGL_TRACKBALL_NO_ACTION;
|
||||
this._new_action = true;
|
||||
|
||||
this._phi = this._startPhi;
|
||||
this._theta = this._startTheta;
|
||||
this._distance = this._startDistance;
|
||||
this._panX = this._startPanX;
|
||||
this._panY = this._startPanY;
|
||||
this._panZ = this._startPanZ;
|
||||
|
||||
this._pathPosNum = 0;
|
||||
|
||||
this._isAutoWalking = false;
|
||||
this._isAnimating = false;
|
||||
|
||||
this._computeMatrix();
|
||||
},
|
||||
|
||||
track : function(m, x, y, z) {
|
||||
if(this._animationLocked && this._isAnimating) this._action = SGL_TRACKBALL_NO_ACTION;
|
||||
if(this._new_action) {
|
||||
this._start[0] = x;
|
||||
this._start[1] = y;
|
||||
this._new_action = false;
|
||||
}
|
||||
|
||||
var dx = this._start[0] - x;
|
||||
var dy = this._start[1] - y;
|
||||
this._start[0] = x;
|
||||
this._start[1] = y;
|
||||
|
||||
switch (this._action) {
|
||||
case SGL_TRACKBALL_ROTATE:
|
||||
this._isAnimating = this._isAutoWalking = false; //stopping animation
|
||||
this.rotate(m, dx, dy);
|
||||
break;
|
||||
|
||||
case SGL_TRACKBALL_PAN:
|
||||
this._isAnimating = this._isAutoWalking = false; //stopping animation
|
||||
this.pan(m, dx, dy);
|
||||
break;
|
||||
|
||||
case SGL_TRACKBALL_SCALE:
|
||||
this._isAnimating = this._isAutoWalking = false; //stopping animation
|
||||
this.scale(m, z);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return this._computeMatrix();
|
||||
},
|
||||
|
||||
pan: function(m, dx, dy) {
|
||||
//determining current X, Y and Z axis
|
||||
var Xvec = [1.0, 0.0, 0.0, 1.0];
|
||||
var Yvec = [0.0, 1.0, 0.0, 1.0];
|
||||
var Zvec = [0.0, 0.0, 1.0, 1.0];
|
||||
Xvec = SglMat4.mul4(SglMat4.rotationAngleAxis(this._phi, [0.0, -1.0, 0.0]), Xvec);
|
||||
Yvec = SglMat4.mul4(SglMat4.rotationAngleAxis(this._phi, [0.0, -1.0, 0.0]), Yvec);
|
||||
Zvec = SglMat4.mul4(SglMat4.rotationAngleAxis(this._phi, [0.0, -1.0, 0.0]), Zvec);
|
||||
Xvec = SglMat4.mul4(SglMat4.rotationAngleAxis(this._theta, [1.0, 0.0, 0.0]), Xvec);
|
||||
Yvec = SglMat4.mul4(SglMat4.rotationAngleAxis(this._theta, [1.0, 0.0, 0.0]), Yvec);
|
||||
Zvec = SglMat4.mul4(SglMat4.rotationAngleAxis(this._theta, [1.0, 0.0, 0.0]), Zvec);
|
||||
|
||||
var panSpeed = Math.max(Math.min(1.5, this._distance),0.05);
|
||||
this._panX += ((dx * Xvec[0]) + (dy * Xvec[1])) * panSpeed;
|
||||
this._panY += ((dx * Yvec[0]) + (dy * Yvec[1])) * panSpeed;
|
||||
this._panZ += ((dx * Zvec[0]) + (dy * Zvec[1])) * panSpeed;
|
||||
|
||||
//clamping
|
||||
this._panX = this._clamp(this._panX, this._minMaxPanX[0], this._minMaxPanX[1]);
|
||||
this._panY = this._clamp(this._panY, this._minMaxPanY[0], this._minMaxPanY[1]);
|
||||
this._panZ = this._clamp(this._panZ, this._minMaxPanZ[0], this._minMaxPanZ[1]);
|
||||
},
|
||||
|
||||
rotate: function(m, dx, dy) {
|
||||
this._phi += dx;
|
||||
if(this._limitPhi)
|
||||
this._phi = this._clamp(this._phi, this._minMaxPhi[0], this._minMaxPhi[1]);
|
||||
|
||||
// avoid eternal accumulation of rotation, just for the sake of cleanliness
|
||||
if (this._phi > 10.0) this._phi = this._phi - 10.0;
|
||||
if (this._phi < -10.0) this._phi = this._phi + 10.0;
|
||||
|
||||
this._theta += dy;
|
||||
this._theta = this._clamp(this._theta, this._minMaxTheta[0], this._minMaxTheta[1]);
|
||||
},
|
||||
|
||||
scale : function(m, s) {
|
||||
this._distance *= s;
|
||||
this._distance = this._clamp(this._distance, this._minMaxDist[0], this._minMaxDist[1]);
|
||||
}
|
||||
};
|
||||
/***********************************************************************/
|
||||
23
frontend/src/config/bootstrap.ts
Normal file
23
frontend/src/config/bootstrap.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import "@/styles/main.css"
|
||||
|
||||
|
||||
type BootstrapOptions = {
|
||||
activeLink?: string | null;
|
||||
onReady?: () => void | Promise<void>;
|
||||
};
|
||||
|
||||
export async function bootstrap(options: BootstrapOptions = {}): Promise<void> {
|
||||
const {
|
||||
activeLink = null,
|
||||
onReady,
|
||||
} = options;
|
||||
|
||||
if(activeLink !== null){setActiveLink(activeLink);}
|
||||
|
||||
await onReady?.();
|
||||
}
|
||||
|
||||
function setActiveLink(activeLink:string): void{
|
||||
const link = document.getElementById(activeLink);
|
||||
link?.classList.add('active');
|
||||
}
|
||||
0
frontend/src/config/ui.ts
Normal file
0
frontend/src/config/ui.ts
Normal file
9
frontend/src/pages/index/index.ts
Normal file
9
frontend/src/pages/index/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { bootstrap } from "@/config/bootstrap"
|
||||
|
||||
bootstrap({
|
||||
onReady:() => init()
|
||||
})
|
||||
|
||||
export function init(){
|
||||
console.log('index')
|
||||
}
|
||||
8
frontend/src/styles/main.css
Normal file
8
frontend/src/styles/main.css
Normal file
@@ -0,0 +1,8 @@
|
||||
@import "tailwindcss";
|
||||
@plugin "daisyui"; /* NOSONAR */
|
||||
|
||||
:root{
|
||||
--dc-primary: rgb(34, 69, 138);
|
||||
--dc-dark-blue: rgb(0, 15, 46);
|
||||
--dc-white: rgb(255, 255, 255);
|
||||
}
|
||||
31
frontend/tsconfig.json
Normal file
31
frontend/tsconfig.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "ESNext",
|
||||
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||
"types": ["vite/client", "node", "leaflet"],
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"erasableSyntaxOnly": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true,
|
||||
|
||||
/* Path Aliases - Molto utile per evitare ../../../ */
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
106
frontend/vite.config.ts
Normal file
106
frontend/vite.config.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import { defineConfig } from 'vitest/config'
|
||||
import type { Plugin } from 'vite'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { readdirSync } from 'node:fs'
|
||||
import tailwindcss from '@tailwindcss/vite'
|
||||
import { visualizer } from 'rollup-plugin-visualizer'
|
||||
|
||||
// MPA: raccoglie automaticamente ogni *.html nella root di frontend/ come entry di build.
|
||||
// { 'index': '/app/index.html', 'scheda': '/app/scheda.html', ... }
|
||||
const frontendRoot = fileURLToPath(new URL('.', import.meta.url))
|
||||
const htmlEntries = Object.fromEntries(
|
||||
readdirSync(frontendRoot)
|
||||
.filter((file) => file.endsWith('.html'))
|
||||
.map((file) => [file.slice(0, -'.html'.length), fileURLToPath(new URL(file, import.meta.url))])
|
||||
)
|
||||
|
||||
function mpaRewritePlugin(): Plugin {
|
||||
return {
|
||||
name: 'mpa-rewrite',
|
||||
configureServer(server: any) {
|
||||
server.middlewares.use((req: any, _res: any, next: any) => {
|
||||
if (
|
||||
req.url &&
|
||||
!req.url.includes('.') &&
|
||||
!req.url.startsWith('/@') &&
|
||||
!req.url.startsWith('/api') &&
|
||||
!req.url.startsWith('/sanctum') &&
|
||||
!req.url.startsWith('/storage') &&
|
||||
!req.url.startsWith('/documentation') &&
|
||||
!req.url.startsWith('/api-docs') &&
|
||||
req.url !== '/'
|
||||
) {
|
||||
// Mappa il primo segmento del path sul relativo .html.
|
||||
// Es: /scheda/uno-slug -> /scheda.html (lo slug resta leggibile da location.pathname)
|
||||
const firstSegment = req.url.split('?')[0].split('/')[1]
|
||||
req.url = `/${firstSegment}.html`
|
||||
}
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
tailwindcss(),
|
||||
visualizer(),
|
||||
mpaRewritePlugin()
|
||||
],
|
||||
resolve: {
|
||||
// Alias lato Vite/bundler: tsconfig `paths` vale solo per il type-check, Vite
|
||||
// ha bisogno del proprio alias per risolvere `@/...` a runtime e in build.
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||
},
|
||||
},
|
||||
build: {
|
||||
rollupOptions: {
|
||||
// MPA: auto-detect delle pagine. Ogni *.html nella root di frontend/ è un entry
|
||||
// (nome = file senza estensione). Aggiungere una pagina = creare il suo .html,
|
||||
// nessuna modifica qui. Vedi htmlEntries sotto.
|
||||
input: htmlEntries,
|
||||
}
|
||||
},
|
||||
server: {
|
||||
host: '0.0.0.0',
|
||||
allowedHosts: ['dyncoll-dev.local'],
|
||||
port: 5173,
|
||||
proxy: {
|
||||
'/api-docs': {
|
||||
target: 'http://backend:8000',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
rewrite: (path: string) => path.replace(/^\/api-docs/, '/docs')
|
||||
},
|
||||
'/api': {
|
||||
target: 'http://backend:8000',
|
||||
changeOrigin: true,
|
||||
secure: false // accetta certificati self-signed
|
||||
},
|
||||
'/sanctum': {
|
||||
target: 'http://backend:8000',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
},
|
||||
'/storage': {
|
||||
target: 'http://backend:8000',
|
||||
changeOrigin: true,
|
||||
secure: false // accetta certificati self-signed
|
||||
},
|
||||
// Documentazione utente (MkDocs) — servizio docker `docs`
|
||||
'/documentation': {
|
||||
target: 'http://docs:8000',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
rewrite: (path: string) => path.replace(/^\/documentation/, '') || '/'
|
||||
}
|
||||
}
|
||||
},
|
||||
test: {
|
||||
coverage: {
|
||||
provider: 'v8',
|
||||
reporter: ['text', 'html', 'lcov'],
|
||||
},
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user