Browse Source

add stl mesh

develop
Simon Vieille 3 months ago
parent
commit
54e0faabc3
  1. 143
      assets/css/app.scss
  2. 10
      assets/css/viewer.scss
  3. BIN
      assets/fonts/deblan/deblan-icon.eot
  4. BIN
      assets/fonts/deblan/deblan-icon.ttf
  5. BIN
      assets/fonts/deblan/deblan-icon.woff
  6. 24
      assets/fonts/deblan/src/deblan-icon.svg
  7. 4
      assets/js/app.js
  8. 42
      assets/js/app/mesh-viewer.js
  9. 11
      assets/js/viewer.js
  10. 32
      config/packages/app.yaml
  11. 6
      config/packages/framework.yaml
  12. 4
      config/packages/liip_imagine.yaml
  13. 1158
      public/stl_viewer/CanvasRenderer.js
  14. 625
      public/stl_viewer/OrbitControls.js
  15. 1028
      public/stl_viewer/Projector.js
  16. 619
      public/stl_viewer/TrackballControls.js
  17. 48
      public/stl_viewer/ie_polyfills.js
  18. 2
      public/stl_viewer/load_stl.min.js
  19. 2
      public/stl_viewer/parser.min.js
  20. 37
      public/stl_viewer/readme.txt
  21. 2
      public/stl_viewer/stl_viewer.min.js
  22. 2
      public/stl_viewer/three.min.js
  23. 54
      public/stl_viewer/webgl_detector.js
  24. 139
      src/Controller/StlMeshAdminController.php
  25. 51
      src/Controller/StlMeshController.php
  26. 12
      src/Entity/Page/MeshPage.php
  27. 110
      src/Entity/StlMesh.php
  28. 14
      src/Factory/StlMeshFactory.php
  29. 81
      src/Form/StlMeshType.php
  30. 50
      src/Repository/StlMeshRepository.php
  31. 15
      src/Repository/StlMeshRepositoryQuery.php
  32. 14
      templates/admin/menu.html.twig
  33. 5
      templates/base.html.twig
  34. 4
      templates/blog/post/_post.html.twig
  35. 14
      templates/blog/post/posts.html.twig
  36. 77
      templates/page/mesh/default.html.twig
  37. 16
      templates/page/mesh/viewer.html.twig
  38. 1
      webpack.config.js

143
assets/css/app.scss

@ -38,7 +38,7 @@ html, body {
scroll-behavior: smooth;
}
$dicons: coffee server search project share contact list response twitter diaspora github code rss linkedin mastodon pixelfed gpg matrix murph;
$dicons: coffee server search project share contact list response twitter diaspora github code rss linkedin mastodon pixelfed gpg matrix murph cube;
.d-none {
display: none;
@ -282,7 +282,6 @@ pre[class*="language-"] {
}
}
.content hr {
border: 0;
border-bottom: 1px dashed $color-hr-border;
@ -667,6 +666,132 @@ $links: (
animation-duration: 1s;
}
.modal-open {
overflow: hidden;
.modal {
overflow-x: hidden;
overflow-y: auto;
}
}
.modal {
position: fixed;
top: 0;
left: 0;
z-index: 2000;
display: none;
width: 100%;
height: 100%;
overflow: hidden;
outline: 0;
}
.modal-header {
}
.modal-dialog {
position: relative;
width: 1280px;
margin: 100px auto;
pointer-events: none;
.modal-header {
.close {
cursor: pointer;
position: absolute;
right: 15px;
top: 13px;
width: 30px;
height: 30px;
z-index: 1000 + 4;
&:hover::before, &:hover::after {
background-color: #333;
}
&:before, &:after {
position: absolute;
left: 15px;
content: ' ';
height: 30px;
width: 2px;
background-color: #333;
z-index: 2001;
ransition-property: background-color;
transition-duration: 0.3s;
}
&:before {
transform: rotate(45deg);
}
&:after {
transform: rotate(-45deg);
}
}
}
}
.modal-content {
position: relative;
display: flex;
flex-direction: column;
width: 100%;
pointer-events: auto;
background-color: #fff;
background-clip: padding-box;
outline: 0;
}
.modal-backdrop {
position: fixed;
display: none;
top: 0;
left: 0;
z-index: 1999;
width: 100vw;
height: 100vh;
background-color: #333;
opacity: 0.89;
}
.modal-body {
position: relative;
flex: 1 1 auto;
padding: 0;
height: calc(100vh - 300px);
iframe {
border: 0;
height: 100%;
width: 100%;
}
}
.mesh {
border: 1px solid $color-hr-border;
border-radius: 10px;
margin: 20px;
&-preview {
img {
border-radius: 10px;
}
}
&-title {
font-size: 16px;
padding: 10px;
color: $color-body-text;
}
&-description {
padding-left: 10px;
padding-right: 10px;
}
}
@media screen and (max-width: 980px) {
.quick-image img {
height: 200px;
@ -781,6 +906,19 @@ $links: (
border: 1px solid #000000;
border-radius: 0;
}
.mesh {
border-color: #86899f;
&-title {
color: #fff;
}
&-description {
color: #fff;
}
}
}
@media all and (max-height: 465px) {
@ -792,4 +930,3 @@ $links: (
}
}
}

10
assets/css/viewer.scss

@ -0,0 +1,10 @@
body {
margin: 0;
padding: 0;
overflow: hidden;
}
#mesh-viewer {
width: 100vw;
height: 100vh;
}

