Skip to content

Instantly share code, notes, and snippets.

@vkuchinov
Last active October 4, 2019 10:35
Show Gist options
  • Select an option

  • Save vkuchinov/11206e4e74f526842ae8d90bb1bae995 to your computer and use it in GitHub Desktop.

Select an option

Save vkuchinov/11206e4e74f526842ae8d90bb1bae995 to your computer and use it in GitHub Desktop.
D3.JS / THREE.JS : A hybrid 3D scatter plot MkII [with dynamic axes]
id x y z colorid radius
1 4.482339877 -3.858535069 -4.466362452 9 190
2 6.06070237 -2.416998983 -1.556150519 2 472
3 5.72501809 -1.785750425 6.442992045 5 69
4 0.8025236617 -3.445751373 0.748589644 8 104
5 -2.040992897 0.2825309251 -0.0666878286 6 500
6 3.429120936 -1.507411016 -1.601075745 3 37
7 -1.58038841 0.6887202924 2.340087009 10 366
8 2.613144321 -0.7398779626 2.534196837 7 96
9 0.3859048242 1.705626991 10.68581231 4 323
10 2.060262558 -0.090579813 0.598218811 7 453
11 -1.573490398 -2.740830799 3.559481967 3 33
12 0.259324269 -0.8356099489 -0.4705839817 5 308
13 4.474273791 0.6449189057 0.3318801124 5 371
14 0.503862094 -1.029535782 -3.063317745 1 47
15 2.800118232 -0.3262068538 6.772912345 4 243
16 2.821844367 -2.911453444 -0.7522593594 3 467
17 -0.7483699574 -1.637750921 4.006477576 5 52
18 3.919926063 1.101892412 -7.021235655 6 85
19 0.5472089118 -0.6489655001 -3.112843659 1 365
20 1.017649108 0.467474673 -5.529214543 7 436
21 4.362372266 -0.3901083595 -0.2553296577 1 271
22 8.3701943 -3.170232403 6.507204661 6 360
23 -0.6345359492 -2.985823588 -5.158898941 4 190
24 2.285618547 0.2956785124 -5.440463871 4 136
25 -1.829370757 -2.741227013 -1.523032522 7 159
26 1.192071577 -0.4039159227 0.5176009868 7 114
27 2.825941013 -0.7137279042 1.988156281 7 335
28 0.8476052049 -4.614315735 -5.605282215 3 373
29 -3.761166959 1.868274892 6.860755689 7 43
30 2.228408793 -1.361769692 0.5100737156 9 48
31 0.1893628307 -1.337407098 8.399195582 7 166
32 2.606413774 0.3329580751 -2.082302643 1 15
33 4.679760062 0.8929715919 1.874565239 5 38
34 -0.70099157 -0.1385377189 -0.4194491651 7 369
35 -3.46580238 0.1695962566 -0.7606974248 1 216
36 -0.9028887483 -2.031838516 7.036592817 4 249
37 3.348113204 0.2999559961 6.199595348 9 70
38 5.24339694 0.07376166821 -2.581581077 4 102
39 -1.224334931 -1.223643593 2.047072981 4 330
40 -0.9378161219 -2.227102466 -3.403787699 2 434
41 -3.1001964 0.01266793467 -4.360014953 6 218
42 -1.416428618 -0.7445283946 -2.224576509 7 65
43 2.379371171 -2.036039495 6.390134334 2 355
44 -0.4261813065 -3.289516939 6.217816651 5 14
45 0.6397218519 -2.535916581 -12.35643942 1 193
46 4.497297556 -1.690192646 -0.4552707233 7 479
47 5.765875403 -1.247495734 -0.3353507473 7 246
48 -2.207118909 -2.227118948 -6.041511561 4 114
49 -2.072302039 2.916027463 1.492594418 4 36
50 -4.315692427 -2.391108164 3.09182016 9 223
51 -2.886370694 -1.504870818 5.027405626 9 406
52 3.994847976 1.803160507 2.876087373 6 468
53 2.416688432 -1.212287413 8.485022323 8 74
54 2.269403497 0.8228506059 1.98328284 7 166
55 1.180251238 -0.2444284997 -1.554412824 9 230
56 -2.859874042 -1.23514539 3.863829935 6 23
57 -1.385575037 -2.155677967 3.191314364 7 155
58 0.2717919017 2.89524005 -1.919524703 8 110
59 -0.8929032543 1.141162062 -1.849140817 9 315
60 2.538018428 0.9864380563 2.455553923 6 403
61 1.268958359 2.171760346 -0.3483309099 5 330
62 4.563875657 0.5951910361 -3.407356824 1 411
63 1.554130376 0.2387495851 -3.520653265 3 370
64 -1.217342608 -5.087350013 0.4459298342 7 269
65 -3.084204817 -2.646182125 3.21598289 6 155
66 -0.2966085985 0.5262995789 -1.87751911 6 485
67 4.197791039 1.618979226 2.201506115 4 359
68 -1.029914674 -2.885648568 -3.035082384 6 137
69 -2.482393241 -6.993660174 -4.874650816 2 370
70 -0.6066044553 -6.908968042 2.492838862 4 196
71 -0.1422670849 -2.372655161 8.394659964 8 151
72 1.988227349 -4.895044871 -1.904022719 1 21
73 4.000090801 -0.8684559946 -4.295235973 6 212
74 -1.249810939 -0.1964712067 -0.455365154 7 102
75 1.510891505 -2.791589729 0.6658998548 1 141
76 0.259759314 0.02643963578 -2.204858223 1 24
77 1.791441333 -7.090766909 0.9637145503 3 170
78 -2.640177003 -0.1335109077 4.048550909 2 300
79 3.204001931 -3.865529917 -12.62370265 9 199
80 -2.080061565 -1.00360148 -5.9071611 9 291
81 -1.454456246 -4.56220579 3.852605418 2 250
82 -3.451573906 -4.638741026 6.392428552 5 56
83 -0.9164897038 0.8347000778 -3.77010017 5 322
84 -0.9898018772 -3.955191441 9.033400771 1 405
85 -3.215943133 0.6396778863 -8.088847059 9 52
86 1.887346723 -0.3757330898 5.60723664 8 412
87 -1.212605241 -3.634833529 9.852661553 9 218
88 0.4575703952 -3.776889826 -0.6357365465 4 204
89 1.000206856 1.180461692 7.183484281 10 434
90 3.991980672 -3.119541182 -2.291057293 5 494
91 3.148096685 0.660074024 1.646828236 7 135
92 3.757003436 1.691072709 6.652611785 8 290
93 -1.175184091 0.253947733 4.06789772 7 330
94 -2.01266346 -2.002267507 3.634406206 4 130
95 1.51803949 -3.716944736 0.1083323603 4 252
96 0.8290121091 -0.2510098422 -12.32325899 10 471
97 3.937185551 1.459966877 -6.114019644 1 174
98 -3.801581684 0.2704052951 -9.950370169 9 428
99 4.400536621 0.8708577188 7.693085685 3 283
100 0.7096924036 -0.5269381067 -3.745397591 9 355
101 0.2508476169 -0.7492739626 3.87423747 5 224
102 -3.534026131 -0.004468464466 5.936880748 4 67
103 -2.998507112 1.488914432 7.114224987 8 490
104 3.013481459 -2.430649097 4.905641151 5 19
105 1.29990998 1.372236091 5.140207336 5 295
106 -2.554991052 0.3667789531 4.058210317 6 167
107 1.604996355 1.897431544 -2.404243031 5 86
108 -1.709317063 -1.566626738 0.7385438477 4 379
109 3.642224823 3.047397772 -1.991450451 6 325
110 1.012055276 3.340189534 -8.405616583 9 185
111 -0.6044469527 2.578390749 2.820234273 7 174
112 -5.137864626 -6.041315028 -2.535793246 5 437
113 -2.830461138 -3.571706532 5.896343651 3 472
114 -4.18962624 0.4124038906 3.016398105 1 342
115 3.703531729 -1.59199886 0.6119123439 5 243
116 3.464679941 -0.1827347235 -2.706711571 8 270
117 1.774740376 -2.63823051 -6.881772605 1 473
118 3.817522564 -2.294583722 -7.454056997 3 341
119 1.512707243 -3.294511769 1.108873827 2 274
120 -0.1773438082 -0.1749495435 -3.68120562 10 217
121 4.465674328 1.408029384 2.027454682 7 444
122 -4.73226317 -0.4261750287 0.04445084115 3 259
123 0.8995207935 -0.442625909 -4.00290775 10 414
124 -6.602214586 0.5788109601 -2.058200147 5 389
125 2.348234861 -0.1642339903 11.32243203 3 178
126 1.54582728 3.38436122 -4.171230038 3 46
127 -1.905651022 -1.836132133 -0.7930360171 4 335
128 5.598196528 2.047962088 -1.73243339 5 25
129 1.265043109 1.511219884 0.8665718372 2 486
130 -1.723838267 -1.863922558 5.741547344 1 260
131 7.610274096 -1.562715475 -3.391796789 7 334
132 0.7449588712 -1.604241525 2.640844276 9 449
133 3.009271787 -1.908787681 -0.3977628628 9 326
134 0.9787898265 -1.891361104 -1.503800683 7 254
135 -0.3423956537 -2.023428678 -3.516815231 2 122
136 0.4672862382 -0.5797585737 4.022959292 7 213
137 3.773642784 -4.956875872 3.45526531 6 72
138 0.5116408932 -6.410042268 9.577598538 8 187
139 -0.9206394109 1.804133584 -10.39355528 4 176
140 0.6690137223 -3.044678386 -5.777699596 6 409
141 -3.402169216 -2.09622438 3.949329111 3 277
142 1.093508501 0.5896416473 1.289395682 7 28
143 0.03018293409 -0.4813050486 -5.165644548 4 492
144 2.855021128 -2.854722329 10.13158759 6 16
145 0.55912314 -3.231235567 0.1702737766 1 75
146 2.766385371 0.7562061905 -7.903760785 7 134
147 -4.2539117 -2.732028921 -3.133456924 9 135
148 -0.02521865821 3.136838471 3.119469423 4 85
149 -1.314909327 -2.01034686 -5.37864583 1 405
150 2.697082352 0.4900173748 -1.642320691 6 29
151 3.504188385 -2.736478881 -8.128141955 4 122
152 2.173806393 -2.020463585 -2.610113285 9 360
153 0.6231183111 -0.8465809738 0.9140431289 1 192
154 -0.8319773373 -1.827977706 -2.844159501 5 227
155 -0.307455075 -2.475200733 -4.542229996 10 456
156 3.319616071 1.079863401 2.893022188 4 103
157 1.442338582 -0.46234663 6.506135029 6 385
158 -4.269178223 -1.4174265 -2.349592638 7 117
159 11.08371198 2.720121038 -3.773052279 5 58
160 2.88651955 -1.927467624 -4.362905513 10 7
161 -0.8630325965 -2.855807777 1.954610226 7 140
162 0.6140463988 4.912806043 12.14822075 2 250
163 -1.097776223 0.2253234193 1.214721325 2 339
164 0.9169856765 3.468961752 7.511568162 7 39
165 1.734757298 -4.134320531 5.340284841 8 244
166 0.9575602778 0.6440991922 0.6879637417 2 109
167 2.082999527 1.31744689 -5.323603858 6 224
168 3.195904559 0.3078603374 -0.4490530943 6 57
169 7.788457731 -2.893718465 1.892125032 5 337
170 3.31851417 -2.344642912 5.349601675 3 74
171 -0.2204259924 -4.246707265 -2.602960653 6 450
172 -1.50120167 2.182364771 -8.134973938 7 206
173 3.521490144 -4.335186154 -4.311449169 6 120
174 0.1920023785 0.4446393539 -6.83571217 8 421
175 2.364651102 1.041621559 -1.477080683 9 351
176 4.064711382 0.112822511 -1.750193036 1 302
177 5.547667872 1.625080625 5.703752789 9 109
178 -2.156390532 -1.231003224 0.971017726 2 469
179 0.7468041114 2.998206892 -4.246549821 3 364
180 -0.5937925872 -1.725175117 2.853594058 5 182
181 4.634091423 -1.900343956 -1.780078552 7 121
182 4.322784234 -0.02800943323 6.109667386 9 284
183 -0.1633564937 -3.376072412 6.843331712 9 177
184 3.71381409 -0.7255510755 -7.401172117 8 58
185 -0.4429915355 -4.098828825 -7.622207517 10 140
186 4.626637212 -2.818210453 4.412723695 10 473
187 4.726871471 0.7745512648 0.5160346967 1 39
188 -2.048713617 3.680638344 -4.480787577 2 10
189 3.842468777 -2.574667154 -2.872819641 3 468
190 0.5202150677 -1.626831576 -1.589327429 10 24
191 5.855603553 -0.7628637139 2.27208959 1 351
192 -1.445359447 -2.724954853 -5.498680548 7 20
193 5.981980075 -1.143839398 -5.883979682 10 188
194 -0.1827975719 -0.3986336841 -1.978201805 7 22
195 0.05038021122 0.8063352507 7.57544709 3 477
196 -1.995681238 3.074781574 5.98454445 3 200
197 2.084807282 -0.8099439468 10.55498706 5 88
198 -3.6163859 -1.837353511 -7.479888428 6 138
199 1.525489218 -2.768055416 4.605818247 2 173
200 1.419045296 -0.9217519312 5.081529479 3 129
201 -1.179410644 -1.803355335 -6.510394446 7 299
202 8.174817451 1.671196348 3.670962478 10 77
203 3.969456614 -0.3769042039 3.153340024 2 14
204 -3.510521251 -0.4885388305 -0.1366992208 8 300
205 1.826937405 -4.720226667 2.499582127 10 23
206 6.637764989 -1.502159384 9.719033941 4 192
207 2.659828708 -2.735513466 2.525907392 1 432
208 0.5974903032 -2.354154695 -3.491373781 9 226
209 -0.1991238014 -4.099492796 2.955639501 5 259
210 -1.783764881 0.03963428585 -3.416409343 2 428
211 1.287861768 -2.243651063 -5.27727244 3 106
212 1.403375596 -0.2015628868 4.298266741 3 153
213 6.221427915 -0.1592802635 1.335541533 10 126
214 1.770599719 0.8969798302 -1.161481263 9 304
215 2.890182445 -0.5036565754 -5.341659507 9 500
216 5.527484607 -0.5349384451 8.021744779 6 155
217 2.070373964 3.151711974 6.786808143 6 458
218 2.728852887 3.388031134 -1.044565559 9 391
219 1.323276056 -0.5078843246 -2.81834301 4 480
220 0.5127549501 0.5688528009 1.024423159 7 89
221 0.5475059809 0.0006064243935 -6.007079703 3 412
222 -2.072244172 1.380716259 -11.6743955 7 362
223 -5.268566173 0.9045962182 1.188833589 6 43
224 2.205885669 -1.313354087 0.0749670464 8 247
225 -0.1952395385 0.5891383005 -0.6954121981 9 320
226 1.990337079 1.345415097 -11.44780535 6 403
227 0.3818823362 0.3822895971 -2.296285885 9 336
228 2.146984162 -2.617218909 -1.879975304 8 491
229 3.969698912 0.3608257731 2.057759671 7 144
230 2.330484191 -0.9218226967 8.908476341 2 203
231 -2.409326765 2.467323569 6.514733622 3 438
232 -0.374073876 -0.294374026 2.034232889 4 102
233 -0.9638425012 1.177179979 1.442536631 8 125
234 -3.874384794 1.309375722 -4.105864573 9 74
235 2.508982046 3.816586775 -6.455756243 8 168
236 -1.384928178 -0.02861923486 2.165773286 3 158
237 0.4020590467 -1.744230452 2.900547132 7 237
238 -3.715680216 -0.9877393895 -0.8595520771 6 12
239 -0.2161101704 -2.056877246 3.254626375 6 68
240 1.667725533 -1.84107345 -1.732818476 7 57
241 0.8498293351 0.3100162782 -0.6953755836 7 278
242 -0.0406952062 0.3021411409 -5.035428466 9 282
243 7.972474491 -2.214267653 6.679337266 3 34
244 -1.640587005 -1.924811465 -1.98742659 1 213
245 2.030601793 -0.5475607999 3.707307433 3 234
246 1.427936203 -0.8044619249 -1.610625965 3 411
247 2.32765769 1.832532742 2.367470721 2 441
248 -1.025494308 0.03107451884 -4.572442628 7 245
249 -0.2847716563 -1.422046008 -0.3680170925 2 44
250 6.91002173 -1.453395911 -8.136692442 5 43
251 2.525896821 -3.448849921 0.003623200085 1 44
252 2.350368048 -4.017176863 2.706762456 1 17
253 -3.731041131 -2.109840253 -3.536905592 4 230
254 0.01777154189 0.5309864861 5.302710102 3 290
255 4.660792805 -1.974615035 5.50948696 5 62
/*
* @author zz85 / https://github.com/zz85
* @author mrdoob / http://mrdoob.com
* Running this will allow you to drag three.js objects around the screen.
* Modified for D3.JS / THREE.JS stack by
* @author vkuchinov / https://github.com/vkuchinov
*
*/
THREE.DragControls = function ( _object, _camera) {
var _domElement = document;
var rect = {
bottom: window.innerHeight,
height: window.innerHeight,
left: 0,
right: window.innerWidth,
top: 0,
width: window.innerWidth,
x: 0,
y: 0
};
var _plane = new THREE.Plane();
var _raycaster = new THREE.Raycaster();
var _mouse = new THREE.Vector2();
var _offset = new THREE.Vector3();
var _intersection = new THREE.Vector3();
var _mouseDown = false;
var scope = this;
function activate() {
_domElement.addEventListener( "mousedown", onDocumentMouseDown, false );
_domElement.addEventListener( "mousemove", onDocumentMouseMove, false );
_domElement.addEventListener( "mouseup", onDocumentMouseUp, false );
_domElement.addEventListener( "mouseleave", onDocumentMouseUp, false );
}
function deactivate() {
_domElement.removeEventListener( "mousedown", onDocumentMouseDown, false );
_domElement.removeEventListener( "mousemove", onDocumentMouseMove, false );
_domElement.removeEventListener( "mouseup", onDocumentMouseUp, false );
_domElement.removeEventListener( "mouseleave", onDocumentMouseUp, false );
}
function dispose() { deactivate(); }
function onDocumentMouseDown( event_ ) {
_mouseDown = true;
event_.preventDefault();
_raycaster.setFromCamera( _mouse, _camera );
if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
_offset.copy( _intersection ).sub( _object.position );
}
document.getElementById("scatter3D").style.cursor = "move";
scope.dispatchEvent( { type: "dragstart", object: _object } );
}
function onDocumentMouseMove( event_ ) {
_plane.normal = new THREE.Vector3(_camera.position.x, _camera.position.y, _camera.position.z);
_plane.normal.normalize();
event_.preventDefault();
if(_mouseDown){
_mouse.x = ( ( event.clientX - rect.left ) / rect.width ) * 2 - 1;
_mouse.y = - ( ( event.clientY - rect.top ) / rect.height ) * 2 + 1;
_raycaster.setFromCamera( _mouse, _camera );
if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
_object.position.copy( _intersection.sub( _offset ) );
}
scope.dispatchEvent( { type: "drag", object: _object } );
return;
}
}
function onDocumentMouseUp( event_ ) {
_mouseDown = false;
event_.preventDefault();
scope.dispatchEvent( { type: "dragend", object: _object } );
document.getElementById("scatter3D").style.cursor = "auto";
}
// API
this.enabled = false;
this.activate = activate;
this.deactivate = deactivate;
this.dispose = dispose;
// Backward compatibility
this.setObjects = function () {
console.error( "THREE.DragControls: setObjects() has been removed." );
};
this.on = function ( type, listener ) {
console.warn( "THREE.DragControls: on() has been deprecated. Use addEventListener() instead." );
scope.addEventListener( type, listener );
};
this.off = function ( type, listener ) {
console.warn( "THREE.DragControls: off() has been deprecated. Use removeEventListener() instead." );
scope.removeEventListener( type, listener );
};
this.notify = function ( type ) {
console.error( "THREE.DragControls: notify() has been deprecated. Use dispatchEvent() instead." );
scope.dispatchEvent( { type: type } );
};
};
THREE.DragControls.prototype = Object.create( THREE.EventDispatcher.prototype );
THREE.DragControls.prototype.constructor = THREE.DragControls;
<!doctype html>
<html>
<head>
<title>D3.JS & THREE.JS Hybrid Scatter Plot 3D</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/98/three.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/stats.js/r16/Stats.min.js"></script>
<script src="OrbitControls.js"></script>
<script src="DragControls4D3.js"></script>
<link href="styles.css" rel="stylesheet"/>
</head>
<body>
<button style="right:0" id="switch" class="ui" onclick="switchUI()">Rotate</button>
<button style="right: 64px" id="showGrid" class="ui" onclick="turnGridOnOff()">Show Grid</button>
<button style="right: 150px" id="scaleGrid" class="ui" onclick="turnOnOffScale()">Static Grid</button>
<svg id="scatter3D" width="0" height="0"></svg>
<div id="tooltip">
<div id="x_value" class="xl">x: 0</div>
<div id="y_value" class="yl">y: 0</div>
<div id="z_value" class="zl">z: 0</div>
</div>
<script src="main.js"></script>
</body>
</html>
var stats, scene, renderer, controls, dragControls;
var camera, cameraControl;
var pointCLoud, points = [], sorted = [], axes = [], labels = [], grids = [];
var offset = new THREE.Vector3();
var drag = false;
var limits = { x: { min: -10, max: 10 }, y: { min: -10, max: 10 }, z: { min: -10, max: 10 } }
var svg;
var colors = d3.scaleOrdinal(d3.schemeCategory10);
var radiusRatio = 2.5E-2;
var w = window.innerWidth, h = window.innerHeight;
var xAxis, yAxis, zAxis;
var scalable = false;
var gridVisible = false;
var xScale = d3.scaleLinear().range([0, window.innerWidth * 0.25]).domain([limits.x.min, limits.x.max]);
var yScale = d3.scaleLinear().range([0, window.innerWidth * 0.25]).domain([limits.y.min, limits.y.max]);
var zScale = d3.scaleLinear().range([0, window.innerWidth * 0.25]).domain([limits.z.min, limits.z.max]);
d3.csv("data.csv", function(error_, data_) {
if (error_) throw error_;
data_.forEach(function(d_) {
points.push({x: Number(d_.x), y: Number(d_.y), z: Number(d_.z), c: d_.colorid, r: d_.radius })
});
svg = d3.select("svg").attr("width", w).attr("height", h);
xAxis = svg.append("g").attr("transform", "translate( " + (window.innerWidth - 32) + "," + ((window.innerHeight / 2) - window.innerHeight * 0.25) + ")").call(d3.axisLeft(xScale).ticks(6));
var xLabel = svg.append("text").attr("transform", "translate( " + (window.innerWidth - 40) + "," + ((window.innerHeight / 2) - window.innerHeight * 0.25 - 20) + ")").attr("font-family", "sans-serif").text("X");
yAxis = svg.append("g").attr("transform", "translate(" + + ((window.innerWidth / 2) - window.innerWidth * 0.125) + ", 32)").call(d3.axisTop(yScale).ticks(6));
var yLabel = svg.append("text").attr("transform", "translate(" + + ((window.innerWidth / 2) - window.innerWidth * 0.125 - 32) + ", 32)").attr("font-family", "sans-serif").text("Y");
zAxis = svg.append("g").attr("transform", "translate(32," + ((window.innerHeight / 2) - window.innerHeight * 0.25) + ")").call(d3.axisLeft(zScale).ticks(6));
var zLabel = svg.append("text").attr("transform", "translate(26," + ((window.innerHeight / 2) - window.innerHeight * 0.25 - 20) + ")").attr("font-family", "sans-serif").text("Z");
init();
});
function init(){
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
stats = new Stats();
stats.domElement.style.position = "absolute";
stats.domElement.style.bottom = "0px";
document.body.appendChild( stats.domElement );
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.set(256, 512, 512);
camera.lookAt(0, 0, 0)
scene.add(camera);
var cloud = new THREE.Geometry();
points.forEach(function(d_) {
var p = new THREE.Vector3(d_.x * 16 , d_.y * 16 , d_.z * 16);
cloud.vertices.push( p );
});
var cloudmaterial = new THREE.PointsMaterial( { color: 0xFFFFFF } );
pointCloud = new THREE.Points( cloud , cloudmaterial );
scene.add( pointCloud );
controls = new THREE.OrbitControls( camera, document );
controls.enableDamping = true;
controls.dampingFactor = 0.25;
controls.screenSpacePanning = false;
controls.minDistance = 100;
controls.maxDistance = 500;
controls.maxPolarAngle = Math.PI / 2;
dragControls = new THREE.DragControls( pointCloud, camera );
animate();
}
function animate() {
var sv = new THREE.Vector3(0, 0, 0);
var scale = 768.0 / sv.subVectors(new THREE.Vector3(0, 0, 0), camera.position).length();
if(!scalable){
axes = [
[new THREE.Vector3(-196.0 / scale, 1E-5, 1E-5), new THREE.Vector3(196.0 / scale, 1E-5, 1E-5)],
[new THREE.Vector3(1E-5, -196.0 / scale, 1E-5), new THREE.Vector3(1E-5, 196.0 / scale, 1E-5)],
[new THREE.Vector3(1E-5, 1E-5, -196.0 / scale), new THREE.Vector3(1E-5, 1E-5, 196.0 / scale)]
];
labels = [
new THREE.Vector3(212.0 / scale, 1E-5, 1E-5),
new THREE.Vector3(1E-5, 212.0 / scale, 1E-5),
new THREE.Vector3(1E-5, 1E-5, 222.0 / scale)
];
grids = [];
for(var xz = 0; xz <= 147.0; xz += 12.25){
grids.push([new THREE.Vector3(xz / scale, 1E-5, 1E-5), new THREE.Vector3(xz / scale, 1E-5, 147 / scale)]);
grids.push([new THREE.Vector3(1E-5, 1E-5, xz / scale), new THREE.Vector3(147.0 / scale, 1E-5, xz / scale)]);
}
}
else {
axes = [
[new THREE.Vector3(-196.0, 1E-5, 1E-5), new THREE.Vector3(196.0, 1E-5, 1E-5)],
[new THREE.Vector3(1E-5, -196.0, 1E-5), new THREE.Vector3(1E-5, 196.0, 1E-5)],
[new THREE.Vector3(1E-5, 1E-5, -196.0), new THREE.Vector3(1E-5, 1E-5, 196.0)]
];
labels = [
new THREE.Vector3(212.0, 1E-5, 1E-5),
new THREE.Vector3(1E-5, 212.0, 1E-5),
new THREE.Vector3(1E-5, 1E-5, 222.0)
];
grids = [];
for(var xz = 0; xz <= 147.0; xz += 12.25){
grids.push([new THREE.Vector3(xz, 1E-5, 1E-5), new THREE.Vector3(xz, 1E-5, 147.0 / scale)]);
grids.push([new THREE.Vector3(1E-5, 1E-5, xz), new THREE.Vector3(147, 1E-5, xz)]);
}
}
controls.update();
renderSVG(pointCloud.position);
requestAnimationFrame( animate );
stats.update();
renderer.render( scene, camera );
}
function toScreenXY (x_, y_, z_, camera_) {
var p = new THREE.Vector3(x_, y_, z_);
var vector = p.project(camera_);
vector.x = (vector.x + 1) / 2 * window.innerWidth;
vector.y = -(vector.y - 1) / 2 * window.innerHeight;
return {x: vector.x, y: vector.y };
}
function renderSVG(p_){
limits = {
x: { min: Number.POSITIVE_INFINITY, max: Number.NEGATIVE_INFINITY },
y: { min: Number.POSITIVE_INFINITY, max: Number.NEGATIVE_INFINITY },
z: { min: Number.POSITIVE_INFINITY, max: Number.NEGATIVE_INFINITY }
};
var projectedPoints = svg.selectAll("circle").data(pointCloud.geometry.vertices);
projectedPoints.enter()
.append("circle")
.attr("class", "_3d")
.attr("opacity", 1.0)
.merge(projectedPoints)
.attr("cx", function(d_){ return valueValidation(toScreenXY(d_.x + p_.x, d_.y + p_.y, d_.z + p_.z, camera).x); })
.attr("cy", function(d_){ return valueValidation(toScreenXY(d_.x + p_.x, d_.y + p_.y, d_.z + p_.z, camera).y); })
.attr("r", function(d_, i_) { return points[i_].r * radiusRatio; })
.attr("fill", function(d_, i_) { return colors(points[i_].c); })
.attr("opacity", function(d_){
if(isPointOnScreen(d_, p_)){
limits.x.min = Math.min((d_.x + p_.x) / 16.0, limits.x.min);
limits.x.max = Math.max((d_.x + p_.x) / 16.0, limits.x.max);
limits.y.min = Math.min((d_.y + p_.y) / 16.0, limits.y.min);
limits.y.max = Math.max((d_.y + p_.y) / 16.0, limits.y.max);
limits.z.min = Math.min((d_.z + p_.z) / 16.0, limits.z.min);
limits.z.max = Math.max((d_.z + p_.z) / 16.0, limits.z.max);
return 0.6;
}
return 0.6;
})
.on("mouseover", function(d_){
if(!drag){
d3.select(this).attr("stroke", "#DEDEDE")
.attr("stroke-width", 6);
var t = d3.select("#tooltip")
.style("left", d3.select(this).attr("cx") + "px")
.style("top", d3.select(this).attr("cy") + "px");
document.getElementById("x_value").innerHTML = "x: " + (d_.x / 16.0).toFixed(2);
document.getElementById("y_value").innerHTML = "y: " + (d_.y / 16.0).toFixed(2);
document.getElementById("z_value").innerHTML = "z: " + (d_.z / 16.0).toFixed(2);
}
})
.on("mouseout", function(d_){
if(!drag){
d3.select(this).attr("stroke", "none")
var t = d3.select("#tooltip")
.style("left","-999px")
.style("top", "-999px");
}
})
projectedPoints.exit().remove();
if(gridVisible){
var projectedGrids = svg.selectAll("line.Grids").data(grids);
projectedGrids.enter()
.append("line")
.attr("class", "3d_ Grids")
.merge(projectedGrids)
.attr("x1", function(d_) { return valueValidation(toScreenXY(d_[0].x, d_[0].y, d_[0].z, camera).x); })
.attr("y1", function(d_) { return valueValidation(toScreenXY(d_[0].x, d_[0].y, d_[0].z, camera).y); })
.attr("x2", function(d_) { return valueValidation(toScreenXY(d_[1].x, d_[1].y, d_[1].z, camera).x); })
.attr("y2", function(d_,){ return valueValidation(toScreenXY(d_[1].x, d_[1].y, d_[1].z, camera).y); })
.attr("stroke", "#DEDEDE")
.attr("stroke-width", 0.25);
projectedGrids.exit().remove();
}
var projectedAxes = svg.selectAll("line.Axes3").data(axes);
projectedAxes.enter()
.append("line")
.attr("class", "3d_ Axes3")
.merge(projectedAxes)
.attr("x1", function(d_) { return valueValidation(toScreenXY(d_[0].x, d_[0].y, d_[0].z, camera).x); })
.attr("y1", function(d_) { return valueValidation(toScreenXY(d_[0].x, d_[0].y, d_[0].z, camera).y); })
.attr("x2", function(d_) { return valueValidation(toScreenXY(d_[1].x, d_[1].y, d_[1].z, camera).x); })
.attr("y2", function(d_,){ return valueValidation(toScreenXY(d_[1].x, d_[1].y, d_[1].z, camera).y); })
.attr("stroke", "#000000");
projectedAxes.exit().remove();
var projectedLabels = svg.selectAll("text.Labels").data(labels);
projectedLabels.enter()
.append("text")
.attr("class", "Labels")
.merge(projectedLabels)
.attr("dx", function (d_) {
var dx = valueValidation(toScreenXY(d_.x, d_.y, d_.z, camera).x);
return dx;
})
.attr("dy", function (d_) {
var dy = valueValidation(toScreenXY(d_.x, d_.y, d_.z, camera).y);
return dy;
})
.attr("font-family", "sans-serif")
.text(function(d_, i_) { var l = ["x", "y", "z"]; return l[i_]; });
projectedLabels.exit().remove();
xScale.domain([limits.x.min, limits.x.max]);
yScale.domain([limits.y.min, limits.y.max]);
zScale.domain([limits.z.min, limits.z.max]);
xAxis.call(d3.axisLeft(xScale).ticks(6));
yAxis.call(d3.axisTop(yScale).ticks(6));
zAxis.call(d3.axisLeft(zScale).ticks(6));
}
function isPointOnScreen(d_, p_){
var tmp = new THREE.Vector3(d_.x + p_.x, d_.y + p_.y, d_.z + p_.z);
var xy = toScreenXY(tmp.x, tmp.y, tmp.z, camera);
if(!xy.x.between([0, window.innerWidth])) { return false; }
if(!xy.y.between([0, window.innerHeight])) { return false; }
return true;
}
function valueValidation(v_){ return v_ || 0; };
function switchUI(){
var b = document.getElementById("switch");
if(b.innerHTML == "Rotate") {
b.innerHTML = "Drag";
controls.enabled = false;
drag = true;
dragControls.enabled = true;
dragControls.activate();
}
else {
b.innerHTML = "Rotate";
controls.enabled = true;
drag = false;
dragControls.enabled = false;
dragControls.deactivate();
}
}
function turnOnOffScale(){
var b = document.getElementById("scaleGrid");
if(b.innerHTML == "Static Grid") {
scalable = true;
b.innerHTML = "Scalable Grid";
}
else {
scalable = false;
b.innerHTML = "Static Grid";
}
}
function turnGridOnOff(){
var b = document.getElementById("showGrid");
if(b.innerHTML == "Show Grid") {
gridVisible = true;
b.innerHTML = "Hide Grid";
}
else {
gridVisible = false;
b.innerHTML = "Show Grid";
d3.selectAll(".Grids").remove();
}
}
Number.prototype.between = function(domain_) {
var min = Math.min.apply(Math, domain_);
var max = Math.max.apply(Math, domain_);
return this >= min && this <= max;
};
/**
* @author qiao / https://github.com/qiao
* @author mrdoob / http://mrdoob.com
* @author alteredq / http://alteredqualia.com/
* @author WestLangley / http://github.com/WestLangley
*/
THREE.OrbitControls = function ( object, domElement ) {
this.object = object;
this.domElement = ( domElement !== undefined ) ? domElement : document;
// API
this.enabled = true;
this.center = new THREE.Vector3();
this.userZoom = true;
this.userZoomSpeed = 1.0;
this.userRotate = true;
this.userRotateSpeed = 1.0;
this.userPan = true;
this.userPanSpeed = 2.0;
this.autoRotate = false;
this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
this.minPolarAngle = 0; // radians
this.maxPolarAngle = Math.PI; // radians
this.minDistance = 0;
this.maxDistance = Infinity;
// 65 /*A*/, 83 /*S*/, 68 /*D*/
this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40, ROTATE: 65, ZOOM: 83, PAN: 68 };
// internals
var scope = this;
var EPS = 0.000001;
var PIXELS_PER_ROUND = 1800;
var rotateStart = new THREE.Vector2();
var rotateEnd = new THREE.Vector2();
var rotateDelta = new THREE.Vector2();
var zoomStart = new THREE.Vector2();
var zoomEnd = new THREE.Vector2();
var zoomDelta = new THREE.Vector2();
var phiDelta = 0;
var thetaDelta = 0;
var scale = 1;
var lastPosition = new THREE.Vector3();
var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2 };
var state = STATE.NONE;
// events
var changeEvent = { type: 'change' };
this.rotateLeft = function ( angle ) {
if ( angle === undefined ) {
angle = getAutoRotationAngle();
}
thetaDelta -= angle;
};
this.rotateRight = function ( angle ) {
if ( angle === undefined ) {
angle = getAutoRotationAngle();
}
thetaDelta += angle;
};
this.rotateUp = function ( angle ) {
if ( angle === undefined ) {
angle = getAutoRotationAngle();
}
phiDelta -= angle;
};
this.rotateDown = function ( angle ) {
if ( angle === undefined ) {
angle = getAutoRotationAngle();
}
phiDelta += angle;
};
this.zoomIn = function ( zoomScale ) {
if ( zoomScale === undefined ) {
zoomScale = getZoomScale();
}
scale /= zoomScale;
};
this.zoomOut = function ( zoomScale ) {
if ( zoomScale === undefined ) {
zoomScale = getZoomScale();
}
scale *= zoomScale;
};
this.pan = function ( distance ) {
distance.transformDirection( this.object.matrix );
distance.multiplyScalar( scope.userPanSpeed );
this.object.position.add( distance );
this.center.add( distance );
};
this.update = function () {
var position = this.object.position;
var offset = position.clone().sub( this.center );
// 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 ) );
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.center ).add( offset );
this.object.lookAt( this.center );
thetaDelta = 0;
phiDelta = 0;
scale = 1;
if ( lastPosition.distanceTo( this.object.position ) > 0 ) {
this.dispatchEvent( changeEvent );
lastPosition.copy( this.object.position );
}
};
function getAutoRotationAngle() {
return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
}
function getZoomScale() {
return Math.pow( 0.95, scope.userZoomSpeed );
}
function onMouseDown( event ) {
if ( scope.enabled === false ) return;
if ( scope.userRotate === false ) return;
event.preventDefault();
if ( state === STATE.NONE )
{
if ( event.button === 0 )
state = STATE.ROTATE;
if ( event.button === 1 )
state = STATE.ZOOM;
if ( event.button === 2 )
state = STATE.PAN;
}
if ( state === STATE.ROTATE ) {
//state = STATE.ROTATE;
rotateStart.set( event.clientX, event.clientY );
} else if ( state === STATE.ZOOM ) {
//state = STATE.ZOOM;
zoomStart.set( event.clientX, event.clientY );
} else if ( state === STATE.PAN ) {
//state = STATE.PAN;
}
document.addEventListener( 'mousemove', onMouseMove, false );
document.addEventListener( 'mouseup', onMouseUp, false );
}
function onMouseMove( event ) {
if ( scope.enabled === false ) return;
event.preventDefault();
if ( state === STATE.ROTATE ) {
rotateEnd.set( event.clientX, event.clientY );
rotateDelta.subVectors( rotateEnd, rotateStart );
scope.rotateLeft( 2 * Math.PI * rotateDelta.x / PIXELS_PER_ROUND * scope.userRotateSpeed );
scope.rotateUp( 2 * Math.PI * rotateDelta.y / PIXELS_PER_ROUND * scope.userRotateSpeed );
rotateStart.copy( rotateEnd );
} else if ( state === STATE.ZOOM ) {
zoomEnd.set( event.clientX, event.clientY );
zoomDelta.subVectors( zoomEnd, zoomStart );
if ( zoomDelta.y > 0 ) {
scope.zoomIn();
} else {
scope.zoomOut();
}
zoomStart.copy( zoomEnd );
} else if ( state === STATE.PAN ) {
var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
scope.pan( new THREE.Vector3( - movementX, movementY, 0 ) );
}
}
function onMouseUp( event ) {
if ( scope.enabled === false ) return;
if ( scope.userRotate === false ) return;
document.removeEventListener( 'mousemove', onMouseMove, false );
document.removeEventListener( 'mouseup', onMouseUp, false );
state = STATE.NONE;
}
function onMouseWheel( event ) {
if ( scope.enabled === false ) return;
if ( scope.userZoom === false ) return;
var delta = 0;
if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9
delta = event.wheelDelta;
} else if ( event.detail ) { // Firefox
delta = - event.detail;
}
if ( delta > 0 ) {
scope.zoomOut();
} else {
scope.zoomIn();
}
}
function onKeyDown( event ) {
if ( scope.enabled === false ) return;
if ( scope.userPan === false ) return;
switch ( event.keyCode ) {
/*case scope.keys.UP:
scope.pan( new THREE.Vector3( 0, 1, 0 ) );
break;
case scope.keys.BOTTOM:
scope.pan( new THREE.Vector3( 0, - 1, 0 ) );
break;
case scope.keys.LEFT:
scope.pan( new THREE.Vector3( - 1, 0, 0 ) );
break;
case scope.keys.RIGHT:
scope.pan( new THREE.Vector3( 1, 0, 0 ) );
break;
*/
case scope.keys.ROTATE:
state = STATE.ROTATE;
break;
case scope.keys.ZOOM:
state = STATE.ZOOM;
break;
case scope.keys.PAN:
state = STATE.PAN;
break;
}
}
function onKeyUp( event ) {
switch ( event.keyCode ) {
case scope.keys.ROTATE:
case scope.keys.ZOOM:
case scope.keys.PAN:
state = STATE.NONE;
break;
}
}
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
window.addEventListener( 'keydown', onKeyDown, false );
window.addEventListener( 'keyup', onKeyUp, false );
};
THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );
body {
overflow: hidden;
margin: 0;
}
svg { z-index: 1; }
.ui { position: absolute; z-index: 2; }
button{ margin: 20px; }
.xl { grid-area: xv; text-align: center; }
.yl { grid-area: yv; text-align: center; }
.zl { grid-area: zv; text-align: center; }
#tooltip > div {
color: #FEFEFE; font-size: 12px;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none
-ms-user-select: none;
user-select: none;
}
#tooltip {
position: absolute;
text-align: center;
width: 192px;
font: 10px sans-serif;
border: 0px;
border-radius: 6px;
opacity: 0.6;
top: -9999px;
left: -9999px;
display: grid;
grid-template-areas: "xv yv zv";
grid-gap: 10px;
background-color: #000000;
padding: 10px;
z-index: 99;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment