working line endpt drag
|
@ -0,0 +1 @@
|
|||
node_modules/
|
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 9.4 KiB |
After Width: | Height: | Size: 26 KiB |
|
@ -0,0 +1,70 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="favicon.ico" />
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
|
||||
<meta property="og:title" content="CAD Tool" />
|
||||
<meta property="og:description" content="Three.js CAD tool" />
|
||||
<meta property="og:url" content="" />
|
||||
<meta property="og:image" content="" />
|
||||
|
||||
<link rel="apple-touch-icon" href="icon-192.png" />
|
||||
<link rel="manifest" href="manifest.json" />
|
||||
<title>CAD Tool</title>
|
||||
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#c {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.split {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#view1 {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#view2 {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 30%;
|
||||
height: 30%;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<canvas id="c"></canvas>
|
||||
<div class="split">
|
||||
<div id="view1"></div>
|
||||
<div id="view2"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<script src="bundle.js" type="module"></script>
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"short_name": "CAD Tool",
|
||||
"name": "CAD Tool using Three.js",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "icon-192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "icon-512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512",
|
||||
"purpose": "maskable any"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
|
@ -0,0 +1,19 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
svg=icon
|
||||
|
||||
size=(16 24 32 64)
|
||||
out=""
|
||||
for i in ${size[@]}; do
|
||||
inkscape --export-filename="./$svg-$i.png" $svg.svg -w $i -h $i
|
||||
out+="$svg-$i.png "
|
||||
done
|
||||
|
||||
size=(192 512)
|
||||
for i in ${size[@]}; do
|
||||
inkscape --export-filename="./$svg-$i.png" $svg.svg -w $i -h $i
|
||||
done
|
||||
|
||||
convert $out favicon.ico
|
||||
|
||||
mv favicon.ico icon-192.png icon-512.png ../dist
|
After Width: | Height: | Size: 761 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 3.1 KiB |
|
@ -0,0 +1,103 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
enable-background="new 0 0 24 24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="black"
|
||||
width="24px"
|
||||
height="24px"
|
||||
version="1.1"
|
||||
id="svg26"
|
||||
sodipodi:docname="icon.svg"
|
||||
inkscape:version="1.0.2 (1.0.2+r75+1)">
|
||||
<metadata
|
||||
id="metadata32">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs30" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1623"
|
||||
inkscape:window-height="1236"
|
||||
id="namedview28"
|
||||
showgrid="false"
|
||||
inkscape:zoom="21.184428"
|
||||
inkscape:cx="16.030816"
|
||||
inkscape:cy="15.296979"
|
||||
inkscape:window-x="426"
|
||||
inkscape:window-y="92"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg26"
|
||||
inkscape:document-rotation="0"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true">
|
||||
<sodipodi:guide
|
||||
position="-3.6819497,21.6"
|
||||
orientation="0,1"
|
||||
id="guide842"
|
||||
inkscape:label=""
|
||||
inkscape:locked="false"
|
||||
inkscape:color="rgb(0,0,255)" />
|
||||
<sodipodi:guide
|
||||
position="-1.0857031,2.4"
|
||||
orientation="0,1"
|
||||
id="guide846"
|
||||
inkscape:label=""
|
||||
inkscape:locked="false"
|
||||
inkscape:color="rgb(0,0,255)" />
|
||||
</sodipodi:namedview>
|
||||
<circle
|
||||
style="fill:#a6a6a6;stroke-width:0.05;stroke-linecap:round"
|
||||
id="path927"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="12" />
|
||||
<g
|
||||
id="g875"
|
||||
transform="matrix(1.2,0,0,1.2,-2.4,-2.4)">
|
||||
<g
|
||||
id="g868"
|
||||
style="fill:#5ccff0;fill-opacity:1">
|
||||
<polygon
|
||||
opacity=".3"
|
||||
points="13,17.17 17,14.87 17,10.24 13,12.57 "
|
||||
id="polygon6"
|
||||
style="opacity:1;fill:#5ccff0;fill-opacity:1" />
|
||||
<polygon
|
||||
opacity=".3"
|
||||
points="12,6.25 8.04,8.53 12,10.84 15.96,8.53 "
|
||||
id="polygon8"
|
||||
style="opacity:1;fill:#5ccff0;fill-opacity:1" />
|
||||
<polygon
|
||||
opacity=".3"
|
||||
points="7,14.87 11,17.17 11,12.57 7,10.24 "
|
||||
id="polygon10"
|
||||
style="opacity:1;fill:#5ccff0;fill-opacity:1" />
|
||||
</g>
|
||||
<path
|
||||
d="M 19,14.87 V 9.13 C 19,8.41 18.62,7.75 18,7.4 L 13,4.52 C 12.69,4.34 12.35,4.25 12,4.25 c -0.35,0 -0.69,0.09 -1,0.27 L 6,7.39 C 5.38,7.75 5,8.41 5,9.13 v 5.74 c 0,0.72 0.38,1.38 1,1.73 l 5,2.88 c 0.31,0.18 0.65,0.27 1,0.27 0.35,0 0.69,-0.09 1,-0.27 l 5,-2.88 c 0.62,-0.35 1,-1.01 1,-1.73 z m -8,2.3 -4,-2.3 v -4.63 l 4,2.33 z M 12,10.84 8.04,8.53 12,6.25 15.96,8.53 Z m 5,4.03 -4,2.3 v -4.6 l 4,-2.33 z"
|
||||
id="path20" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.2 KiB |
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"scripts": {
|
||||
"start": "webpack --watch --config webpack.dev.js",
|
||||
"build": "webpack --config webpack.prod.js",
|
||||
"get-bundle-size": "webpack --profile --json > stats.json --config webpack.prod.js && webpack-bundle-analyzer stats.json dist/"
|
||||
},
|
||||
"devDependencies": {
|
||||
"css-loader": "^5.1.3",
|
||||
"dat.gui": "^0.7.7",
|
||||
"redux": "^4.0.5",
|
||||
"sass": "^1.32.8",
|
||||
"sass-loader": "^11.0.1",
|
||||
"stats-js": "^1.0.1",
|
||||
"style-loader": "^2.0.0",
|
||||
"three": "^0.126.1",
|
||||
"webpack": "^5.26.3",
|
||||
"webpack-bundle-analyzer": "^4.4.0",
|
||||
"webpack-cli": "^4.5.0",
|
||||
"webpack-merge": "^5.7.3"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,266 @@
|
|||
|
||||
import { Matrix4 } from 'three';
|
||||
import * as THREE from '../node_modules/three/src/Three'
|
||||
|
||||
export class Sketcher extends THREE.Group {
|
||||
constructor(camera, domElement, plane) {
|
||||
super()
|
||||
this.camera = camera;
|
||||
this.domElement = domElement;
|
||||
this.scene = scene;
|
||||
this.plane = plane;
|
||||
this.matrixAutoUpdate = false;
|
||||
this.sketchNormal = new THREE.Vector3(0, 0, 1)
|
||||
this.orientSketcher(plane)
|
||||
|
||||
|
||||
this.add(new THREE.PlaneHelper(this.plane, 1, 0xffff00));
|
||||
|
||||
|
||||
this.linesGroup = new THREE.Group()
|
||||
this.linesArr = this.linesGroup.children
|
||||
this.pointsGroup = new THREE.Group()
|
||||
this.ptsArr = this.pointsGroup.children
|
||||
this.add(this.linesGroup)
|
||||
this.add(this.pointsGroup)
|
||||
|
||||
window.lg = this.linesArr
|
||||
window.pg = this.ptsArr
|
||||
|
||||
this.pickThreshold = 100
|
||||
this.grabbedObject = null
|
||||
|
||||
this.lineMaterial = new THREE.LineBasicMaterial({
|
||||
color: 0x555,
|
||||
})
|
||||
this.pointMaterial = new THREE.PointsMaterial({
|
||||
color: 0xAAA,
|
||||
size: 3,
|
||||
})
|
||||
|
||||
this.pointStart = this.pointStart.bind(this);
|
||||
this.pointEnd = this.pointEnd.bind(this);
|
||||
this.move = this.move.bind(this);
|
||||
this.keyHandler = this.keyHandler.bind(this);
|
||||
this.picker = this.picker.bind(this);
|
||||
this.grabbedMove = this.grabbedMove.bind(this);
|
||||
this.grabEnd = this.grabEnd.bind(this);
|
||||
this.raycaster = new THREE.Raycaster();
|
||||
|
||||
|
||||
window.addEventListener('keydown', this.keyHandler)
|
||||
domElement.addEventListener('pointerdown', this.picker)
|
||||
|
||||
|
||||
this.mode = ""
|
||||
this.keyTable = {
|
||||
'l': this.addLine,
|
||||
'Escape': this.clear
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
orientSketcher() {
|
||||
|
||||
const theta = this.sketchNormal.angleTo(this.plane.normal)
|
||||
const axis = this.sketchNormal.clone().cross(this.plane.normal).normalize()
|
||||
const rot = new THREE.Matrix4().makeRotationAxis(axis, theta)
|
||||
const trans = new THREE.Matrix4().makeTranslation(0, 0, this.plane.constant)
|
||||
|
||||
this.matrix = rot.multiply(trans) // world matrix will auto update in next render
|
||||
this.inverse = this.matrix.clone().invert()
|
||||
|
||||
}
|
||||
|
||||
keyHandler(e) {
|
||||
switch (e.key) {
|
||||
case 'Escape':
|
||||
this.clear()
|
||||
this.mode = ""
|
||||
break;
|
||||
case 'l':
|
||||
this.addLine()
|
||||
this.mode = "line"
|
||||
break;
|
||||
case '=':
|
||||
this.plane.applyMatrix4(new Matrix4().makeRotationY(0.1))
|
||||
this.orientSketcher()
|
||||
|
||||
this.dispatchEvent({ type: 'change' })
|
||||
break;
|
||||
case '-':
|
||||
this.plane.applyMatrix4(new Matrix4().makeRotationY(-0.1))
|
||||
this.orientSketcher()
|
||||
|
||||
this.dispatchEvent({ type: 'change' })
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
picker(e) {
|
||||
if (this.mode || e.buttons != 1) return
|
||||
this.raycaster.setFromCamera(
|
||||
new THREE.Vector2(
|
||||
(e.clientX / window.innerWidth) * 2 - 1,
|
||||
- (e.clientY / window.innerHeight) * 2 + 1
|
||||
),
|
||||
this.camera
|
||||
);
|
||||
|
||||
// console.log(this.ptsArr)
|
||||
const candidates = this.raycaster.intersectObjects(this.ptsArr)
|
||||
// console.log(candidates)
|
||||
|
||||
|
||||
if (!candidates.length) return;
|
||||
|
||||
let minDist = candidates[0].distanceToRay
|
||||
let idx = 0
|
||||
|
||||
for (let i = 1; i < candidates.length; i++) {
|
||||
if (candidates.distanceToRay < minDist) {
|
||||
minDist = candidates.distanceToRay
|
||||
idx = i
|
||||
}
|
||||
}
|
||||
|
||||
if (minDist < this.pickThreshold) {
|
||||
this.grabPtIdx = this.ptsArr.indexOf(
|
||||
candidates[idx].object
|
||||
)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
this.domElement.addEventListener('pointermove', this.grabbedMove);
|
||||
this.domElement.addEventListener('pointerup', this.grabEnd);
|
||||
}
|
||||
|
||||
grabbedMove(e) {
|
||||
const mouseLoc = this.getLocation(e);
|
||||
|
||||
this.moveLinePt(this.grabPtIdx,mouseLoc)
|
||||
|
||||
this.dispatchEvent({ type: 'change' })
|
||||
}
|
||||
|
||||
moveLinePt(ptIdx, absPos) {
|
||||
this.ptsArr[ptIdx].geometry.attributes.position.set(absPos);
|
||||
this.ptsArr[ptIdx].geometry.attributes.position.needsUpdate = true;
|
||||
|
||||
const lineIdx = Math.floor(ptIdx / 2)
|
||||
const endPtIdx = (ptIdx % 2) * 3
|
||||
this.linesArr[lineIdx].geometry.attributes.position.set(absPos, endPtIdx)
|
||||
this.linesArr[lineIdx].geometry.attributes.position.needsUpdate = true;
|
||||
}
|
||||
|
||||
grabEnd() {
|
||||
this.domElement.removeEventListener('pointermove', this.grabbedMove)
|
||||
this.domElement.removeEventListener('pointerup', this.grabEnd)
|
||||
this.ptsArr[this.grabPtIdx].geometry.computeBoundingSphere()
|
||||
// this.grabbedObject = null
|
||||
}
|
||||
|
||||
|
||||
addLine() {
|
||||
this.domElement.addEventListener('pointerdown', this.pointStart)
|
||||
}
|
||||
|
||||
clear() {
|
||||
if (this.mode == "") return
|
||||
|
||||
this.domElement.removeEventListener('pointerdown', this.pointStart)
|
||||
this.domElement.removeEventListener('pointermove', this.move);
|
||||
this.domElement.removeEventListener('pointerdown', this.pointEnd);
|
||||
this.domElement.removeEventListener('pointerdown', this.pointEnd);
|
||||
|
||||
const lastLine = this.linesArr[this.linesArr.length - 1]
|
||||
this.linesGroup.remove(lastLine)
|
||||
lastLine.geometry.dispose()
|
||||
|
||||
const lastPoints = this.ptsArr.slice(this.ptsArr.length - 2)
|
||||
this.pointsGroup.remove(...lastPoints)
|
||||
lastPoints.forEach(obj => obj.geometry.dispose())
|
||||
|
||||
this.dispatchEvent({ type: 'change' })
|
||||
}
|
||||
|
||||
getLocation(e) {
|
||||
this.raycaster.setFromCamera(
|
||||
new THREE.Vector2(
|
||||
(e.clientX / window.innerWidth) * 2 - 1,
|
||||
- (e.clientY / window.innerHeight) * 2 + 1
|
||||
),
|
||||
this.camera
|
||||
);
|
||||
// return this.worldToLocal(this.raycaster.ray.intersectPlane(this.plane)).toArray()
|
||||
return this.raycaster.ray.intersectPlane(this.plane).applyMatrix4(this.inverse).toArray()
|
||||
}
|
||||
|
||||
pointStart(e) {
|
||||
if (e.buttons !== 1) return
|
||||
const mouseLoc = this.getLocation(e);
|
||||
|
||||
this.lineGeom = new THREE.BufferGeometry()
|
||||
this.lineGeom.setAttribute('position',
|
||||
new THREE.BufferAttribute(
|
||||
new Float32Array(6), 3
|
||||
)
|
||||
);
|
||||
this.lineGeom.attributes.position.set(mouseLoc)
|
||||
this.line = new THREE.LineSegments(this.lineGeom, this.lineMaterial);
|
||||
this.line.frustumCulled = false;
|
||||
this.linesGroup.add(this.line)
|
||||
|
||||
this.p1Geom = new THREE.BufferGeometry()
|
||||
this.p1Geom.setAttribute('position',
|
||||
new THREE.BufferAttribute(
|
||||
new Float32Array(3), 3
|
||||
)
|
||||
);
|
||||
this.p1Geom.attributes.position.set(mouseLoc)
|
||||
this.p1 = new THREE.Points(this.p1Geom, this.pointMaterial);
|
||||
this.pointsGroup.add(this.p1)
|
||||
|
||||
this.p2Geom = new THREE.BufferGeometry()
|
||||
this.p2Geom.setAttribute('position',
|
||||
new THREE.BufferAttribute(
|
||||
new Float32Array(3), 3
|
||||
)
|
||||
);
|
||||
this.p2 = new THREE.Points(this.p2Geom, this.pointMaterial);
|
||||
this.pointsGroup.add(this.p2)
|
||||
|
||||
this.domElement.removeEventListener('pointerdown', this.pointStart)
|
||||
this.domElement.addEventListener('pointermove', this.move)
|
||||
this.domElement.addEventListener('pointerdown', this.pointEnd)
|
||||
}
|
||||
|
||||
|
||||
move(e) {
|
||||
const mouseLoc = this.getLocation(e);
|
||||
this.lineGeom.attributes.position.set(mouseLoc, 3)
|
||||
this.lineGeom.attributes.position.needsUpdate = true;
|
||||
this.p2Geom.attributes.position.set(mouseLoc);
|
||||
this.p2Geom.attributes.position.needsUpdate = true;
|
||||
this.p2Geom.computeBoundingSphere();
|
||||
this.dispatchEvent({ type: 'change' })
|
||||
}
|
||||
|
||||
pointEnd(e) {
|
||||
if (e.buttons !== 1) return;
|
||||
this.domElement.removeEventListener('pointermove', this.move);
|
||||
this.domElement.removeEventListener('pointerdown', this.pointEnd);
|
||||
|
||||
|
||||
this.pointStart(e)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,181 @@
|
|||
// class MinMaxGUIHelper {
|
||||
// constructor(obj, minProp, maxProp, minDif) {
|
||||
// this.obj = obj;
|
||||
// this.minProp = minProp;
|
||||
// this.maxProp = maxProp;
|
||||
// this.minDif = minDif;
|
||||
// }
|
||||
// get min() {
|
||||
// return this.obj[this.minProp];
|
||||
// }
|
||||
// set min(v) {
|
||||
// this.obj[this.minProp] = v;
|
||||
// this.obj[this.maxProp] = Math.max(this.obj[this.maxProp], v + this.minDif);
|
||||
// }
|
||||
// get max() {
|
||||
// return this.obj[this.maxProp];
|
||||
// }
|
||||
// set max(v) {
|
||||
// this.obj[this.maxProp] = v;
|
||||
// this.min = this.min; // this will call the min setter
|
||||
// }
|
||||
// }
|
||||
|
||||
// const gui = new GUI();
|
||||
// gui.add(camera, 'zoom', 0.01, 1, 0.01).listen();
|
||||
// const minMaxGUIHelper = new MinMaxGUIHelper(camera, 'near', 'far', 0.1);
|
||||
// gui.add(minMaxGUIHelper, 'min', 0.1, 50, 0.1).name('near');
|
||||
// gui.add(minMaxGUIHelper, 'max', 0.1, 50, 0.1).name('far');
|
||||
|
||||
import * as THREE from '../node_modules/three/src/Three';
|
||||
import { OrbitControls } from './OrbitControls'
|
||||
import { TrackballControls } from './trackball'
|
||||
import { Sketcher } from './Sketcher'
|
||||
import GUI from '../node_modules/dat.gui/src/dat/gui/GUI.js'
|
||||
import Stats from '../node_modules/three/examples/jsm/libs/stats.module.js';
|
||||
|
||||
import { KeyboardController } from './utils'
|
||||
|
||||
|
||||
|
||||
function main() {
|
||||
|
||||
const Controller = new KeyboardController()
|
||||
|
||||
var stats = new Stats();
|
||||
stats.showPanel(0); // 0: fps, 1: ms, 2: mb, 3+: custom
|
||||
document.body.appendChild(stats.dom);
|
||||
|
||||
const canvas = document.querySelector('#c');
|
||||
const view1Elem = document.querySelector('#view1');
|
||||
const view2Elem = document.querySelector('#view2');
|
||||
const renderer = new THREE.WebGLRenderer({ canvas });
|
||||
|
||||
const scene = new THREE.Scene();
|
||||
window.scene = scene;
|
||||
scene.background = new THREE.Color('pink');
|
||||
|
||||
const helpersGroup = new THREE.Group();
|
||||
scene.add(helpersGroup);
|
||||
|
||||
|
||||
const size = 1;
|
||||
const near = 5;
|
||||
const far = 50;
|
||||
const camera = new THREE.OrthographicCamera(-size, size, size, -size, near, far);
|
||||
camera.zoom = 0.1;
|
||||
camera.position.set(0, 0, 20);
|
||||
const controls = new OrbitControls(camera, view1Elem);
|
||||
// const controls = new TrackballControls(camera, view1Elem);
|
||||
controls.target.set(0, 0, 0);
|
||||
controls.update()
|
||||
const cameraHelper = new THREE.CameraHelper(camera);
|
||||
helpersGroup.add(cameraHelper);
|
||||
|
||||
|
||||
const camera2 = new THREE.PerspectiveCamera(
|
||||
60, // fov
|
||||
2, // aspect
|
||||
0.1, // near
|
||||
500, // far
|
||||
);
|
||||
camera2.position.set(16, 28, 40);
|
||||
camera2.lookAt(0, 5, 0);
|
||||
const controls2 = new OrbitControls(camera2, view2Elem);
|
||||
controls2.target.set(0, 5, 0);
|
||||
controls2.update();
|
||||
|
||||
|
||||
|
||||
const axesHelper = new THREE.AxesHelper(5);
|
||||
helpersGroup.add(axesHelper);
|
||||
|
||||
const sketchPlane = new THREE.Plane(new THREE.Vector3(0, 0, 1), 0);
|
||||
const sketcher = new Sketcher(camera, view1Elem, sketchPlane)
|
||||
scene.add(sketcher)
|
||||
|
||||
{
|
||||
const color = 0xFFFFFF;
|
||||
const intensity = 1;
|
||||
const light = new THREE.DirectionalLight(color, intensity);
|
||||
light.position.set(0, 10, 0);
|
||||
light.target.position.set(-5, 0, 0);
|
||||
scene.add(light);
|
||||
scene.add(light.target);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function resizeRendererToDisplaySize(renderer) {
|
||||
const canvas = renderer.domElement;
|
||||
const width = canvas.clientWidth;
|
||||
const height = canvas.clientHeight;
|
||||
const needResize = canvas.width !== width || canvas.height !== height;
|
||||
if (needResize) {
|
||||
renderer.setSize(width, height, false);
|
||||
}
|
||||
return needResize;
|
||||
}
|
||||
|
||||
function setScissorForElement(elem) {
|
||||
const canvasRect = canvas.getBoundingClientRect();
|
||||
const elemRect = elem.getBoundingClientRect();
|
||||
|
||||
// compute a canvas relative rectangle
|
||||
const right = Math.min(elemRect.right, canvasRect.right) - canvasRect.left;
|
||||
const left = Math.max(0, elemRect.left - canvasRect.left);
|
||||
const bottom = Math.min(elemRect.bottom, canvasRect.bottom) - canvasRect.top;
|
||||
const top = Math.max(0, elemRect.top - canvasRect.top);
|
||||
|
||||
const width = Math.min(canvasRect.width, right - left);
|
||||
const height = Math.min(canvasRect.height, bottom - top);
|
||||
|
||||
// setup the scissor to only render to that part of the canvas
|
||||
const positiveYUpBottom = canvasRect.height - bottom;
|
||||
renderer.setScissor(left, positiveYUpBottom, width, height);
|
||||
renderer.setViewport(left, positiveYUpBottom, width, height);
|
||||
|
||||
// return the aspect
|
||||
return width / height;
|
||||
}
|
||||
|
||||
function render() {
|
||||
stats.begin();
|
||||
resizeRendererToDisplaySize(renderer);
|
||||
|
||||
renderer.setScissorTest(true);
|
||||
{
|
||||
const aspect = setScissorForElement(view1Elem);
|
||||
camera.left = -aspect;
|
||||
camera.right = aspect;
|
||||
camera.updateProjectionMatrix();
|
||||
cameraHelper.update();
|
||||
cameraHelper.visible = false;
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
{
|
||||
const aspect = setScissorForElement(view2Elem);
|
||||
camera2.aspect = aspect;
|
||||
camera2.updateProjectionMatrix();
|
||||
cameraHelper.visible = true;
|
||||
renderer.render(scene, camera2);
|
||||
}
|
||||
|
||||
stats.end();
|
||||
|
||||
// requestAnimationFrame(render);
|
||||
}
|
||||
// requestAnimationFrame(render);
|
||||
|
||||
|
||||
controls.addEventListener('change', render);
|
||||
controls.addEventListener('start', render);
|
||||
controls2.addEventListener('change', render);
|
||||
sketcher.addEventListener('change', render);
|
||||
window.addEventListener('resize', render);
|
||||
render();
|
||||
}
|
||||
|
||||
main();
|
|
@ -0,0 +1,754 @@
|
|||
import {
|
||||
EventDispatcher,
|
||||
MOUSE,
|
||||
Quaternion,
|
||||
Vector2,
|
||||
Vector3
|
||||
} from '../node_modules/three/src/Three';
|
||||
|
||||
var TrackballControls = function ( object, domElement ) {
|
||||
|
||||
if ( domElement === undefined ) console.warn( 'THREE.TrackballControls: The second parameter "domElement" is now mandatory.' );
|
||||
if ( domElement === document ) console.error( 'THREE.TrackballControls: "document" should not be used as the target "domElement". Please use "renderer.domElement" instead.' );
|
||||
|
||||
var scope = this;
|
||||
var STATE = { NONE: - 1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM_PAN: 4 };
|
||||
|
||||
this.object = object;
|
||||
this.domElement = domElement;
|
||||
|
||||
// API
|
||||
|
||||
this.enabled = true;
|
||||
|
||||
this.screen = { left: 0, top: 0, width: 0, height: 0 };
|
||||
|
||||
this.rotateSpeed = 3.0;
|
||||
this.zoomSpeed = 1.2;
|
||||
this.panSpeed = 89.5;
|
||||
|
||||
this.noRotate = false;
|
||||
this.noZoom = false;
|
||||
this.noPan = false;
|
||||
|
||||
this.staticMoving = true;
|
||||
this.dynamicDampingFactor = 0.2;
|
||||
|
||||
this.minDistance = 0;
|
||||
this.maxDistance = Infinity;
|
||||
|
||||
this.keys = [ 65 /*A*/, 83 /*S*/, 68 /*D*/ ];
|
||||
|
||||
this.mouseButtons = { LEFT: null, MIDDLE: MOUSE.ROTATE, RIGHT: MOUSE.PAN };
|
||||
|
||||
// internals
|
||||
|
||||
this.target = new Vector3();
|
||||
|
||||
var EPS = 0.000001;
|
||||
|
||||
var lastPosition = new Vector3();
|
||||
var lastZoom = 1;
|
||||
|
||||
var _state = STATE.NONE,
|
||||
_keyState = STATE.NONE,
|
||||
|
||||
_eye = new Vector3(),
|
||||
|
||||
_movePrev = new Vector2(),
|
||||
_moveCurr = new Vector2(),
|
||||
|
||||
_lastAxis = new Vector3(),
|
||||
_lastAngle = 0,
|
||||
|
||||
_zoomStart = new Vector2(),
|
||||
_zoomEnd = new Vector2(),
|
||||
|
||||
_touchZoomDistanceStart = 0,
|
||||
_touchZoomDistanceEnd = 0,
|
||||
|
||||
_panStart = new Vector2(),
|
||||
_panEnd = new Vector2();
|
||||
|
||||
// for reset
|
||||
|
||||
this.target0 = this.target.clone();
|
||||
this.position0 = this.object.position.clone();
|
||||
this.up0 = this.object.up.clone();
|
||||
this.zoom0 = this.object.zoom;
|
||||
|
||||
// events
|
||||
|
||||
var changeEvent = { type: 'change' };
|
||||
var startEvent = { type: 'start' };
|
||||
var endEvent = { type: 'end' };
|
||||
|
||||
|
||||
// methods
|
||||
|
||||
this.handleResize = function () {
|
||||
|
||||
var box = scope.domElement.getBoundingClientRect();
|
||||
// adjustments come from similar code in the jquery offset() function
|
||||
var d = scope.domElement.ownerDocument.documentElement;
|
||||
scope.screen.left = box.left + window.pageXOffset - d.clientLeft;
|
||||
scope.screen.top = box.top + window.pageYOffset - d.clientTop;
|
||||
scope.screen.width = box.width;
|
||||
scope.screen.height = box.height;
|
||||
|
||||
};
|
||||
|
||||
var getMouseOnScreen = ( function () {
|
||||
|
||||
var vector = new Vector2();
|
||||
|
||||
return function getMouseOnScreen( pageX, pageY ) {
|
||||
|
||||
vector.set(
|
||||
( pageX - scope.screen.left ) / scope.screen.width,
|
||||
( pageY - scope.screen.top ) / scope.screen.height
|
||||
);
|
||||
|
||||
return vector;
|
||||
|
||||
};
|
||||
|
||||
}() );
|
||||
|
||||
var getMouseOnCircle = ( function () {
|
||||
|
||||
var vector = new Vector2();
|
||||
|
||||
return function getMouseOnCircle( pageX, pageY ) {
|
||||
|
||||
vector.set(
|
||||
( ( pageX - scope.screen.width * 0.5 - scope.screen.left ) / ( scope.screen.width * 0.5 ) ),
|
||||
( ( scope.screen.height + 2 * ( scope.screen.top - pageY ) ) / scope.screen.width ) // screen.width intentional
|
||||
);
|
||||
|
||||
return vector;
|
||||
|
||||
};
|
||||
|
||||
}() );
|
||||
|
||||
this.rotateCamera = ( function () {
|
||||
|
||||
var axis = new Vector3(),
|
||||
quaternion = new Quaternion(),
|
||||
eyeDirection = new Vector3(),
|
||||
objectUpDirection = new Vector3(),
|
||||
objectSidewaysDirection = new Vector3(),
|
||||
moveDirection = new Vector3(),
|
||||
angle;
|
||||
|
||||
return function rotateCamera() {
|
||||
|
||||
moveDirection.set( _moveCurr.x - _movePrev.x, _moveCurr.y - _movePrev.y, 0 );
|
||||
angle = moveDirection.length();
|
||||
|
||||
if ( angle ) {
|
||||
|
||||
_eye.copy( scope.object.position ).sub( scope.target );
|
||||
|
||||
eyeDirection.copy( _eye ).normalize();
|
||||
objectUpDirection.copy( scope.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 *= scope.rotateSpeed;
|
||||
quaternion.setFromAxisAngle( axis, angle );
|
||||
|
||||
_eye.applyQuaternion( quaternion );
|
||||
scope.object.up.applyQuaternion( quaternion );
|
||||
|
||||
_lastAxis.copy( axis );
|
||||
_lastAngle = angle;
|
||||
|
||||
} else if ( ! scope.staticMoving && _lastAngle ) {
|
||||
|
||||
_lastAngle *= Math.sqrt( 1.0 - scope.dynamicDampingFactor );
|
||||
_eye.copy( scope.object.position ).sub( scope.target );
|
||||
quaternion.setFromAxisAngle( _lastAxis, _lastAngle );
|
||||
_eye.applyQuaternion( quaternion );
|
||||
scope.object.up.applyQuaternion( quaternion );
|
||||
|
||||
}
|
||||
|
||||
_movePrev.copy( _moveCurr );
|
||||
|
||||
};
|
||||
|
||||
}() );
|
||||
|
||||
|
||||
this.zoomCamera = function () {
|
||||
|
||||
var factor;
|
||||
|
||||
if ( _state === STATE.TOUCH_ZOOM_PAN ) {
|
||||
|
||||
factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
|
||||
_touchZoomDistanceStart = _touchZoomDistanceEnd;
|
||||
|
||||
if ( scope.object.isPerspectiveCamera ) {
|
||||
|
||||
_eye.multiplyScalar( factor );
|
||||
|
||||
} else if ( scope.object.isOrthographicCamera ) {
|
||||
|
||||
scope.object.zoom *= factor;
|
||||
scope.object.updateProjectionMatrix();
|
||||
|
||||
} else {
|
||||
|
||||
console.warn( 'THREE.TrackballControls: Unsupported camera type' );
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * scope.zoomSpeed;
|
||||
|
||||
if ( factor !== 1.0 && factor > 0.0 ) {
|
||||
|
||||
if ( scope.object.isPerspectiveCamera ) {
|
||||
|
||||
_eye.multiplyScalar( factor );
|
||||
|
||||
} else if ( scope.object.isOrthographicCamera ) {
|
||||
|
||||
scope.object.zoom /= factor;
|
||||
scope.object.updateProjectionMatrix();
|
||||
|
||||
} else {
|
||||
|
||||
console.warn( 'THREE.TrackballControls: Unsupported camera type' );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( scope.staticMoving ) {
|
||||
|
||||
_zoomStart.copy( _zoomEnd );
|
||||
|
||||
} else {
|
||||
|
||||
_zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.panCamera = ( function () {
|
||||
|
||||
var mouseChange = new Vector2(),
|
||||
objectUp = new Vector3(),
|
||||
pan = new Vector3();
|
||||
|
||||
return function panCamera() {
|
||||
|
||||
mouseChange.copy( _panEnd ).sub( _panStart );
|
||||
|
||||
if ( mouseChange.lengthSq() ) {
|
||||
|
||||
if ( scope.object.isOrthographicCamera ) {
|
||||
|
||||
var scale_x = ( scope.object.right - scope.object.left ) / scope.object.zoom / scope.domElement.clientWidth;
|
||||
var scale_y = ( scope.object.top - scope.object.bottom ) / scope.object.zoom / scope.domElement.clientWidth;
|
||||
|
||||
mouseChange.x *= scale_x;
|
||||
mouseChange.y *= scale_y;
|
||||
|
||||
}
|
||||
|
||||
mouseChange.multiplyScalar( _eye.length() * scope.panSpeed );
|
||||
|
||||
pan.copy( _eye ).cross( scope.object.up ).setLength( mouseChange.x );
|
||||
pan.add( objectUp.copy( scope.object.up ).setLength( mouseChange.y ) );
|
||||
|
||||
scope.object.position.add( pan );
|
||||
scope.target.add( pan );
|
||||
|
||||
if ( scope.staticMoving ) {
|
||||
|
||||
_panStart.copy( _panEnd );
|
||||
|
||||
} else {
|
||||
|
||||
_panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( scope.dynamicDampingFactor ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}() );
|
||||
|
||||
this.checkDistances = function () {
|
||||
|
||||
if ( ! scope.noZoom || ! scope.noPan ) {
|
||||
|
||||
if ( _eye.lengthSq() > scope.maxDistance * scope.maxDistance ) {
|
||||
|
||||
scope.object.position.addVectors( scope.target, _eye.setLength( scope.maxDistance ) );
|
||||
_zoomStart.copy( _zoomEnd );
|
||||
|
||||
}
|
||||
|
||||
if ( _eye.lengthSq() < scope.minDistance * scope.minDistance ) {
|
||||
|
||||
scope.object.position.addVectors( scope.target, _eye.setLength( scope.minDistance ) );
|
||||
_zoomStart.copy( _zoomEnd );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.update = function () {
|
||||
|
||||
_eye.subVectors( scope.object.position, scope.target );
|
||||
|
||||
if ( ! scope.noRotate ) {
|
||||
|
||||
scope.rotateCamera();
|
||||
|
||||
}
|
||||
|
||||
if ( ! scope.noZoom ) {
|
||||
|
||||
scope.zoomCamera();
|
||||
|
||||
}
|
||||
|
||||
if ( ! scope.noPan ) {
|
||||
|
||||
scope.panCamera();
|
||||
|
||||
}
|
||||
|
||||
scope.object.position.addVectors( scope.target, _eye );
|
||||
|
||||
if ( scope.object.isPerspectiveCamera ) {
|
||||
|
||||
scope.checkDistances();
|
||||
|
||||
scope.object.lookAt( scope.target );
|
||||
|
||||
if ( lastPosition.distanceToSquared( scope.object.position ) > EPS ) {
|
||||
|
||||
scope.dispatchEvent( changeEvent );
|
||||
|
||||
lastPosition.copy( scope.object.position );
|
||||
|
||||
}
|
||||
|
||||
} else if ( scope.object.isOrthographicCamera ) {
|
||||
|
||||
scope.object.lookAt( scope.target );
|
||||
|
||||
if ( lastPosition.distanceToSquared( scope.object.position ) > EPS || lastZoom !== scope.object.zoom ) {
|
||||
|
||||
scope.dispatchEvent( changeEvent );
|
||||
|
||||
lastPosition.copy( scope.object.position );
|
||||
lastZoom = scope.object.zoom;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
console.warn( 'THREE.TrackballControls: Unsupported camera type' );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.reset = function () {
|
||||
|
||||
_state = STATE.NONE;
|
||||
_keyState = STATE.NONE;
|
||||
|
||||
scope.target.copy( scope.target0 );
|
||||
scope.object.position.copy( scope.position0 );
|
||||
scope.object.up.copy( scope.up0 );
|
||||
scope.object.zoom = scope.zoom0;
|
||||
|
||||
scope.object.updateProjectionMatrix();
|
||||
|
||||
_eye.subVectors( scope.object.position, scope.target );
|
||||
|
||||
scope.object.lookAt( scope.target );
|
||||
|
||||
scope.dispatchEvent( changeEvent );
|
||||
|
||||
lastPosition.copy( scope.object.position );
|
||||
lastZoom = scope.object.zoom;
|
||||
|
||||
};
|
||||
|
||||
// listeners
|
||||
|
||||
function onPointerDown( event ) {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
|
||||
switch ( event.pointerType ) {
|
||||
|
||||
case 'mouse':
|
||||
case 'pen':
|
||||
onMouseDown( event );
|
||||
break;
|
||||
|
||||
// TODO touch
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function onPointerMove( event ) {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
|
||||
switch ( event.pointerType ) {
|
||||
|
||||
case 'mouse':
|
||||
case 'pen':
|
||||
onMouseMove( event );
|
||||
break;
|
||||
|
||||
// TODO touch
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function onPointerUp( event ) {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
|
||||
switch ( event.pointerType ) {
|
||||
|
||||
case 'mouse':
|
||||
case 'pen':
|
||||
onMouseUp( event );
|
||||
break;
|
||||
|
||||
// TODO touch
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function keydown( event ) {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
|
||||
window.removeEventListener( 'keydown', keydown );
|
||||
|
||||
if ( _keyState !== STATE.NONE ) {
|
||||
|
||||
return;
|
||||
|
||||
} else if ( event.keyCode === scope.keys[ STATE.ROTATE ] && ! scope.noRotate ) {
|
||||
|
||||
_keyState = STATE.ROTATE;
|
||||
|
||||
} else if ( event.keyCode === scope.keys[ STATE.ZOOM ] && ! scope.noZoom ) {
|
||||
|
||||
_keyState = STATE.ZOOM;
|
||||
|
||||
} else if ( event.keyCode === scope.keys[ STATE.PAN ] && ! scope.noPan ) {
|
||||
|
||||
_keyState = STATE.PAN;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function keyup() {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
|
||||
_keyState = STATE.NONE;
|
||||
|
||||
window.addEventListener( 'keydown', keydown );
|
||||
|
||||
}
|
||||
|
||||
function onMouseDown( event ) {
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
if ( _state === STATE.NONE ) {
|
||||
|
||||
switch ( event.button ) {
|
||||
|
||||
case 0:
|
||||
_state = scope.mouseButtons.LEFT;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
_state = scope.mouseButtons.MIDDLE;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
_state = scope.mouseButtons.RIGHT;
|
||||
break;
|
||||
|
||||
default:
|
||||
_state = STATE.NONE;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var state = ( _keyState !== STATE.NONE ) ? _keyState : _state;
|
||||
|
||||
if ( state === STATE.ROTATE && ! scope.noRotate ) {
|
||||
|
||||
_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
|
||||
_movePrev.copy( _moveCurr );
|
||||
|
||||
} else if ( state === STATE.ZOOM && ! scope.noZoom ) {
|
||||
|
||||
_zoomStart.copy( getMouseOnScreen( event.pageX, event.pageY ) );
|
||||
_zoomEnd.copy( _zoomStart );
|
||||
|
||||
} else if ( state === STATE.PAN && ! scope.noPan ) {
|
||||
|
||||
_panStart.copy( getMouseOnScreen( event.pageX, event.pageY ) );
|
||||
_panEnd.copy( _panStart );
|
||||
|
||||
}
|
||||
|
||||
scope.domElement.ownerDocument.addEventListener( 'pointermove', onPointerMove );
|
||||
scope.domElement.ownerDocument.addEventListener( 'pointerup', onPointerUp );
|
||||
|
||||
scope.dispatchEvent( startEvent );
|
||||
|
||||
}
|
||||
|
||||
function onMouseMove( event ) {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
var state = ( _keyState !== STATE.NONE ) ? _keyState : _state;
|
||||
|
||||
if ( state === STATE.ROTATE && ! scope.noRotate ) {
|
||||
|
||||
_movePrev.copy( _moveCurr );
|
||||
_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
|
||||
|
||||
} else if ( state === STATE.ZOOM && ! scope.noZoom ) {
|
||||
|
||||
_zoomEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) );
|
||||
|
||||
} else if ( state === STATE.PAN && ! scope.noPan ) {
|
||||
|
||||
_panEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) );
|
||||
|
||||
}
|
||||
scope.update()
|
||||
}
|
||||
|
||||
function onMouseUp( event ) {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
_state = STATE.NONE;
|
||||
|
||||
scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove );
|
||||
scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
|
||||
|
||||
scope.dispatchEvent( endEvent );
|
||||
|
||||
}
|
||||
|
||||
function mousewheel( event ) {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
|
||||
if ( scope.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;
|
||||
|
||||
}
|
||||
scope.update()
|
||||
scope.dispatchEvent( startEvent );
|
||||
scope.dispatchEvent( endEvent );
|
||||
|
||||
}
|
||||
|
||||
function touchstart( event ) {
|
||||
|
||||
if ( scope.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;
|
||||
|
||||
}
|
||||
|
||||
scope.dispatchEvent( startEvent );
|
||||
|
||||
}
|
||||
|
||||
function touchmove( event ) {
|
||||
|
||||
if ( scope.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 );
|
||||
|
||||
var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2;
|
||||
var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2;
|
||||
_panEnd.copy( getMouseOnScreen( x, y ) );
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function touchend( event ) {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
|
||||
switch ( event.touches.length ) {
|
||||
|
||||
case 0:
|
||||
_state = STATE.NONE;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
_state = STATE.TOUCH_ROTATE;
|
||||
_moveCurr.copy( getMouseOnCircle( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) );
|
||||
_movePrev.copy( _moveCurr );
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
scope.dispatchEvent( endEvent );
|
||||
|
||||
}
|
||||
|
||||
function contextmenu( event ) {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
}
|
||||
|
||||
this.dispose = function () {
|
||||
|
||||
scope.domElement.removeEventListener( 'contextmenu', contextmenu );
|
||||
|
||||
scope.domElement.removeEventListener( 'pointerdown', onPointerDown );
|
||||
scope.domElement.removeEventListener( 'wheel', mousewheel );
|
||||
|
||||
scope.domElement.removeEventListener( 'touchstart', touchstart );
|
||||
scope.domElement.removeEventListener( 'touchend', touchend );
|
||||
scope.domElement.removeEventListener( 'touchmove', touchmove );
|
||||
|
||||
scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove );
|
||||
scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp );
|
||||
|
||||
window.removeEventListener( 'keydown', keydown );
|
||||
window.removeEventListener( 'keyup', keyup );
|
||||
|
||||
};
|
||||
|
||||
this.domElement.addEventListener( 'contextmenu', contextmenu );
|
||||
|
||||
this.domElement.addEventListener( 'pointerdown', onPointerDown );
|
||||
this.domElement.addEventListener( 'wheel', mousewheel );
|
||||
|
||||
this.domElement.addEventListener( 'touchstart', touchstart );
|
||||
this.domElement.addEventListener( 'touchend', touchend );
|
||||
this.domElement.addEventListener( 'touchmove', touchmove );
|
||||
|
||||
this.domElement.ownerDocument.addEventListener( 'pointermove', onPointerMove );
|
||||
this.domElement.ownerDocument.addEventListener( 'pointerup', onPointerUp );
|
||||
|
||||
window.addEventListener( 'keydown', keydown );
|
||||
window.addEventListener( 'keyup', keyup );
|
||||
|
||||
this.handleResize();
|
||||
|
||||
// force an update at start
|
||||
this.update();
|
||||
|
||||
};
|
||||
|
||||
TrackballControls.prototype = Object.create( EventDispatcher.prototype );
|
||||
TrackballControls.prototype.constructor = TrackballControls;
|
||||
|
||||
export { TrackballControls };
|
|
@ -0,0 +1,10 @@
|
|||
export function KeyboardController() {
|
||||
this.state="";
|
||||
window.addEventListener('keydown', (e)=> {
|
||||
if (e.key == "Escape"){
|
||||
this.state = ""
|
||||
} else {
|
||||
this.state = e.key
|
||||
}
|
||||
})
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
entry: './src/index.js',
|
||||
|
||||
plugins: [
|
||||
],
|
||||
|
||||
output: {
|
||||
filename: 'bundle.js',
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
// clean: true,
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.s[ac]ss$/i,
|
||||
use: [
|
||||
// Creates `style` nodes from JS strings
|
||||
"style-loader",
|
||||
// Translates CSS into CommonJS
|
||||
"css-loader",
|
||||
// Compiles Sass to CSS
|
||||
"sass-loader",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
const { merge } = require('webpack-merge');
|
||||
|
||||
const common = require('./webpack.common.js');
|
||||
|
||||
|
||||
module.exports = merge(common, {
|
||||
|
||||
mode: 'development',
|
||||
|
||||
// devtool: 'inline-source-map',
|
||||
|
||||
});
|
|
@ -0,0 +1,10 @@
|
|||
const { merge } = require('webpack-merge');
|
||||
|
||||
const common = require('./webpack.common.js');
|
||||
|
||||
|
||||
module.exports = merge(common, {
|
||||
|
||||
mode: 'production',
|
||||
|
||||
});
|