BIN
assets/fonts/deblan/deblan-icon.eot

Binary file not shown.

BIN
assets/fonts/deblan/deblan-icon.ttf

Binary file not shown.

BIN
assets/fonts/deblan/deblan-icon.woff

Binary file not shown.

24
assets/fonts/deblan/src/deblan-icon.svg

@ -41,9 +41,9 @@
showgrid="false"
showguides="true"
inkscape:guide-bbox="true"
inkscape:zoom="0.70710678"
inkscape:cx="813.74735"
inkscape:cy="51.104949"
inkscape:zoom="0.1767767"
inkscape:cx="2355.8362"
inkscape:cy="1162.0935"
inkscape:window-x="0"
inkscape:window-y="21"
inkscape:current-layer="svg5496"
@ -177,6 +177,11 @@
id="glyph1007"
unicode="murph"
d="m 137.30469,982.25195 c -52.575606,0 -94.902346,-42.32673 -94.902346,-94.90234 V 93.9707 c 0,-26.02529 10.374685,-49.53596 27.226562,-66.64843 h 0.332032 L 379.07227,336.42969 431.47461,192.73047 h 44.19922 l 137.93555,378.23047 2.96679,2.96484 71.59375,-372.16406 h 119.80664 l -95.47461,468.08789 284.99414,284.98828 c -17.12583,16.95602 -40.69709,27.41406 -66.8125,27.41406 z m 860.1914,-27.41406 c 0.15107,-0.14631 0.002,0.002 0.002,0.002 z m -764.45703,-56.1582 h 65.60156 L 414.15234,539.0293 331.38477,456.23438 287.23242,577.31641 245.91992,370.76758 227.04883,351.88867 96.787109,221.58203 Z m 363.66992,0 h 65.12891 l 18.95117,-92.92188 -7.6289,-7.63281 -157.52149,-157.57227 z" />
<glyph
glyph-name="glyphe 20"
id="glyph1001"
unicode="cube"
d="m 518.45763,958.03619 c -6.08548,-0.0798 -13.20959,-1.2371 -22.13782,-4.26504 L 170.49311,831.28346 c -26.41283,-11.69072 -49.4246,-27.91669 -50.34857,-73.59319 V 410.3969 c -0.33616,-55.18412 22.54804,-57.59151 40.02139,-72.29871 L 485.50765,179.2971 c 23.95624,-13.05801 46.13769,-6.59485 68.42467,-1.29092 l 322.75639,165.25571 c 17.67284,10.72056 36.21407,18.12146 43.89761,67.13501 l -1.29445,352.84931 c -3.55377,33.76392 -19.68358,55.79086 -49.05766,65.45539 L 543.76563,953.1257 c -7.90878,2.1834 -15.16554,5.04354 -25.308,4.91049 z M 517.61604,851.7991 818.87291,740.4233 519.44184,619.00542 219.09787,739.51038 Z M 817.5,631.33337 818.4129,421.3626 569.19131,300.85763 l 2.7387,230.05494 z" />
</font>
<symbol
id="beer">
@ -320,7 +325,7 @@
</defs>
<path
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;vector-effect:none;fill:#1e2430;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:40;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers;enable-background:accumulate"
d="m 137.30469,41.748047 c -52.575606,0 -94.902346,42.326737 -94.902346,94.902343 V 930.0293 c 0,26.02529 10.374685,49.53596 27.226562,66.64843 h 0.332032 L 379.07227,687.57031 431.47461,831.26953 h 44.19922 l 137.93555,-378.23047 2.96679,-2.96484 71.59375,372.16406 H 807.97656 L 712.50195,354.15039 997.49609,69.162109 C 980.37026,52.206085 956.799,41.748047 930.68359,41.748047 Z m 860.1914,27.414062 c 0.15107,0.14631 0.002,-0.0019 0.002,-0.002 z M 233.03906,125.32031 h 65.60156 L 414.15234,484.9707 331.38477,567.76562 287.23242,446.68359 245.91992,653.23242 227.04883,672.11133 96.787109,802.41797 Z m 363.66992,0 h 65.12891 l 18.95117,92.92188 -7.6289,7.63281 -157.52149,157.57227 z"
d="m 4832.4937,2078.2156 c -52.5756,0 -94.9023,42.3267 -94.9023,94.9023 v 793.3789 c 0,26.0253 10.3747,49.536 27.2265,66.6485 h 0.3321 l 309.1113,-309.1075 52.4023,143.6993 h 44.1993 l 137.9355,-378.2305 2.9668,-2.9648 71.5937,372.164 h 119.8067 l -95.4746,-468.0879 284.9941,-284.9883 c -17.1258,-16.956 -40.6971,-27.414 -66.8125,-27.414 z m 860.1914,27.414 c 0.1511,0.1463 0,-0 0,-0 z m -764.457,56.1582 h 65.6015 l 115.5118,359.6504 -82.7676,82.795 -44.1524,-121.0821 -41.3125,206.5489 -18.871,18.8789 -130.2618,130.3066 z m 363.6699,0 h 65.1289 l 18.9512,92.9219 -7.6289,7.6328 -157.5215,157.5723 z"
id="rect2455"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ssscccccccccccsscccccccccccccccccc" />
@ -430,4 +435,15 @@
d="m -889.13054,3204.4306 v 808.4067 h 215.79586 v -59.3455 H -816.50711 V 3263.774 h 143.17243 v -59.3434 z m 626.1962,0 v 59.3434 h 143.17243 v 689.7178 h -143.17243 v 59.3455 h 215.797948 v -808.4067 z m -247.17033,256.5367 c -16.20834,0 -30.79551,3.4966 -43.76218,10.4918 -12.96667,6.9951 -26.78823,16.7213 -41.46101,29.1761 v -31.7335 h -89.57147 v 287.3989 h 89.57147 v -203.7147 c 6.65399,-4.6066 13.48022,-8.6143 20.47541,-12.0266 6.99508,-3.4123 14.15941,-5.1188 21.49581,-5.1188 9.55444,0 16.97739,1.4499 22.26636,4.3504 5.45964,2.7298 9.46941,7.1652 12.02867,13.3073 2.55916,6.142 4.09469,14.1617 4.60655,24.0573 0.6825,9.725 1.0225,21.5834 1.0225,35.5737 v 143.5714 h 90.08379 v -203.7147 c 7.84823,-5.4596 15.10018,-9.6385 21.75406,-12.5389 6.65399,-3.0711 13.39262,-4.6065 20.21715,-4.6065 9.725,0 17.23144,1.4499 22.52042,4.3504 5.45973,2.7298 9.46951,7.1652 12.02867,13.3073 2.55926,6.142 4.09478,14.0762 4.60655,23.8012 0.51185,9.725 0.76845,21.6688 0.76845,35.8298 v 143.5714 h 90.08378 v -187.0795 c 0,-20.4736 -2.04664,-37.0219 -6.14136,-49.6474 -3.92406,-12.796 -9.89498,-23.8011 -17.91387,-33.0142 -7.33638,-8.5308 -16.29486,-14.9294 -26.87292,-19.1947 -10.57805,-4.2653 -22.51978,-6.3975 -35.82775,-6.3975 -15.69647,0 -31.22256,4.3499 -46.57775,13.0512 -15.18463,8.5306 -31.39284,20.8146 -48.62488,36.8524 -6.99519,-16.5496 -17.14699,-29.0025 -30.45485,-37.3626 -13.30787,-8.3601 -28.74836,-12.541 -46.3216,-12.541 z"
id="path1008"
inkscape:connector-curvature="0" />
<circle
id="path970"
style="fill:#000000;stroke:none"
cx="816"
cy="392.66663"
r="1.5" />
<path
style="fill:#4d4d4d;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 518.45763,65.963806 c -6.08548,0.0798 -13.20959,1.2371 -22.13782,4.26504 L 170.49311,192.71654 c -26.41283,11.69072 -49.4246,27.91669 -50.34857,73.59319 V 613.6031 c -0.33616,55.18412 22.54804,57.59151 40.02139,72.29871 L 485.50765,844.7029 c 23.95624,13.05801 46.13769,6.59485 68.42467,1.29092 L 876.68871,680.73811 c 17.67284,-10.72056 36.21407,-18.12146 43.89761,-67.13501 L 919.29187,260.75379 C 915.7381,226.98987 899.60829,204.96293 870.23421,195.2984 L 543.76563,70.874296 c -7.90878,-2.1834 -15.16554,-5.04354 -25.308,-4.91049 z M 517.61604,172.2009 818.87291,283.5767 519.44184,404.99458 219.09787,284.48962 Z M 817.5,392.66663 l 0.9129,209.97077 -249.22159,120.50497 2.7387,-230.05494 z"
id="path980"
inkscape:connector-curvature="0" />
</svg>

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 72 KiB

4
assets/js/app.js

@ -10,6 +10,7 @@ const Knmc = require('./app/knmc')
const VideoRatio = require('./app/video-ratio')
const Stats = require('./app/stats')
const Particles = require('./app/particles')
const MeshViewer = require('./app/mesh-viewer')
const app = new App([
new FormPnw(window),
@ -20,7 +21,8 @@ const app = new App([
new Knmc(window),
new VideoRatio(window),
// new Stats(),
new Particles(window)
new Particles(window),
new MeshViewer(window)
])
window.addEventListener('load', function () {

42
assets/js/app/mesh-viewer.js

@ -0,0 +1,42 @@
const MeshViewer = function (w) {
this.window = w
}
MeshViewer.prototype.init = function () {
const openers = this.window.document.querySelectorAll('*[data-modal]')
const backdrop = this.window.document.querySelector('.modal-backdrop')
const body = this.window.document.querySelector('body')
for (let i = 0, len = openers.length; i < len; i++) {
openers[i].addEventListener('click', (e) => {
e.preventDefault()
let target = e.target
if (target.tagName != 'A') {
target = target.parentNode
}
const modal = this.window.document.querySelector('#mesh-viewer')
const modalBody = modal.querySelector('.modal-body')
modal.style.display = 'block'
modal.classList.add('show')
modalBody.innerHTML = '<iframe src="' + target.getAttribute('href') + '"></iframe>'
body.classList.add('modal-open')
backdrop.style.display = 'block'
modal.querySelector('.close').addEventListener('click', () => {
modal.style.display = 'none'
modal.classList.remove('show')
body.classList.remove('modal-open')
backdrop.style.display = 'none'
modalBody.innerHTML = ''
})
})
}
}
module.exports = MeshViewer

11
assets/js/viewer.js

@ -0,0 +1,11 @@
import '../css/viewer.scss'
const container = document.getElementById('mesh-viewer')
const viewer = new StlViewer(
container,
{
auto_rotate: true,
allow_drag_and_drop: true,
models: [{filename: container.getAttribute('data-file')}]
}
);

32
config/packages/app.yaml

@ -15,6 +15,7 @@ core:
- {name: 'Blog\PostController::rss', action: 'App\Controller\Blog\PostController::rss'}
- {name: 'Blog\PostController::jsonApi', action: 'App\Controller\Blog\PostController::jsonApi'}
- {name: 'Blog\CategoryController::categories', action: 'App\Controller\Blog\CategoryController::categories'}
- {name: 'StlMeshController::meshes', action: 'App\Controller\StlMeshController::meshes'}
pages:
App\Entity\Page\SimplePage:
name: 'Page de contenu'
@ -36,12 +37,43 @@ core:
name: 'RSS'
templates:
- {name: "Par défaut", file: "page/rss/default.xml.twig"}
App\Entity\Page\MeshPage:
name: 'Mesh'
templates:
- {name: "Par défaut", file: "page/mesh/default.html.twig"}
App\Entity\Page\TextPage:
name: 'Texte'
templates:
- {name: "Par défaut", file: "page/text/default.txt.twig"}
file_manager:
mimes:
- image/png
- image/jpg
- image/jpeg
- image/gif
- image/svg+xml
- video/mp4
- audio/mpeg3
- audio/x-mpeg-3
- multipart/x-zip
- multipart/x-gzip
- application/pdf
- application/ogg
- application/zip
- application/rar
- application/x-rar-compressed
- application/x-zip-compressed
- application/tar
- application/x-tar
- application/x-bzip
- application/x-bzip2
- application/x-gzip
- application/octet-stream
- application/msword
- text/plain
- text/css
- application/sla
path_locked:
- "%kernel.project_dir%/public/uploads/page"
- "%kernel.project_dir%/public/uploads/post"

6
config/packages/framework.yaml

@ -16,6 +16,6 @@ framework:
php_errors:
log: true
assets:
base_urls:
- "%env(ASSET_BASE_URL)%"
# assets:
# base_urls:
# - "%env(ASSET_BASE_URL)%"

4
config/packages/liip_imagine.yaml

@ -7,3 +7,7 @@ liip_imagine:
filters:
downscale:
max: [120, 120]
mesh_preview_filter:
filters:
downscale:
max: [300, 300]

1158
public/stl_viewer/CanvasRenderer.js

File diff suppressed because it is too large

625
public/stl_viewer/OrbitControls.js

@ -0,0 +1,625 @@
/**
* @author qiao / https://github.com/qiao
* @author mrdoob / http://mrdoob.com
* @author alteredq / http://alteredqualia.com/
* @author WestLangley / http://github.com/WestLangley
* @author erich666 / http://erichaines.com
*/
/*global THREE, console */
// This set of controls performs orbiting, dollying (zooming), and panning. It maintains
// the "up" direction as +Y, unlike the TrackballControls. Touch on tablet and phones is
// supported.
//
// Orbit - left mouse / touch: one finger move
// Zoom - middle mouse, or mousewheel / touch: two finger spread or squish
// Pan - right mouse, or arrow keys / touch: three finter swipe
//
// This is a drop-in replacement for (most) TrackballControls used in examples.
// That is, include this js file and wherever you see:
// controls = new THREE.TrackballControls( camera );
// controls.target.z = 150;
// Simple substitute "OrbitControls" and the control should work as-is.
THREE.OrbitControls = function ( object, domElement ) {
this.object = object;
this.domElement = ( domElement !== undefined ) ? domElement : document;
// API
// Set to false to disable this control
this.enabled = true;
// "target" sets the location of focus, where the control orbits around
// and where it pans with respect to.
this.target = new THREE.Vector3();
// center is old, deprecated; use "target" instead
this.center = this.target;
// This option actually enables dollying in and out; left as "zoom" for
// backwards compatibility
this.noZoom = false;
this.zoomSpeed = 1.0;
// Limits to how far you can dolly in and out
this.minDistance = 0;
this.maxDistance = Infinity;
// Set to true to disable this control
this.noRotate = false;
this.rotateSpeed = 1.0;
// Set to true to disable this control
this.noPan = false;
this.keyPanSpeed = 7.0; // pixels moved per arrow key push
// Set to true to automatically rotate around the target
this.autoRotate = false;
this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
// How far you can orbit vertically, upper and lower limits.
// Range is 0 to Math.PI radians.
this.minPolarAngle = 0; // radians
this.maxPolarAngle = Math.PI; // radians
// Set to true to disable use of the keys
this.noKeys = false;
// The four arrow keys
this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
////////////
// internals
var scope = this;
var EPS = 0.000001;
var rotateStart = new THREE.Vector2();
var rotateEnd = new THREE.Vector2();
var rotateDelta = new THREE.Vector2();
var panStart = new THREE.Vector2();
var panEnd = new THREE.Vector2();
var panDelta = new THREE.Vector2();
var panOffset = new THREE.Vector3();
var offset = new THREE.Vector3();
var dollyStart = new THREE.Vector2();
var dollyEnd = new THREE.Vector2();
var dollyDelta = new THREE.Vector2();
var phiDelta = 0;
var thetaDelta = 0;
var scale = 1;
var pan = new THREE.Vector3();
var lastPosition = new THREE.Vector3();
var STATE = { NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 };
var state = STATE.NONE;
// for reset
this.target0 = this.target.clone();
this.position0 = this.object.position.clone();
// events
var changeEvent = { type: 'change' };
var startEvent = { type: 'start'};
var endEvent = { type: 'end'};
this.rotateLeft = function ( angle ) {
if ( angle === undefined ) {
angle = getAutoRotationAngle();
}
thetaDelta -= angle;
};
this.rotateUp = function ( angle ) {
if ( angle === undefined ) {
angle = getAutoRotationAngle();
}
phiDelta -= angle;
};
// pass in distance in world space to move left
this.panLeft = function ( distance ) {
var te = this.object.matrix.elements;
// get X column of matrix
panOffset.set( te[ 0 ], te[ 1 ], te[ 2 ] );
panOffset.multiplyScalar( - distance );
pan.add( panOffset );
};
// pass in distance in world space to move up
this.panUp = function ( distance ) {
var te = this.object.matrix.elements;
// get Y column of matrix
panOffset.set( te[ 4 ], te[ 5 ], te[ 6 ] );
panOffset.multiplyScalar( distance );
pan.add( panOffset );
};
// pass in x,y of change desired in pixel space,
// right and down are positive
this.pan = function ( deltaX, deltaY ) {
var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
if ( scope.object.fov !== undefined ) {
// perspective
var position = scope.object.position;
var offset = position.clone().sub( scope.target );
var targetDistance = offset.length();
// half of the fov is center to top of screen
targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 );
// we actually don't use screenWidth, since perspective camera is fixed to screen height
scope.panLeft( 2 * deltaX * targetDistance / element.clientHeight );
scope.panUp( 2 * deltaY * targetDistance / element.clientHeight );
} else if ( scope.object.top !== undefined ) {
// orthographic
scope.panLeft( deltaX * (scope.object.right - scope.object.left) / element.clientWidth );
scope.panUp( deltaY * (scope.object.top - scope.object.bottom) / element.clientHeight );
} else {
// camera neither orthographic or perspective
console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
}
};
this.dollyIn = function ( dollyScale ) {
if ( dollyScale === undefined ) {
dollyScale = getZoomScale();
}
scale /= dollyScale;
};
this.dollyOut = function ( dollyScale ) {
if ( dollyScale === undefined ) {
dollyScale = getZoomScale();
}
scale *= dollyScale;
};
this.update = function () {
var position = this.object.position;
offset.copy( position ).sub( this.target );
// angle from z-axis around y-axis
var theta = Math.atan2( offset.x, offset.z );
// angle from y-axis
var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y );
if ( this.autoRotate ) {
this.rotateLeft( getAutoRotationAngle() );
}
theta += thetaDelta;
phi += phiDelta;
// restrict phi to be between desired limits
phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) );
// restrict phi to be betwee EPS and PI-EPS
phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) );
var radius = offset.length() * scale;
// restrict radius to be between desired limits
radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) );
// move target to panned location
this.target.add( pan );
offset.x = radius * Math.sin( phi ) * Math.sin( theta );
offset.y = radius * Math.cos( phi );
offset.z = radius * Math.sin( phi ) * Math.cos( theta );
position.copy( this.target ).add( offset );
this.object.lookAt( this.target );
thetaDelta = 0;
phiDelta = 0;
scale = 1;
pan.set( 0, 0, 0 );
if ( lastPosition.distanceTo( this.object.position ) > 0 ) {
this.dispatchEvent( changeEvent );
lastPosition.copy( this.object.position );
}
};
this.reset = function () {
state = STATE.NONE;
this.target.copy( this.target0 );
//this.object.position.copy( this.position0 );
this.update();
};
function getAutoRotationAngle() {
return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
}
function getZoomScale() {
return Math.pow( 0.95, scope.zoomSpeed );
}
function onMouseDown( event ) {
if ( scope.enabled === false ) return;
event.preventDefault();
if ( event.button === 0 ) {
if ( scope.noRotate === true ) return;
state = STATE.ROTATE;
rotateStart.set( event.clientX, event.clientY );
} else if ( event.button === 1 ) {
if ( scope.noZoom === true ) return;
state = STATE.DOLLY;
dollyStart.set( event.clientX, event.clientY );
} else if ( event.button === 2 ) {
if ( scope.noPan === true ) return;
state = STATE.PAN;
panStart.set( event.clientX, event.clientY );
}
scope.domElement.addEventListener( 'mousemove', onMouseMove, false );
scope.domElement.addEventListener( 'mouseup', onMouseUp, false );
scope.dispatchEvent( startEvent );
}
function onMouseMove( event ) {
if ( scope.enabled === false ) return;
event.preventDefault();
var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
if ( state === STATE.ROTATE ) {
if ( scope.noRotate === true ) return;
rotateEnd.set( event.clientX, event.clientY );
rotateDelta.subVectors( rotateEnd, rotateStart );
// rotating across whole screen goes 360 degrees around
scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
// rotating up and down along whole screen attempts to go 360, but limited to 180
scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
rotateStart.copy( rotateEnd );
} else if ( state === STATE.DOLLY ) {
if ( scope.noZoom === true ) return;
dollyEnd.set( event.clientX, event.clientY );
dollyDelta.subVectors( dollyEnd, dollyStart );
if ( dollyDelta.y > 0 ) {
scope.dollyIn();
} else {
scope.dollyOut();
}
dollyStart.copy( dollyEnd );
} else if ( state === STATE.PAN ) {
if ( scope.noPan === true ) return;
panEnd.set( event.clientX, event.clientY );
panDelta.subVectors( panEnd, panStart );
scope.pan( panDelta.x, panDelta.y );
panStart.copy( panEnd );
}
scope.update();
}
function onMouseUp( /* event */ ) {
if ( scope.enabled === false ) return;
scope.domElement.removeEventListener( 'mousemove', onMouseMove, false );
scope.domElement.removeEventListener( 'mouseup', onMouseUp, false );
scope.dispatchEvent( endEvent );
state = STATE.NONE;
}
function onMouseWheel( event ) {
if ( scope.enabled === false || scope.noZoom === true ) return;
event.preventDefault();
var delta = 0;
if ( event.wheelDelta !== undefined ) { // WebKit / Opera / Explorer 9
delta = event.wheelDelta;
} else if ( event.detail !== undefined ) { // Firefox
delta = - event.detail;
}
if ( delta > 0 ) {
scope.dollyOut();
} else {
scope.dollyIn();
}
scope.update();
scope.dispatchEvent( startEvent );
scope.dispatchEvent( endEvent );
}
function onKeyDown( event ) {
if ( scope.enabled === false || scope.noKeys === true || scope.noPan === true ) return;
switch ( event.keyCode ) {
case scope.keys.UP:
scope.pan( 0, scope.keyPanSpeed );
scope.update();
break;
case scope.keys.BOTTOM:
scope.pan( 0, - scope.keyPanSpeed );
scope.update();
break;
case scope.keys.LEFT:
scope.pan( scope.keyPanSpeed, 0 );
scope.update();
break;
case scope.keys.RIGHT:
scope.pan( - scope.keyPanSpeed, 0 );
scope.update();
break;
}
}
function touchstart( event ) {
if ( scope.enabled === false ) return;
switch ( event.touches.length ) {
case 1: // one-fingered touch: rotate
if ( scope.noRotate === true ) return;
state = STATE.TOUCH_ROTATE;
rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
break;
case 2: // two-fingered touch: dolly
if ( scope.noZoom === true ) return;
state = STATE.TOUCH_DOLLY;
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
var distance = Math.sqrt( dx * dx + dy * dy );
dollyStart.set( 0, distance );
break;
case 3: // three-fingered touch: pan
if ( scope.noPan === true ) return;
state = STATE.TOUCH_PAN;
panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
break;
default:
state = STATE.NONE;
}
scope.dispatchEvent( startEvent );
}
function touchmove( event ) {
if ( scope.enabled === false ) return;
event.preventDefault();
event.stopPropagation();
var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
switch ( event.touches.length ) {
case 1: // one-fingered touch: rotate
if ( scope.noRotate === true ) return;
if ( state !== STATE.TOUCH_ROTATE ) return;
rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
rotateDelta.subVectors( rotateEnd, rotateStart );
// rotating across whole screen goes 360 degrees around
scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
// rotating up and down along whole screen attempts to go 360, but limited to 180
scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
rotateStart.copy( rotateEnd );
scope.update();
break;
case 2: // two-fingered touch: dolly
if ( scope.noZoom === true ) return;
if ( state !== STATE.TOUCH_DOLLY ) return;
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
var distance = Math.sqrt( dx * dx + dy * dy );
dollyEnd.set( 0, distance );
dollyDelta.subVectors( dollyEnd, dollyStart );
if ( dollyDelta.y > 0 ) {
scope.dollyOut();
} else {
scope.dollyIn();
}
dollyStart.copy( dollyEnd );
scope.update();
break;
case 3: // three-fingered touch: pan
if ( scope.noPan === true ) return;
if ( state !== STATE.TOUCH_PAN ) return;
panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
panDelta.subVectors( panEnd, panStart );
scope.pan( panDelta.x, panDelta.y );
panStart.copy( panEnd );
scope.update();
break;
default:
state = STATE.NONE;
}
}
function touchend( /* event */ ) {
if ( scope.enabled === false ) return;
scope.dispatchEvent( endEvent );
state = STATE.NONE;
}
this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
this.domElement.addEventListener( 'mousedown', onMouseDown, false );
this.domElement.addEventListener( 'mousewheel', onMouseWheel, false );
this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox
this.domElement.addEventListener( 'touchstart', touchstart, false );
this.domElement.addEventListener( 'touchend', touchend, false );
this.domElement.addEventListener( 'touchmove', touchmove, false );
window.addEventListener( 'keydown', onKeyDown, false );
};
THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );

1028
public/stl_viewer/Projector.js

File diff suppressed because it is too large

619
public/stl_viewer/TrackballControls.js

@ -0,0 +1,619 @@
/**
* @author Eberhard Graether / http://egraether.com/
* @author Mark Lundin / http://mark-lundin.com
* @author Simone Manini / http://daron1337.github.io
* @author Luca Antiga / http://lantiga.github.io
*/
THREE.TrackballControls = function ( object, domElement ) {
var _this = this;
var STATE = { NONE: - 1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM_PAN: 4 };
this.object = object;
this.domElement = ( domElement !== undefined ) ? domElement : document;
// API
this.enabled = true;
this.screen = { left: 0, top: 0, width: 0, height: 0 };
this.rotateSpeed = 1.0;
this.zoomSpeed = 1.2;
this.panSpeed = 0.3;
this.noRotate = false;
this.noZoom = false;
this.noPan = false;
this.staticMoving = false;
this.dynamicDampingFactor = 0.2;
this.minDistance = 0;
this.maxDistance = Infinity;
this.keys = [ 65 /*A*/, 83 /*S*/, 68 /*D*/ ];
// internals
this.target = new THREE.Vector3();
var EPS = 0.000001;
var lastPosition = new THREE.Vector3();
var _state = STATE.NONE,
_prevState = STATE.NONE,
_eye = new THREE.Vector3(),
_movePrev = new THREE.Vector2(),
_moveCurr = new THREE.Vector2(),
_lastAxis = new THREE.Vector3(),
_lastAngle = 0,
_zoomStart = new THREE.Vector2(),
_zoomEnd = new THREE.Vector2(),
_touchZoomDistanceStart = 0,
_touchZoomDistanceEnd = 0,
_panStart = new THREE.Vector2(),
_panEnd = new THREE.Vector2();
// for reset
this.target0 = this.target.clone();
this.position0 = this.object.position.clone();
this.up0 = this.object.up.clone();
// events
var changeEvent = { type: 'change' };
var startEvent = { type: 'start' };
var endEvent = { type: 'end' };
// methods
this.handleResize = function () {
if ( this.domElement === document ) {
this.screen.left = 0;
this.screen.top = 0;
this.screen.width = window.innerWidth;
this.screen.height = window.innerHeight;
} else {
var box = this.domElement.getBoundingClientRect();
// adjustments come from similar code in the jquery offset() function
var d = this.domElement.ownerDocument.documentElement;
this.screen.left = box.left + window.pageXOffset - d.clientLeft;
this.screen.top = box.top + window.pageYOffset - d.clientTop;
this.screen.width = box.width;
this.screen.height = box.height;
}
};
var getMouseOnScreen = ( function () {
var vector = new THREE.Vector2();
return function getMouseOnScreen( pageX, pageY ) {
vector.set(
( pageX - _this.screen.left ) / _this.screen.width,
( pageY - _this.screen.top ) / _this.screen.height
);
return vector;
};
}() );
var getMouseOnCircle = ( function () {
var vector = new THREE.Vector2();
return function getMouseOnCircle( pageX, pageY ) {
vector.set(
( ( pageX - _this.screen.width * 0.5 - _this.screen.left ) / ( _this.screen.width * 0.5 ) ),
( ( _this.screen.height + 2 * ( _this.screen.top - pageY ) ) / _this.screen.width ) // screen.width intentional
);
return vector;
};
}() );
this.rotateCamera = ( function () {
var axis = new THREE.Vector3(),
quaternion = new THREE.Quaternion(),
eyeDirection = new THREE.Vector3(),
objectUpDirection = new THREE.Vector3(),
objectSidewaysDirection = new THREE.Vector3(),
moveDirection = new THREE.Vector3(),
angle;
return function rotateCamera() {
moveDirection.set( _moveCurr.x - _movePrev.x, _moveCurr.y - _movePrev.y, 0 );
angle = moveDirection.length();
if ( angle ) {
_eye.copy( _this.object.position ).sub( _this.target );
eyeDirection.copy( _eye ).normalize();
objectUpDirection.copy( _this.object.up ).normalize();
objectSidewaysDirection.crossVectors( objectUpDirection, eyeDirection ).normalize();
objectUpDirection.setLength( _moveCurr.y - _movePrev.y );
objectSidewaysDirection.setLength( _moveCurr.x - _movePrev.x );
moveDirection.copy( objectUpDirection.add( objectSidewaysDirection ) );
axis.crossVectors( moveDirection, _eye ).normalize();
angle *= _this.rotateSpeed;
quaternion.setFromAxisAngle( axis, angle );
_eye.applyQuaternion( quaternion );
_this.object.up.applyQuaternion( quaternion );
_lastAxis.copy( axis );
_lastAngle = angle;
} else if ( ! _this.staticMoving && _lastAngle ) {
_lastAngle *= Math.sqrt( 1.0 - _this.dynamicDampingFactor );
_eye.copy( _this.object.position ).sub( _this.target );
quaternion.setFromAxisAngle( _lastAxis, _lastAngle );
_eye.applyQuaternion( quaternion );
_this.object.up.applyQuaternion( quaternion );
}
_movePrev.copy( _moveCurr );
};
}() );
this.zoomCamera = function () {
var factor;
if ( _state === STATE.TOUCH_ZOOM_PAN ) {
factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
_touchZoomDistanceStart = _touchZoomDistanceEnd;
_eye.multiplyScalar( factor );
} else {
factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * _this.zoomSpeed;
if ( factor !== 1.0 && factor > 0.0 ) {
_eye.multiplyScalar( factor );
}
if ( _this.staticMoving ) {
_zoomStart.copy( _zoomEnd );
} else {
_zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor;
}
}
};
this.panCamera = ( function () {
var mouseChange = new THREE.Vector2(),
objectUp = new THREE.Vector3(),
pan = new THREE.Vector3();
return function panCamera() {
mouseChange.copy( _panEnd ).sub( _panStart );
if ( mouseChange.lengthSq() ) {
mouseChange.multiplyScalar( _eye.length() * _this.panSpeed );
pan.copy( _eye ).cross( _this.object.up ).setLength( mouseChange.x );
pan.add( objectUp.copy( _this.object.up ).setLength( mouseChange.y ) );
_this.object.position.add( pan );
_this.target.add( pan );
if ( _this.staticMoving ) {
_panStart.copy( _panEnd );
} else {
_panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( _this.dynamicDampingFactor ) );
}
}
};
}() );
this.checkDistances = function () {
if ( ! _this.noZoom || ! _this.noPan ) {
if ( _eye.lengthSq() > _this.maxDistance * _this.maxDistance ) {
_this.object.position.addVectors( _this.target, _eye.setLength( _this.maxDistance ) );
_zoomStart.copy( _zoomEnd );
}
if ( _eye.lengthSq() < _this.minDistance * _this.minDistance ) {
_this.object.position.addVectors( _this.target, _eye.setLength( _this.minDistance ) );
_zoomStart.copy( _zoomEnd );
}
}
};
this.update = function () {
_eye.subVectors( _this.object.position, _this.target );
if ( ! _this.noRotate ) {
_this.rotateCamera();
}
if ( ! _this.noZoom ) {
_this.zoomCamera();
}
if ( ! _this.noPan ) {
_this.panCamera();
}
_this.object.position.addVectors( _this.target, _eye );
_this.checkDistances();
_this.object.lookAt( _this.target );
if ( lastPosition.distanceToSquared( _this.object.position ) > EPS ) {
_this.dispatchEvent( changeEvent );
lastPosition.copy( _this.object.position );
}
};
this.reset = function () {
_state = STATE.NONE;
_prevState = STATE.NONE;
_this.target.copy( _this.target0 );
_this.object.position.copy( _this.position0 );
_this.object.up.copy( _this.up0 );
_eye.subVectors( _this.object.position, _this.target );
_this.object.lookAt( _this.target );
_this.dispatchEvent( changeEvent );
lastPosition.copy( _this.object.position );
};
// listeners
function keydown( event ) {
if ( _this.enabled === false ) return;
window.removeEventListener( 'keydown', keydown );
_prevState = _state;
if ( _state !== STATE.NONE ) {
return;
} else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && ! _this.noRotate ) {
_state = STATE.ROTATE;
} else if ( event.keyCode === _this.keys[ STATE.ZOOM ] && ! _this.noZoom ) {
_state = STATE.ZOOM;
} else if ( event.keyCode === _this.keys[ STATE.PAN ] && ! _this.noPan ) {
_state = STATE.PAN;
}
}
function keyup( event ) {
if ( _this.enabled === false ) return;
_state = _prevState;
window.addEventListener( 'keydown', keydown, false );
}
function mousedown( event ) {
if ( _this.enabled === false ) return;
event.preventDefault();
event.stopPropagation();
if ( _state === STATE.NONE ) {
_state = event.button;
}
if ( _state === STATE.ROTATE && ! _this.noRotate ) {
_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
_movePrev.copy( _moveCurr );
} else if ( _state === STATE.ZOOM && ! _this.noZoom ) {
_zoomStart.copy( getMouseOnScreen( event.pageX, event.pageY ) );
_zoomEnd.copy( _zoomStart );
} else if ( _state === STATE.PAN && ! _this.noPan ) {
_panStart.copy( getMouseOnScreen( event.pageX, event.pageY ) );
_panEnd.copy( _panStart );
}
document.addEventListener( 'mousemove', mousemove, false );
document.addEventListener( 'mouseup', mouseup, false );
_this.dispatchEvent( startEvent );
}
function mousemove( event ) {
if ( _this.enabled === false ) return;
event.preventDefault();
event.stopPropagation();
if ( _state === STATE.ROTATE && ! _this.noRotate ) {
_movePrev.copy( _moveCurr );
_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
} else if ( _state === STATE.ZOOM && ! _this.noZoom ) {
_zoomEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) );
} else if ( _state === STATE.PAN && ! _this.noPan ) {
_panEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) );
}
}
function mouseup( event ) {
if ( _this.enabled === false ) return;
event.preventDefault();
event.stopPropagation();
_state = STATE.NONE;
document.removeEventListener( 'mousemove', mousemove );
document.removeEventListener( 'mouseup', mouseup );
_this.dispatchEvent( endEvent );
}
function mousewheel( event ) {
if ( _this.enabled === false ) return;
if ( _this.noZoom === true ) return;
event.preventDefault();
event.stopPropagation();
switch ( event.deltaMode ) {
case 2:
// Zoom in pages
_zoomStart.y -= event.deltaY * 0.025;
break;
case 1:
// Zoom in lines
_zoomStart.y -= event.deltaY * 0.01;
break;
default:
// undefined, 0, assume pixels
_zoomStart.y -= event.deltaY * 0.00025;
break;
}
_this.dispatchEvent( startEvent );
_this.dispatchEvent( endEvent );
}
function touchstart( event ) {
if ( _this.enabled === false ) return;
event.preventDefault();
switch ( event.touches.length ) {
case 1:
_state = STATE.TOUCH_ROTATE;
_moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
_movePrev.copy( _moveCurr );
break;
default: // 2 or more
_state = STATE.TOUCH_ZOOM_PAN;
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
_touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
_panStart.copy( getMouseOnScreen( x, y ) );
_panEnd.copy( _panStart );
break;
}
_this.dispatchEvent( startEvent );
}
function touchmove( event ) {
if ( _this.enabled === false ) return;
event.preventDefault();
event.stopPropagation();
switch ( event.touches.length ) {
case 1:
_movePrev.copy( _moveCurr );
_moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
break;
default: // 2 or more
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
_touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy );