summaryrefslogtreecommitdiff
path: root/client
diff options
context:
space:
mode:
Diffstat (limited to 'client')
-rw-r--r--client/splash/cloud/datasetList.js1
-rw-r--r--client/splash/cloud/index.js52
-rw-r--r--client/splash/constants.js54
-rw-r--r--client/splash/face/faces.js13
-rw-r--r--client/splash/face/index.js22
-rw-r--r--client/splash/face/markers.js101
-rw-r--r--client/splash/face/mesh.js84
-rw-r--r--client/splash/face/util.js99
-rw-r--r--client/splash/index.js42
-rw-r--r--client/splash/renderer.js41
-rw-r--r--client/util/index.js (renamed from client/util.js)0
-rw-r--r--client/util/math.js51
-rw-r--r--client/util/vendor/DRACOLoader.js518
-rw-r--r--client/util/vendor/geometryHelper.js106
-rw-r--r--client/util/vendor/oktween.js159
15 files changed, 1343 insertions, 0 deletions
diff --git a/client/splash/cloud/datasetList.js b/client/splash/cloud/datasetList.js
new file mode 100644
index 00000000..88058de7
--- /dev/null
+++ b/client/splash/cloud/datasetList.js
@@ -0,0 +1 @@
+export default ["10K US Adult Faces","3D-RMA","3D Dynamic","3DPeS","4DFAB","50 People One Question","aPascal","Aberdeen","Adience","AFAD","AFEW-VA","AffectNet","AFLW","AFW","AgeDB","ALERT Airport","AM-FED","APiS1.0","AR Face","AWE Ears","B3D(AC)","BBC Pose","BPAD","BFM","BioID Face","BJUT-3D","The Bosphorus","BP4D+","BP4D-Spontanous","Brainwash","BU-3DFE","BUHMAP-DB ","CAFE","Caltech 10K Web Faces","Caltech Faces","Caltech Pedestrians","CAMEL","CAS-PEAL","Casablanca","CASIA Webface","CAVIAR4REID","CelebA","CelebFaces+","CFD","ChaLearn","ChokePoint","Cityscapes","CCP","CMDP","CMU PIE","COCO","COCO-a","COCO QA","COFW","CK","CK+","Columbia Gaze","Ongoing Complex Activities","CUHK01","CUHK02","CUHK03","CVC-01","UFI","D3DFACS","Dartmouth Children","Data61 Pedestrian","DeepFashion","DISFA","Long Distance Heterogeneous Face","Duke MTMC","EmotioNet Database","ETHZ Pedestrian","EuroCity Persons","ExpW","Face Research Lab London","FaceScrub","FaceTracer","SFC","Facebook100","Face Place","Faces94","Faces95","Faces96","FIW","FDDB","FEI","FERET","FER+","CMU FiA","300-W","Florida Inmate","FRAV2D","FRAV3D","GRIMACE","FRGC","Gallagher","Gavab","GeoFaces","Georgia Tech Face","Google Makeup","Google (private)","Graz Pedestrian","H3D","HDA+","Helen","Hi4D-ADSIP","HID","Hipsterwars","HollywoodHeads","HRT Transgender","IFAD","IFDB","IIT Dehli Ear","IJB-A","IJB-B","IJB-C","","iLIDS-VID","Images of Groups","IMDB","IMFDB","IMM Face Dataset","Immediacy","imSitu","INRIA Pedestrian","iQIYI-VID dataset ","JAFFE","Jiku Mobile Video Dataset","JPL-Interaction dataset","Karpathy Instagram","KDEF","UB KinFace","KinectFaceDB","KITTI","LAG","Large Scale Person Search","Leeds Sports Pose","Leeds Sports Pose Extended","LFW","LFW-a","LFWP","m2vts","xm2vtsdb","MAFL","MALF","Mapillary","Market 1501","Market 1203","MARS","McGill Real World","Multiple Encounter Dataset","MegaAge","MegaFace","MIFS","MIKKI dataset","MIT CBCL","CBCL","CBCLSS","MIW","MMI Facial Expression Dataset","Moments in Time","MORPH Commercial","MORPH Non-Commercial","MOT","Large MPI Facial Expression","Small MPI Facial Expression","MPIIGaze","MPII Human Pose","MR2","MRP Drone","MsCeleb","MSMT17","MUCT","MUG Faces","MULTIPIE","MTFL","News Dataset","ND-2006","MID","Novaemötions Dataset","Nude Detection","ORL","Penn Fudan","PETA","PETS 2017","PPB","PIPA","PKU","PKU-Reid","Pornography DB","Precarious","PRID","PRW","PSU","PubFig","pubfig83","Put Face","GRID","QMUL-iLIDS","QMUL-SurvFace","RaFD","RAiD","RAP","ReSEED","SAIVT SoftBio","SAMM","Sarc3D","SCface","SCUT-FBP","SCUT HEAD","SDU-VID","SED Dataset","Sheffield Face","Shinpuhkan 2014","Social Relation","SOTON HiD","SVW","STAIR Action","Stanford Drone","Buffy Stickmen","We Are Family Stickmen","Stickmen PASCAL","Stirling/ESRC 3D Face","SUN","SVS","Texas 3DFRD","TinyFace","Tiny Images","TownCenter","TUD-Brussels","TUD-Campus","TUD-Crossing","TUD-Motionparis","TUD-Multiview","TUD-Pedestrian","TUD-Stadtmitte","TVHI","ND-TWINS-2009-2010","UCCS","UCF101","UCF-CC-50","UCF Selfie","UFDD","UMB","UMD","UNBC-McMaster Pain","Urban Tribes","USED Social Event Dataset","UTKFace","V47","VADANA","CIP","VGG Face","VGG Face2","Violent Flows","VIPeR","Phrasal Recognition","VMU","VOC","VQA","WARD","WGT","WIDER","WIDER FACE","WIDER Attribute","WildTrack","YaleFaces","Yale Face Database B","Extended Yale Face Database B ","YawDD","YFCC100M","UOY 3D Face Database","YouTubeFaces","YMU","YouTube Pose","WLFDB","SAL","Semaine","Belfast naturalistic","Belfast induced","VAM-faces","MAHNOB-HCI","DEAP","AMFED","Recola","AVEC13","AVEC14","Mimicry","Meissner Caucasian and African American","Nottingham Scans","Nottingham Originals","Stirling Pain","Utrecht ECVP","Mooney","Visual Commonsense Reasoning","HUFRD Pilgrims Dataset"]
diff --git a/client/splash/cloud/index.js b/client/splash/cloud/index.js
new file mode 100644
index 00000000..d0a39d8c
--- /dev/null
+++ b/client/splash/cloud/index.js
@@ -0,0 +1,52 @@
+import { Object3D } from 'three'
+
+import TextSprite from 'three.textsprite'
+
+import datasetList from './datasetList'
+import { choice } from '../../util'
+
+import {
+ CLOUD_SCALE,
+ CLOUD_COLORS,
+ CLOUD_ROTATION_SPEED,
+ CLOUD_MAX_COUNT,
+ CLOUD_TEXT_MIN_SIZE,
+ CLOUD_TEXT_MAX_SIZE,
+} from '../constants'
+
+import { scene } from '../renderer'
+
+export const fontFamily = 'Helvetica, Arial, sans-serif'
+
+let cloud = new Object3D()
+
+export function init() {
+ let sprites = Array.from({ length: Math.min(datasetList.length, CLOUD_MAX_COUNT) }, (t, i) => {
+ const sprite = new TextSprite({
+ textSize: CLOUD_TEXT_MIN_SIZE + Math.random() * (CLOUD_TEXT_MAX_SIZE - CLOUD_TEXT_MIN_SIZE),
+ redrawInterval: 1,
+ material: {
+ color: choice(CLOUD_COLORS),
+ },
+ texture: {
+ text: datasetList[i],
+ fontFamily,
+ },
+ })
+ sprite.position
+ .setX(Math.random())
+ .setY(Math.random())
+ .setZ(Math.random())
+ .subScalar(1 / 2)
+ .setLength(1 + Math.random())
+ .multiplyScalar(CLOUD_SCALE)
+ cloud.add(sprite)
+ return sprite
+ })
+ scene.add(cloud)
+ return cloud
+}
+
+export function update() {
+ cloud.rotation.y += CLOUD_ROTATION_SPEED
+}
diff --git a/client/splash/constants.js b/client/splash/constants.js
new file mode 100644
index 00000000..a3724b72
--- /dev/null
+++ b/client/splash/constants.js
@@ -0,0 +1,54 @@
+/* renderer */
+
+export const CAMERA_NEAR = 0.001
+export const CAMERA_FAR = 1000
+
+export const CAMERA_X = 0
+export const CAMERA_Y = 0.2
+export const CAMERA_Z = 2
+
+export const FOG_COLOR = 0x191919
+
+/* tag cloud */
+
+export const CLOUD_COLORS = [
+ 0xffffff,
+ 0xffffff,
+ 0xffffff,
+ 0xdddde6,
+ 0x888896,
+]
+
+export const CLOUD_SCALE = 1
+export const CLOUD_ROTATION_SPEED = 0.001
+export const CLOUD_MAX_COUNT = 80
+export const CLOUD_TEXT_MIN_SIZE = 0.01
+export const CLOUD_TEXT_MAX_SIZE = 0.04
+
+/* face */
+
+export const FACE_SCALE = 1
+
+/* face markers */
+
+export const FACE_MARKERS_SCALE = 0.82
+export const FACE_OFFSET_X = 0
+export const FACE_OFFSET_Y = -0.0295
+export const FACE_OFFSET_Z = 0.0275
+
+export const MARKER_POINT_COLOR = 0xffffff
+export const MARKER_COLORS = [
+ 0xff3333,
+ 0xff8833,
+ 0xffff33,
+ 0x338833,
+ 0x3388ff,
+ 0x3333ff,
+ 0x8833ff,
+ 0xff3388,
+ 0xffffff,
+]
+
+export const POINT_SCALE = 0.01
+export const LINE_THICKNESS = 0.0075
+export const FACE_POINT_COUNT = 68
diff --git a/client/splash/face/faces.js b/client/splash/face/faces.js
new file mode 100644
index 00000000..bab50c97
--- /dev/null
+++ b/client/splash/face/faces.js
@@ -0,0 +1,13 @@
+import { recenter } from './util'
+
+const rawFaces = {
+ putin: [[6.229589182834997700e02, 1.237765302967744901e03, 4.134526977539062500e02], [6.376896424835800872e02, 1.410526376450482758e03, 3.891479492187500000e02], [6.739611274719236462e02, 1.582250497753667105e03, 3.769988098144531250e02], [7.100658831128885140e02, 1.741763669768688942e03, 3.758922424316406250e02], [7.536649862551219030e02, 1.917029460341510003e03, 4.252069091796875000e02], [8.298141803217866936e02, 2.059907416716931493e03, 5.431895141601562500e02], [9.258791576609889944e02, 2.164709160946117208e03, 7.162614746093750000e02], [1.070414319266524672e03, 2.251194729721967633e03, 8.754176635742187500e02], [1.274411328424191879e03, 2.298006267580519307e03, 9.157481689453125000e02], [1.458846686449237723e03, 2.247376503427543867e03, 8.904161376953125000e02], [1.613419451724781766e03, 2.154859059460210574e03, 7.340153198242187500e02], [1.719510602704216581e03, 2.046877580339021051e03, 5.658121948242187500e02], [1.802992956004423377e03, 1.898674323395374358e03, 4.472657165527343750e02], [1.839803416592467329e03, 1.728563973209157211e03, 4.044224853515625000e02], [1.867230816650390807e03, 1.567819041550580550e03, 4.134500732421875000e02], [1.892678734334310093e03, 1.401836273899452635e03, 4.331260375976562500e02], [1.900552931542490114e03, 1.223502216072830606e03, 4.526267700195312500e02], [7.511424780752142851e02, 1.160890923207600963e03, 1.001351135253906250e03], [8.158950379315542705e02, 1.135870041375253777e03, 1.114991333007812500e03], [9.076874701406441091e02, 1.127822704063864421e03, 1.194016601562500000e03], [9.926902658799115216e02, 1.143210580432368261e03, 1.233907104492187500e03], [1.063656488096947669e03, 1.162017750208237658e03, 1.248264404296875000e03], [1.395103497613645231e03, 1.151143294423421366e03, 1.261149414062500000e03], [1.466387226179534309e03, 1.130542678432090725e03, 1.248993530273437500e03], [1.552676099051681376e03, 1.115153013015447868e03, 1.213308837890625000e03], [1.652305108762255031e03, 1.120968661861045803e03, 1.146292236328125000e03], [1.727642721019071814e03, 1.143332434488932449e03, 1.041585449218750000e03], [1.244122246476715645e03, 1.334647129869648325e03, 1.250885375976562500e03], [1.249189824721392597e03, 1.465660963816923413e03, 1.309278930664062500e03], [1.252202780570235745e03, 1.585706690278895167e03, 1.358756103515625000e03], [1.259493648693608066e03, 1.685694304749732282e03, 1.360432617187500000e03], [1.148455034173703552e03, 1.740496228553921810e03, 1.155312866210937500e03], [1.194674198315190097e03, 1.755804119561887546e03, 1.196942993164062500e03], [1.258795422961665054e03, 1.777236518698300188e03, 1.212644042968750000e03], [1.305280558926451249e03, 1.757635707289752190e03, 1.198189453125000000e03], [1.356348142915613380e03, 1.737675595379997958e03, 1.164412109375000000e03], [8.664502087162989028e02, 1.296397628532858562e03, 1.039572875976562500e03], [9.217092343199485640e02, 1.262228697521733466e03, 1.107280639648437500e03], [9.986516724530387137e02, 1.265434423307531233e03, 1.120204345703125000e03], [1.074714246772317438e03, 1.306653943337833425e03, 1.091015014648437500e03], [1.007696155622893571e03, 1.330022539822447698e03, 1.095014892578125000e03], [9.264212392470415125e02, 1.326119930090810840e03, 1.079521362304687500e03], [1.405216489784390433e03, 1.300602885574640140e03, 1.104635620117187500e03], [1.476379755775601325e03, 1.256362955328249654e03, 1.138428955078125000e03], [1.554564041795917774e03, 1.249119944242589781e03, 1.133978515625000000e03], [1.616356373027726704e03, 1.280854080128389342e03, 1.071557495117187500e03], [1.554167071892233480e03, 1.313725106410606259e03, 1.107841308593750000e03], [1.472497819489123913e03, 1.322406412808287541e03, 1.114204223632812500e03], [1.037582353868671362e03, 1.936206664907418599e03, 1.033433837890625000e03], [1.116472667858647128e03, 1.915096195044424576e03, 1.131442626953125000e03], [1.209116239719764735e03, 1.900456612907858926e03, 1.186649047851562500e03], [1.263021651623295838e03, 1.914385048409256797e03, 1.188636718750000000e03], [1.300469609698127215e03, 1.904690494719861135e03, 1.189560302734375000e03], [1.398002848905675364e03, 1.918324532410865231e03, 1.142420776367187500e03], [1.480066685814951143e03, 1.930527828548656316e03, 1.061488159179687500e03], [1.389899255969477508e03, 1.978273845885034234e03, 1.124004760742187500e03], [1.327246293191348741e03, 2.005233211071538562e03, 1.142569091796875000e03], [1.262139949065563542e03, 2.009971803241804992e03, 1.149364868164062500e03], [1.180817821697160070e03, 2.011372329759785316e03, 1.134800781250000000e03], [1.120554480578852463e03, 1.984728334003523742e03, 1.106011474609375000e03], [1.061098198954264262e03, 1.933346772066004633e03, 1.042356811523437500e03], [1.192023425322887761e03, 1.945863449802773175e03, 1.133595458984375000e03], [1.264533596083696921e03, 1.947978800443761884e03, 1.155409667968750000e03], [1.322869685094496617e03, 1.947868674591663421e03, 1.140181640625000000e03], [1.466934774301566449e03, 1.932516156771791202e03, 1.067155761718750000e03], [1.321161541688208445e03, 1.941148711607690529e03, 1.139805419921875000e03], [1.264645510983934400e03, 1.947900976849724657e03, 1.145079345703125000e03], [1.191629635949228032e03, 1.946293020139208465e03, 1.132726318359375000e03]],
+ xi: [[1.581475713243671350e02, 2.509900304577397208e02, 1.024322662353515625e02], [1.602838203130983743e02, 2.899340435970530621e02, 9.691181182861328125e01], [1.677042909659591032e02, 3.286639095949659009e02, 9.189924621582031250e01], [1.748665216932109558e02, 3.625784639964384155e02, 9.149455261230468750e01], [1.847612098245059542e02, 3.980092255955116229e02, 1.006391220092773438e02], [2.038414167516371549e02, 4.305498111620136115e02, 1.265865783691406250e02], [2.267264444687787091e02, 4.546917949123007929e02, 1.604735412597656250e02], [2.612276697794596316e02, 4.780719417916091629e02, 1.935450286865234375e02], [3.090068905699486663e02, 4.907490536080154016e02, 2.012679595947265625e02], [3.518451845056870297e02, 4.797107582361557547e02, 1.910112304687500000e02], [3.847857219621247395e02, 4.564630420879288408e02, 1.558248138427734375e02], [4.103254317339729482e02, 4.305017980957030659e02, 1.197048492431640625e02], [4.318702770756740392e02, 3.973330254169538307e02, 9.420705413818359375e01], [4.422064956365847479e02, 3.622428712253944241e02, 8.656275177001953125e01], [4.490620300891353054e02, 3.271828325578277941e02, 8.970116424560546875e01], [4.560659534529144139e02, 2.910778101423675253e02, 9.644176483154296875e01], [4.601242073208678676e02, 2.548580883071001892e02, 1.051369400024414062e02], [1.962528726615157666e02, 2.332017434931736375e02, 2.350029449462890625e02], [2.121071722591624393e02, 2.262617082603305221e02, 2.581283569335937500e02], [2.337514653224571362e02, 2.251718836885340238e02, 2.734199218750000000e02], [2.531733047485351449e02, 2.294949851660634863e02, 2.820414123535156250e02], [2.699339236988740822e02, 2.343355223233092204e02, 2.848821716308593750e02], [3.455108120787377857e02, 2.358723006125057395e02, 2.856489868164062500e02], [3.613513837627336329e02, 2.322616238283643497e02, 2.837029724121093750e02], [3.815044319900812297e02, 2.292742164881089195e02, 2.767124328613281250e02], [4.047670535218482541e02, 2.327066104784198899e02, 2.606012268066406250e02], [4.217962602802351739e02, 2.402466705142750527e02, 2.363506011962890625e02], [3.089879679361979470e02, 2.760715659167719878e02, 2.821576232910156250e02], [3.093649245318244425e02, 3.046348659859451118e02, 2.941600341796875000e02], [3.114766959994447006e02, 3.293608332435757120e02, 3.046447143554687500e02], [3.122007153080958801e02, 3.512138293816061605e02, 3.057337951660156250e02], [2.828070219152114078e02, 3.584083089312384800e02, 2.589963073730468750e02], [2.940082791197533538e02, 3.623387865373199475e02, 2.662980346679687500e02], [3.092972130868948852e02, 3.668293242331112083e02, 2.692637634277343750e02], [3.198154823452819073e02, 3.628364545754824917e02, 2.664876708984375000e02], [3.327329919553270656e02, 3.587899754064222861e02, 2.584887084960937500e02], [2.224500400618010758e02, 2.660715080829695012e02, 2.413853302001953125e02], [2.361075687184053322e02, 2.595814741366517637e02, 2.553526916503906250e02], [2.531863677380131890e02, 2.604359155512790949e02, 2.575644531250000000e02], [2.701898918002259506e02, 2.695448503202550228e02, 2.497847900390625000e02], [2.550828977098652217e02, 2.744724371337890716e02, 2.524707183837890625e02], [2.360909179089115923e02, 2.732695200422698463e02, 2.497717590332031250e02], [3.453027739281748154e02, 2.714797381053251684e02, 2.498313293457031250e02], [3.623781652113971177e02, 2.630507298607919893e02, 2.577862548828125000e02], [3.801104738721660965e02, 2.626405963194604283e02, 2.570069580078125000e02], [3.942162860945159650e02, 2.698504937983494187e02, 2.416210479736328125e02], [3.801208079020182709e02, 2.762396809117934140e02, 2.497300720214843750e02], [3.607991919424019898e02, 2.768305075970818052e02, 2.518104248046875000e02], [2.491371596093271137e02, 3.952959470980774768e02, 2.243894042968750000e02], [2.708619362027037027e02, 3.908969472967409047e02, 2.487248382568359375e02], [2.956859382180606417e02, 3.884492228908164293e02, 2.622833557128906250e02], [3.090222115310967865e02, 3.917322305836396481e02, 2.625950012207031250e02], [3.181707092883540327e02, 3.897191172401577433e02, 2.624730224609375000e02], [3.430552471086090804e02, 3.928625462670419211e02, 2.471974639892578125e02], [3.613266984528186754e02, 3.978334362673291480e02, 2.215844879150390625e02], [3.386838139552696134e02, 4.127940916293274540e02, 2.407136993408203125e02], [3.237892077277688827e02, 4.194459653487859327e02, 2.482334289550781250e02], [3.079967322256050579e02, 4.202518811513862715e02, 2.512642974853515625e02], [2.868524067299038620e02, 4.193043642051546840e02, 2.499754791259765625e02], [2.717448862412396693e02, 4.119777032710056233e02, 2.416939392089843750e02], [2.543327108644971304e02, 3.948142095348881071e02, 2.252314910888671875e02], [2.901215895409677614e02, 3.978477875473919880e02, 2.505760803222656250e02], [3.087859695135378502e02, 3.988037545716528598e02, 2.547194671630859375e02], [3.235175030876608844e02, 3.988561727606081035e02, 2.500653839111328125e02], [3.580007754456763678e02, 3.984504858996821213e02, 2.228356323242187500e02], [3.221863525989008963e02, 4.030397094247854852e02, 2.482552185058593750e02], [3.081756245332605317e02, 4.048862038526347646e02, 2.513024139404296875e02], [2.894371609257716500e02, 4.031360126091451548e02, 2.486108093261718750e02]],
+ may: [[2.382511024026309201e02, 2.801454149852079922e02, 5.214575576782226562e01], [2.493454499637378774e02, 3.064797704180549545e02, 5.088167190551757812e01], [2.639089957742129400e02, 3.319260479018267915e02, 5.266226196289062500e01], [2.761821218154009330e02, 3.563184045051126532e02, 5.502143859863281250e01], [2.895152865241554991e02, 3.809634247544232721e02, 6.649546051025390625e01], [3.060860955631030720e02, 4.000378012982537257e02, 9.035191345214843750e01], [3.213366342881146238e02, 4.128964293356502822e02, 1.197938156127929688e02], [3.427375466739429157e02, 4.230581409050437287e02, 1.479590148925781250e02], [3.741586932014016043e02, 4.234630673397289229e02, 1.611701660156250000e02], [4.023389876421759936e02, 4.080303395708870084e02, 1.599018707275390625e02], [4.257357296214383950e02, 3.868248218132467855e02, 1.410530548095703125e02], [4.422483188404756334e02, 3.651947661276425379e02, 1.185040130615234375e02], [4.545492390352137022e02, 3.384910384952321465e02, 1.011212463378906250e02], [4.562152171415440876e02, 3.097211315199908768e02, 9.170339202880859375e01], [4.545628575942095608e02, 2.840973517743279331e02, 8.920196533203125000e01], [4.518868203555836089e02, 2.565134695434570631e02, 8.989577484130859375e01], [4.476127057243795662e02, 2.289923642596077116e02, 9.148764801025390625e01], [2.440917306787826817e02, 2.537266974774529444e02, 1.485117950439453125e02], [2.505385673074160877e02, 2.449884134449678754e02, 1.672230224609375000e02], [2.629362676283892029e02, 2.388631448005227753e02, 1.830232696533203125e02], [2.764214070039636226e02, 2.376199662870519660e02, 1.927939453125000000e02], [2.886053881476906895e02, 2.378487686426499863e02, 1.978783874511718750e02], [3.423349636302274348e02, 2.245833317745433533e02, 2.082518310546875000e02], [3.541764831542968750e02, 2.182193829974006576e02, 2.078261871337890625e02], [3.692832874971277306e02, 2.125500047392004035e02, 2.033292694091796875e02], [3.884832659553078429e02, 2.116430788197237121e02, 1.939130706787109375e02], [4.040244427849264639e02, 2.158026554152545202e02, 1.781080627441406250e02], [3.225035565544576457e02, 2.592519715971105825e02, 2.056854705810546875e02], [3.248514710370232024e02, 2.779033468627930006e02, 2.160809936523437500e02], [3.270475357055663608e02, 2.946293671372358176e02, 2.269593200683593750e02], [3.308855010986327443e02, 3.100248983764648756e02, 2.303575286865234375e02], [3.207017386043772831e02, 3.250651369700713076e02, 1.937343902587890625e02], [3.269732386869542324e02, 3.255231738640280810e02, 2.018161010742187500e02], [3.372413344439337948e02, 3.264521555103975743e02, 2.067531127929687500e02], [3.444994117288027837e02, 3.217518894689223998e02, 2.058569641113281250e02], [3.539391475901883837e02, 3.172060759420955947e02, 2.011738739013671875e02], [2.669883459652170927e02, 2.717859535486558116e02, 1.602029724121093750e02], [2.727741014368393166e02, 2.638736764167337014e02, 1.725038604736328125e02], [2.851500773710362182e02, 2.613269290520163395e02, 1.773946533203125000e02], [2.994385292502009861e02, 2.645952199419807584e02, 1.756203308105468750e02], [2.895026475794174416e02, 2.709050594374713228e02, 1.749083251953125000e02], [2.761346585442037735e02, 2.735916131232767157e02, 1.694635925292968750e02], [3.532332682890050251e02, 2.518493531889074291e02, 1.864256134033203125e02], [3.633393915512982630e02, 2.427597525921990211e02, 1.922457427978515625e02], [3.761221859202665314e02, 2.392758025045956174e02, 1.929562835693359375e02], [3.890149852079503034e02, 2.427061907420439297e02, 1.852474365234375000e02], [3.783926052317899575e02, 2.491870305498908920e02, 1.901230163574218750e02], [3.645112599092370829e02, 2.528680367682962356e02, 1.894682617187500000e02], [3.149588652666876669e02, 3.656840930534812060e02, 1.685671386718750000e02], [3.209378256405100274e02, 3.546107831887638326e02, 1.896470184326171875e02], [3.335197990866267901e02, 3.465708392693014730e02, 2.029352569580078125e02], [3.426302308924057343e02, 3.463943742460363637e02, 2.055313262939453125e02], [3.487268890380859148e02, 3.431470490220013971e02, 2.064477233886718750e02], [3.683972360049976942e02, 3.438066943718405355e02, 1.994532775878906250e02], [3.876000764734604331e02, 3.492489317770565549e02, 1.836993255615234375e02], [3.744968906178193038e02, 3.656988448917164760e02, 1.932265167236328125e02], [3.646742408303653065e02, 3.739269323730468955e02, 1.959875488281250000e02], [3.539474262910730431e02, 3.774166064812155810e02, 1.956282958984375000e02], [3.400370959113625418e02, 3.795127504954618871e02, 1.915529479980468750e02], [3.295073663150562311e02, 3.757046441291360566e02, 1.844334564208984375e02], [3.180933715820311818e02, 3.644562714341108176e02, 1.699502258300781250e02], [3.342565650491152951e02, 3.568250763657514426e02, 1.932405548095703125e02], [3.456540888169232062e02, 3.534686874210133283e02, 1.999556121826171875e02], [3.567734405517577443e02, 3.518089711626838607e02, 1.987718353271484375e02], [3.853768515194162774e02, 3.501925692210478473e02, 1.839606781005859375e02], [3.596766138413372573e02, 3.610454082533892688e02, 1.959595794677734375e02], [3.495193201401654051e02, 3.635478616871553754e02, 1.955101776123046875e02], [3.382061392391428853e02, 3.660274766630285512e02, 1.905125427246093750e02]],
+}
+
+export const names = Object.keys(rawFaces)
+
+export const faces = rawFaces
+
+// names.map(name => faces[name] = recenter(rawFaces[name]))
diff --git a/client/splash/face/index.js b/client/splash/face/index.js
new file mode 100644
index 00000000..4090a9fa
--- /dev/null
+++ b/client/splash/face/index.js
@@ -0,0 +1,22 @@
+import { choice } from '../../util'
+import { faces, names } from './faces'
+import * as markers from './markers'
+import * as mesh from './mesh'
+
+export function init() {
+ const name = choice(names)
+ const face = faces[name]
+ markers.build(face)
+ mesh.load(name)
+}
+
+export function load() {
+ const name = choice(names)
+ const face = faces[name]
+ markers.swap(face)
+ // mesh.load(name)
+}
+
+export function update() {
+ // markers.update()
+}
diff --git a/client/splash/face/markers.js b/client/splash/face/markers.js
new file mode 100644
index 00000000..22b9966c
--- /dev/null
+++ b/client/splash/face/markers.js
@@ -0,0 +1,101 @@
+import * as THREE from 'three'
+import { MeshLine, MeshLineMaterial } from 'three.meshline'
+
+import oktween from '../../util/vendor/oktween'
+import { scene } from '../renderer'
+
+import { getLineGeometry, updateFace, lerpPoints, getBboxForPoints, getBboxScaleAndCentroid } from './util'
+
+import {
+ POINT_SCALE, LINE_THICKNESS, FACE_POINT_COUNT, MARKER_COLORS, MARKER_POINT_COLOR,
+ FACE_SCALE, FACE_MARKERS_SCALE, FACE_OFFSET_X, FACE_OFFSET_Y, FACE_OFFSET_Z,
+} from '../constants'
+
+const faceBuffer = Array.from({ length: FACE_POINT_COUNT }, () => new THREE.Vector3())
+
+let group = new THREE.Object3D()
+let cubes
+let meshes
+
+let swapFrom
+let swapTo
+
+export function build(points) {
+ swapTo = points
+
+ const matrix = new THREE.Matrix4()
+ const quaternion = new THREE.Quaternion()
+
+ const bbox = getBboxForPoints(points)
+ let { scale, midX, midY, midZ } = getBboxScaleAndCentroid(bbox)
+
+ scale /= 2
+ scale *= FACE_SCALE * FACE_MARKERS_SCALE
+
+ const boxColor = new THREE.Color()
+ boxColor.setHex(MARKER_POINT_COLOR)
+ const scaledPoints = points.map((p) => new THREE.Vector3(
+ (p[0] - midX) * scale + FACE_OFFSET_X,
+ (p[1] - midY) * scale * -1 + FACE_OFFSET_Y,
+ (p[2] - midZ) * scale + FACE_OFFSET_Z
+ ))
+
+ cubes = scaledPoints.map((position) => {
+ let geometry = new THREE.BoxBufferGeometry()
+ let rotation = new THREE.Euler()
+ let boxScale = new THREE.Vector3()
+ boxScale.x = POINT_SCALE
+ boxScale.y = POINT_SCALE
+ boxScale.z = POINT_SCALE
+ quaternion.setFromEuler(rotation, false)
+ matrix.compose(position, quaternion, boxScale)
+ geometry.applyMatrix(matrix)
+ let material = new THREE.MeshBasicMaterial({ color: boxColor })
+ let cube = new THREE.Mesh(geometry, material)
+ group.add(cube)
+ return cube
+ })
+
+ meshes = getLineGeometry(scaledPoints).map((geometry, i) => {
+ const color = new THREE.Color()
+ const material = new MeshLineMaterial({
+ color: color.setHex(MARKER_COLORS[i % MARKER_COLORS.length]),
+ })
+ const line = new MeshLine()
+ line.setGeometry(geometry, () => LINE_THICKNESS)
+ const mesh = new THREE.Mesh(line.geometry, material)
+ mesh.geometry.dynamic = true
+ mesh.scale.x = 2
+ mesh.scale.y = 2
+ mesh.scale.z = 2
+ group.add(mesh)
+ return [line, mesh]
+ })
+
+ group.frustumCulled = false
+ scene.add(group)
+
+ updateFace(scaledPoints, cubes, meshes)
+}
+
+export function swap(face) {
+ swapFrom = swapTo
+ swapTo = face
+ oktween.add({
+ from: { n: 0 },
+ to: { n: 1 },
+ duration: 1000,
+ easing: oktween.easing.quad_in_out,
+ update: (obj) => {
+ lerpPoints(obj.n, swapFrom, swapTo, faceBuffer)
+ updateFace(faceBuffer, cubes, meshes)
+ },
+ finished: () => {
+ setTimeout(swap, 2000)
+ }
+ })
+}
+
+export function update() {
+ // group.rotation.y += 0.005
+}
diff --git a/client/splash/face/mesh.js b/client/splash/face/mesh.js
new file mode 100644
index 00000000..69a40900
--- /dev/null
+++ b/client/splash/face/mesh.js
@@ -0,0 +1,84 @@
+import { Points, Mesh, MeshBasicMaterial, VertexColors, TrianglesDrawMode } from 'three'
+
+import { scene } from '../renderer'
+
+import DRACOLoader from '../../util/vendor/DRACOLoader'
+import GeometryHelper from '../../util/vendor/geometryHelper'
+
+import { getBboxScaleAndCentroid } from './util'
+
+import { FACE_SCALE } from '../constants'
+
+DRACOLoader.setDecoderPath('/assets/js/vendor/draco/')
+
+const dracoLoader = new DRACOLoader()
+DRACOLoader.getDecoderModule()
+
+export function load(name) {
+ dracoLoader.setVerbosity(1)
+ dracoLoader.setDrawMode(TrianglesDrawMode)
+ dracoLoader.setSkipDequantization('position', true)
+ dracoLoader.load('/assets/data/faces/' + name + '.drc', dracoDidLoad)
+}
+
+export function update(name) {
+ load(name)
+}
+
+function setDequantizationForMaterial(material, bufferGeometry) {
+ material.onBeforeCompile = (shader) => {
+ // Add uniform variables needed for dequantization.
+ const posAttribute = bufferGeometry.attributes.position
+ shader.uniforms.normConstant = { value: posAttribute.maxRange / (1 << posAttribute.numQuantizationBits) }
+ shader.uniforms.minPos = { value: posAttribute.minValues }
+
+ shader.vertexShader = 'uniform float maxRange;\n' +
+ 'uniform float normConstant;\n' +
+ 'uniform vec3 minPos;\n' +
+ shader.vertexShader
+ shader.vertexShader = shader.vertexShader.replace(
+ '#include <begin_vertex>',
+ 'vec3 transformed = minPos + position * normConstant;'
+ )
+ }
+}
+
+function dracoDidLoad(bufferGeometry) {
+ const material = new MeshBasicMaterial({ vertexColors: VertexColors })
+ material.wireframe = true
+ // If the position attribute is quantized, modify the material to perform
+ // dequantization on the GPU.
+ if (bufferGeometry.attributes.position.isQuantized) {
+ setDequantizationForMaterial(material, bufferGeometry)
+ }
+
+ let geometry
+ // Point cloud does not have face indices.
+ if (bufferGeometry.index === null) {
+ geometry = new Points(bufferGeometry, material)
+ } else {
+ if (bufferGeometry.attributes.normal === undefined) {
+ const geometryHelper = new GeometryHelper()
+ geometryHelper.computeVertexNormals(bufferGeometry)
+ }
+ geometry = new Mesh(bufferGeometry, material)
+ geometry.drawMode = dracoLoader.drawMode
+ }
+
+ // Compute range of the geometry coordinates for proper rendering.
+ bufferGeometry.computeBoundingBox()
+ const bbox = bufferGeometry.boundingBox
+ const { scale, midX, midY, midZ } = getBboxScaleAndCentroid(bbox, bufferGeometry.attributes.position)
+ geometry.scale.multiplyScalar(scale * FACE_SCALE)
+ geometry.position.x = -midX * scale
+ geometry.position.y = -midY * scale
+ geometry.position.z = -midZ * scale
+ geometry.frustumCulled = false
+ // geometry.castShadow = true
+ // geometry.receiveShadow = true
+
+ const selectedObject = scene.getObjectByName("my_mesh")
+ scene.remove(selectedObject)
+ geometry.name = "my_mesh"
+ scene.add(geometry)
+}
diff --git a/client/splash/face/util.js b/client/splash/face/util.js
new file mode 100644
index 00000000..38b1e376
--- /dev/null
+++ b/client/splash/face/util.js
@@ -0,0 +1,99 @@
+
+import { Geometry } from 'three'
+import { LINE_THICKNESS } from '../constants'
+
+export function getLineGeometry(points) {
+ return [
+ points.slice(0, 17),
+ points.slice(17, 22),
+ points.slice(22, 27),
+ points.slice(27, 31),
+ points.slice(31, 36),
+ points.slice(36, 42),
+ points.slice(42, 48),
+ points.slice(48)
+ ].map((a, i) => {
+ const geometry = new Geometry()
+ a.forEach(p => geometry.vertices.push(p))
+ if (i > 4) {
+ geometry.vertices.push(a[0])
+ }
+ return geometry
+ })
+}
+
+export function updateFace(buf, cubes, meshes) {
+ updateCubeGeometry(buf, cubes)
+ updateLineGeometry(buf, meshes)
+}
+
+export function updateLineGeometry(points, meshes) {
+ getLineGeometry(points).forEach((geometry, i) => {
+ const [line, mesh] = meshes[i]
+ line.setGeometry(geometry, () => LINE_THICKNESS)
+ mesh.geometry.vertices = line.geometry.vertices
+ mesh.geometry.verticesNeedUpdate = true
+ })
+}
+
+export function updateCubeGeometry(points, cubes) {
+ cubes.forEach((cube, i) => {
+ const p = points[i]
+ cube.position.set(p.x, p.y, p.z)
+ })
+}
+
+export function lerp(n, a, b) {
+ return (b - a) * n + a
+}
+
+export function lerpPoint(n, A, B, C) {
+ C.x = lerp(n, A.x, B.x)
+ C.y = lerp(n, A.y, B.y)
+ C.z = lerp(n, A.z, B.z)
+}
+
+export function lerpPoints(n, A, B, C) {
+ for (let i = 0, len = A.length; i < len; i++) {
+ lerpPoint(n, A[i], B[i], C[i])
+ }
+}
+
+export function getBboxForPoints(points) {
+ return points.reduce((a, p) => {
+ a.min.x = Math.min(a.min.x, p[0])
+ a.max.x = Math.max(a.max.x, p[0])
+ a.min.y = Math.min(a.min.y, p[1])
+ a.max.y = Math.max(a.max.y, p[1])
+ a.min.z = Math.min(a.min.z, p[2])
+ a.max.z = Math.max(a.max.z, p[2])
+ return a
+ }, {
+ min: { x: Infinity, y: Infinity, z: Infinity },
+ max: { x: -Infinity, y: -Infinity, z: -Infinity }
+ })
+}
+
+export function getBboxScaleAndCentroid(bbox, position) {
+ if (position && position.isQuantized) {
+ // If the geometry is quantized, transform the bounding box to the dequantized
+ // coordinates.
+ const normConstant = position.maxRange / (1 << position.numQuantizationBits)
+ const minPos = position.minValues
+ bbox.max.x = minPos[0] + bbox.max.x * normConstant
+ bbox.max.y = minPos[1] + bbox.max.y * normConstant
+ bbox.max.z = minPos[2] + bbox.max.z * normConstant
+ bbox.min.x = minPos[0] + bbox.min.x * normConstant
+ bbox.min.y = minPos[1] + bbox.min.y * normConstant
+ bbox.min.z = minPos[2] + bbox.min.z * normConstant
+ }
+ const sizeX = bbox.max.x - bbox.min.x
+ const sizeY = bbox.max.y - bbox.min.y
+ const sizeZ = bbox.max.z - bbox.min.z
+ const diagonalSize = Math.sqrt(sizeX * sizeX + sizeY * sizeY + sizeZ * sizeZ)
+ const scale = 1.0 / diagonalSize
+ const midX = (bbox.min.x + bbox.max.x) / 2
+ const midY = (bbox.min.y + bbox.max.y) / 2
+ const midZ = (bbox.min.z + bbox.max.z) / 2
+ return { scale, midX, midY, midZ }
+}
diff --git a/client/splash/index.js b/client/splash/index.js
new file mode 100644
index 00000000..b559f5ee
--- /dev/null
+++ b/client/splash/index.js
@@ -0,0 +1,42 @@
+// import oktween from '../util/vendor/oktween'
+
+import { Vector3 } from 'three'
+import OrbitControls from 'three-orbitcontrols'
+
+import { init, render, camera, renderer } from './renderer'
+
+import * as cloud from './cloud'
+import * as face from './face'
+
+const controls = new OrbitControls(camera, renderer.domElement)
+controls.maxDistance = camera.far / 2
+controls.enableDamping = true
+controls.dampingFactor = 1 / 8
+controls.rotateSpeed = 1 / 4
+controls.zoomSpeed = 1
+controls.keyPanSpeed = 1 / 2
+
+function build() {
+ init()
+ cloud.init()
+ face.init()
+ animate()
+}
+
+function animate() {
+ requestAnimationFrame(animate)
+
+ // camera.position.z += -0.0025
+ // camera.rotation.y += 0.00001
+
+ controls.update()
+ cloud.update()
+ face.update()
+
+ let cameraTarget = new Vector3(0, 0, 0)
+ camera.lookAt(cameraTarget)
+
+ render()
+}
+
+document.addEventListener('DOMContentLoaded', build)
diff --git a/client/splash/renderer.js b/client/splash/renderer.js
new file mode 100644
index 00000000..ad111f1a
--- /dev/null
+++ b/client/splash/renderer.js
@@ -0,0 +1,41 @@
+import * as THREE from 'three'
+
+import { FOG_COLOR, CAMERA_NEAR, CAMERA_FAR, CAMERA_X, CAMERA_Y, CAMERA_Z } from './constants'
+
+export const scene = new THREE.Scene()
+scene.fog = new THREE.Fog(FOG_COLOR, 2, 15)
+
+export const camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, CAMERA_NEAR, CAMERA_FAR)
+camera.position.set(CAMERA_X, CAMERA_Y, CAMERA_Z)
+
+export const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true })
+renderer.setClearColor(scene.fog.color)
+renderer.setPixelRatio(window.devicePixelRatio)
+renderer.setSize(window.innerWidth, window.innerHeight)
+
+let lights = [
+ new THREE.DirectionalLight(0xefefff, 1.5),
+ new THREE.DirectionalLight(0xefefff, 1.5),
+]
+lights[0].position.set(1, 1, 1).normalize()
+lights[1].position.set(-1, -1, -1).normalize()
+lights.forEach(light => scene.add(light))
+
+export function init() {
+ const container = document.querySelector('#three_container')
+ container.appendChild(renderer.domElement)
+ window.addEventListener('resize', () => {
+ camera.aspect = window.innerWidth / window.innerHeight
+ camera.updateProjectionMatrix()
+ renderer.setSize(window.innerWidth, window.innerHeight)
+ }, false)
+}
+
+export function render() {
+ renderer.render(scene, camera)
+}
+
+
+window.scene = scene
+window.THREE = THREE
+
diff --git a/client/util.js b/client/util/index.js
index d0db0d98..d0db0d98 100644
--- a/client/util.js
+++ b/client/util/index.js
diff --git a/client/util/math.js b/client/util/math.js
new file mode 100644
index 00000000..064d37c6
--- /dev/null
+++ b/client/util/math.js
@@ -0,0 +1,51 @@
+export const mod = (n,m) => n-(m * Math.floor(n/m))
+export const clamp = (n,a,b) => n<a?a:n<b?n:b
+export const norm = (n,a,b) => (n-a) / (b-a)
+export const lerp = (n,a,b) => (b-a)*n+a
+export const mix = (n,a,b) => a*(1-n)+b*n
+export const randint = (n) => Math.floor(Math.random()*n)
+export const randrange = (a,b) => Math.random() * (b-a) + a
+export const randsign = () => Math.random() >= 0.5 ? -1 : 1
+export const choice = (a) => a[ Math.floor(Math.random() * a.length) ]
+export const angle = (x0,y0,x1,y1) => Math.atan2(y1-y0, x1-x0)
+export const dist = (x0,y0,x1,y1) => Math.sqrt(Math.pow(x1-x0, 2) + Math.pow(y1-y0, 2))
+export const xor = (a,b) => { a=!!a; b=!!b; return (a||b) && !(a&&b) }
+export const quantize = (a,b) => Math.floor(a/b)*b
+export const shuffle = (a) => {
+ for (var i = a.length; i > 0; i--){
+ var r = randint(i)
+ var swap = a[i-1]
+ a[i-1] = a[r]
+ a[r] = swap
+ }
+ return a
+}
+// returns a gaussian random function with the given mean and stdev.
+export function gaussian(mean, stdev) {
+ let y2;
+ let use_last = false;
+ return () => {
+ let y1;
+ if (use_last) {
+ y1 = y2;
+ use_last = false;
+ }
+ else {
+ let x1, x2, w;
+ do {
+ x1 = 2.0 * Math.random() - 1.0;
+ x2 = 2.0 * Math.random() - 1.0;
+ w = x1 * x1 + x2 * x2;
+ } while( w >= 1.0);
+ w = Math.sqrt((-2.0 * Math.log(w))/w);
+ y1 = x1 * w;
+ y2 = x2 * w;
+ use_last = true;
+ }
+
+ let retval = mean + stdev * y1;
+ if (retval > 0)
+ return retval;
+ return -retval;
+ }
+}
diff --git a/client/util/vendor/DRACOLoader.js b/client/util/vendor/DRACOLoader.js
new file mode 100644
index 00000000..67c18dcf
--- /dev/null
+++ b/client/util/vendor/DRACOLoader.js
@@ -0,0 +1,518 @@
+// Copyright 2016 The Draco Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+import * as THREE from 'three';
+
+/**
+ * @param {THREE.LoadingManager} manager
+ */
+const DRACOLoader = function(manager) {
+ this.timeLoaded = 0;
+ this.manager = manager || THREE.DefaultLoadingManager;
+ this.materials = null;
+ this.verbosity = 0;
+ this.attributeOptions = {};
+ this.drawMode = THREE.TrianglesDrawMode;
+ // Native Draco attribute type to Three.JS attribute type.
+ this.nativeAttributeMap = {
+ 'position' : 'POSITION',
+ 'normal' : 'NORMAL',
+ 'color' : 'COLOR',
+ 'uv' : 'TEX_COORD'
+ };
+};
+
+DRACOLoader.prototype = {
+
+ constructor: DRACOLoader,
+
+ load: function(url, onLoad, onProgress, onError) {
+ var scope = this;
+ var loader = new THREE.FileLoader(scope.manager);
+ loader.setPath(this.path);
+ loader.setResponseType('arraybuffer');
+ if (this.crossOrigin !== undefined) {
+ loader.crossOrigin = this.crossOrigin;
+ }
+ loader.load(url, function(blob) {
+ scope.decodeDracoFile(blob, onLoad);
+ }, onProgress, onError);
+ },
+
+ setPath: function(value) {
+ this.path = value;
+ },
+
+ setCrossOrigin: function(value) {
+ this.crossOrigin = value;
+ },
+
+ setVerbosity: function(level) {
+ this.verbosity = level;
+ },
+
+ /**
+ * Sets desired mode for generated geometry indices.
+ * Can be either:
+ * THREE.TrianglesDrawMode
+ * THREE.TriangleStripDrawMode
+ */
+ setDrawMode: function(drawMode) {
+ this.drawMode = drawMode;
+ },
+
+ /**
+ * Skips dequantization for a specific attribute.
+ * |attributeName| is the THREE.js name of the given attribute type.
+ * The only currently supported |attributeName| is 'position', more may be
+ * added in future.
+ */
+ setSkipDequantization: function(attributeName, skip) {
+ var skipDequantization = true;
+ if (typeof skip !== 'undefined')
+ skipDequantization = skip;
+ this.getAttributeOptions(attributeName).skipDequantization =
+ skipDequantization;
+ },
+
+ /**
+ * |attributeUniqueIdMap| specifies attribute unique id for an attribute in
+ * the geometry to be decoded. The name of the attribute must be one of the
+ * supported attribute type in Three.JS, including:
+ * 'position',
+ * 'color',
+ * 'normal',
+ * 'uv',
+ * 'uv2',
+ * 'skinIndex',
+ * 'skinWeight'.
+ * The format is:
+ * attributeUniqueIdMap[attributeName] = attributeId
+ */
+ decodeDracoFile: function(rawBuffer, callback, attributeUniqueIdMap,
+ attributeTypeMap) {
+ var scope = this;
+ DRACOLoader.getDecoderModule()
+ .then( function ( module ) {
+ scope.decodeDracoFileInternal( rawBuffer, module.decoder, callback,
+ attributeUniqueIdMap || {}, attributeTypeMap || {});
+ });
+ },
+
+ decodeDracoFileInternal: function(rawBuffer, dracoDecoder, callback,
+ attributeUniqueIdMap, attributeTypeMap) {
+ /*
+ * Here is how to use Draco Javascript decoder and get the geometry.
+ */
+ var buffer = new dracoDecoder.DecoderBuffer();
+ buffer.Init(new Int8Array(rawBuffer), rawBuffer.byteLength);
+ var decoder = new dracoDecoder.Decoder();
+
+ /*
+ * Determine what type is this file: mesh or point cloud.
+ */
+ var geometryType = decoder.GetEncodedGeometryType(buffer);
+ if (geometryType == dracoDecoder.TRIANGULAR_MESH) {
+ if (this.verbosity > 0) {
+ console.log('Loaded a mesh.');
+ }
+ } else if (geometryType == dracoDecoder.POINT_CLOUD) {
+ if (this.verbosity > 0) {
+ console.log('Loaded a point cloud.');
+ }
+ } else {
+ var errorMsg = 'THREE.DRACOLoader: Unknown geometry type.'
+ console.error(errorMsg);
+ throw new Error(errorMsg);
+ }
+ callback(this.convertDracoGeometryTo3JS(dracoDecoder, decoder,
+ geometryType, buffer, attributeUniqueIdMap, attributeTypeMap));
+ },
+
+ addAttributeToGeometry: function(dracoDecoder, decoder, dracoGeometry,
+ attributeName, attributeType, attribute,
+ geometry, geometryBuffer) {
+ if (attribute.ptr === 0) {
+ var errorMsg = 'THREE.DRACOLoader: No attribute ' + attributeName;
+ console.error(errorMsg);
+ throw new Error(errorMsg);
+ }
+
+ var numComponents = attribute.num_components();
+ var numPoints = dracoGeometry.num_points();
+ var numValues = numPoints * numComponents;
+ var attributeData;
+ var TypedBufferAttribute;
+
+ switch ( attributeType ) {
+
+ case Float32Array:
+ attributeData = new dracoDecoder.DracoFloat32Array();
+ decoder.GetAttributeFloatForAllPoints(
+ dracoGeometry, attribute, attributeData);
+ geometryBuffer[ attributeName ] = new Float32Array( numValues );
+ TypedBufferAttribute = THREE.Float32BufferAttribute;
+ break;
+
+ case Int8Array:
+ attributeData = new dracoDecoder.DracoInt8Array();
+ decoder.GetAttributeInt8ForAllPoints(
+ dracoGeometry, attribute, attributeData );
+ geometryBuffer[ attributeName ] = new Int8Array( numValues );
+ TypedBufferAttribute = THREE.Int8BufferAttribute;
+ break;
+
+ case Int16Array:
+ attributeData = new dracoDecoder.DracoInt16Array();
+ decoder.GetAttributeInt16ForAllPoints(
+ dracoGeometry, attribute, attributeData);
+ geometryBuffer[ attributeName ] = new Int16Array( numValues );
+ TypedBufferAttribute = THREE.Int16BufferAttribute;
+ break;
+
+ case Int32Array:
+ attributeData = new dracoDecoder.DracoInt32Array();
+ decoder.GetAttributeInt32ForAllPoints(
+ dracoGeometry, attribute, attributeData);
+ geometryBuffer[ attributeName ] = new Int32Array( numValues );
+ TypedBufferAttribute = THREE.Int32BufferAttribute;
+ break;
+
+ case Uint8Array:
+ attributeData = new dracoDecoder.DracoUInt8Array();
+ decoder.GetAttributeUInt8ForAllPoints(
+ dracoGeometry, attribute, attributeData);
+ geometryBuffer[ attributeName ] = new Uint8Array( numValues );
+ TypedBufferAttribute = THREE.Uint8BufferAttribute;
+ break;
+
+ case Uint16Array:
+ attributeData = new dracoDecoder.DracoUInt16Array();
+ decoder.GetAttributeUInt16ForAllPoints(
+ dracoGeometry, attribute, attributeData);
+ geometryBuffer[ attributeName ] = new Uint16Array( numValues );
+ TypedBufferAttribute = THREE.Uint16BufferAttribute;
+ break;
+
+ case Uint32Array:
+ attributeData = new dracoDecoder.DracoUInt32Array();
+ decoder.GetAttributeUInt32ForAllPoints(
+ dracoGeometry, attribute, attributeData);
+ geometryBuffer[ attributeName ] = new Uint32Array( numValues );
+ TypedBufferAttribute = THREE.Uint32BufferAttribute;
+ break;
+
+ default:
+ var errorMsg = 'THREE.DRACOLoader: Unexpected attribute type.';
+ console.error( errorMsg );
+ throw new Error( errorMsg );
+
+ }
+
+ // Copy data from decoder.
+ for (var i = 0; i < numValues; i++) {
+ geometryBuffer[attributeName][i] = attributeData.GetValue(i);
+ }
+ // Add attribute to THREEJS geometry for rendering.
+ geometry.addAttribute(attributeName,
+ new TypedBufferAttribute(geometryBuffer[attributeName],
+ numComponents));
+ dracoDecoder.destroy(attributeData);
+ },
+
+ convertDracoGeometryTo3JS: function(dracoDecoder, decoder, geometryType,
+ buffer, attributeUniqueIdMap,
+ attributeTypeMap) {
+ if (this.getAttributeOptions('position').skipDequantization === true) {
+ decoder.SkipAttributeTransform(dracoDecoder.POSITION);
+ }
+ var dracoGeometry;
+ var decodingStatus;
+ const start_time = performance.now();
+ if (geometryType === dracoDecoder.TRIANGULAR_MESH) {
+ dracoGeometry = new dracoDecoder.Mesh();
+ decodingStatus = decoder.DecodeBufferToMesh(buffer, dracoGeometry);
+ } else {
+ dracoGeometry = new dracoDecoder.PointCloud();
+ decodingStatus =
+ decoder.DecodeBufferToPointCloud(buffer, dracoGeometry);
+ }
+ if (!decodingStatus.ok() || dracoGeometry.ptr == 0) {
+ var errorMsg = 'THREE.DRACOLoader: Decoding failed: ';
+ errorMsg += decodingStatus.error_msg();
+ console.error(errorMsg);
+ dracoDecoder.destroy(decoder);
+ dracoDecoder.destroy(dracoGeometry);
+ throw new Error(errorMsg);
+ }
+
+ var decode_end = performance.now();
+ dracoDecoder.destroy(buffer);
+ /*
+ * Example on how to retrieve mesh and attributes.
+ */
+ var numFaces;
+ if (geometryType == dracoDecoder.TRIANGULAR_MESH) {
+ numFaces = dracoGeometry.num_faces();
+ if (this.verbosity > 0) {
+ console.log('Number of faces loaded: ' + numFaces.toString());
+ }
+ } else {
+ numFaces = 0;
+ }
+
+ var numPoints = dracoGeometry.num_points();
+ var numAttributes = dracoGeometry.num_attributes();
+ if (this.verbosity > 0) {
+ console.log('Number of points loaded: ' + numPoints.toString());
+ console.log('Number of attributes loaded: ' +
+ numAttributes.toString());
+ }
+
+ // Verify if there is position attribute.
+ var posAttId = decoder.GetAttributeId(dracoGeometry,
+ dracoDecoder.POSITION);
+ if (posAttId == -1) {
+ var errorMsg = 'THREE.DRACOLoader: No position attribute found.';
+ console.error(errorMsg);
+ dracoDecoder.destroy(decoder);
+ dracoDecoder.destroy(dracoGeometry);
+ throw new Error(errorMsg);
+ }
+ var posAttribute = decoder.GetAttribute(dracoGeometry, posAttId);
+
+ // Structure for converting to THREEJS geometry later.
+ var geometryBuffer = {};
+ // Import data to Three JS geometry.
+ var geometry = new THREE.BufferGeometry();
+
+ // Add native Draco attribute type to geometry.
+ for (var attributeName in this.nativeAttributeMap) {
+ // The native attribute type is only used when no unique Id is
+ // provided. For example, loading .drc files.
+ if (attributeUniqueIdMap[attributeName] === undefined) {
+ var attId = decoder.GetAttributeId(dracoGeometry,
+ dracoDecoder[this.nativeAttributeMap[attributeName]]);
+ if (attId !== -1) {
+ if (this.verbosity > 0) {
+ console.log('Loaded ' + attributeName + ' attribute.');
+ }
+ var attribute = decoder.GetAttribute(dracoGeometry, attId);
+ this.addAttributeToGeometry(dracoDecoder, decoder, dracoGeometry,
+ attributeName, Float32Array, attribute, geometry, geometryBuffer);
+ }
+ }
+ }
+
+ // Add attributes of user specified unique id. E.g. GLTF models.
+ for (var attributeName in attributeUniqueIdMap) {
+ var attributeType = attributeTypeMap[attributeName] || Float32Array;
+ var attributeId = attributeUniqueIdMap[attributeName];
+ var attribute = decoder.GetAttributeByUniqueId(dracoGeometry,
+ attributeId);
+ this.addAttributeToGeometry(dracoDecoder, decoder, dracoGeometry,
+ attributeName, attributeType, attribute, geometry, geometryBuffer);
+ }
+
+ // For mesh, we need to generate the faces.
+ if (geometryType == dracoDecoder.TRIANGULAR_MESH) {
+ if (this.drawMode === THREE.TriangleStripDrawMode) {
+ var stripsArray = new dracoDecoder.DracoInt32Array();
+ var numStrips = decoder.GetTriangleStripsFromMesh(
+ dracoGeometry, stripsArray);
+ geometryBuffer.indices = new Uint32Array(stripsArray.size());
+ for (var i = 0; i < stripsArray.size(); ++i) {
+ geometryBuffer.indices[i] = stripsArray.GetValue(i);
+ }
+ dracoDecoder.destroy(stripsArray);
+ } else {
+ var numIndices = numFaces * 3;
+ geometryBuffer.indices = new Uint32Array(numIndices);
+ var ia = new dracoDecoder.DracoInt32Array();
+ for (var i = 0; i < numFaces; ++i) {
+ decoder.GetFaceFromMesh(dracoGeometry, i, ia);
+ var index = i * 3;
+ geometryBuffer.indices[index] = ia.GetValue(0);
+ geometryBuffer.indices[index + 1] = ia.GetValue(1);
+ geometryBuffer.indices[index + 2] = ia.GetValue(2);
+ }
+ dracoDecoder.destroy(ia);
+ }
+ }
+
+ geometry.drawMode = this.drawMode;
+ if (geometryType == dracoDecoder.TRIANGULAR_MESH) {
+ geometry.setIndex(new(geometryBuffer.indices.length > 65535 ?
+ THREE.Uint32BufferAttribute : THREE.Uint16BufferAttribute)
+ (geometryBuffer.indices, 1));
+ }
+ var posTransform = new dracoDecoder.AttributeQuantizationTransform();
+ if (posTransform.InitFromAttribute(posAttribute)) {
+ // Quantized attribute. Store the quantization parameters into the
+ // THREE.js attribute.
+ geometry.attributes['position'].isQuantized = true;
+ geometry.attributes['position'].maxRange = posTransform.range();
+ geometry.attributes['position'].numQuantizationBits =
+ posTransform.quantization_bits();
+ geometry.attributes['position'].minValues = new Float32Array(3);
+ for (var i = 0; i < 3; ++i) {
+ geometry.attributes['position'].minValues[i] =
+ posTransform.min_value(i);
+ }
+ }
+ dracoDecoder.destroy(posTransform);
+ dracoDecoder.destroy(decoder);
+ dracoDecoder.destroy(dracoGeometry);
+
+ this.decode_time = decode_end - start_time;
+ this.import_time = performance.now() - decode_end;
+
+ if (this.verbosity > 0) {
+ console.log('Decode time: ' + this.decode_time);
+ console.log('Import time: ' + this.import_time);
+ }
+ return geometry;
+ },
+
+ isVersionSupported: function(version, callback) {
+ DRACOLoader.getDecoderModule()
+ .then( function ( module ) {
+ callback( module.decoder.isVersionSupported( version ) );
+ });
+ },
+
+ getAttributeOptions: function(attributeName) {
+ if (typeof this.attributeOptions[attributeName] === 'undefined')
+ this.attributeOptions[attributeName] = {};
+ return this.attributeOptions[attributeName];
+ }
+};
+
+DRACOLoader.decoderPath = './';
+DRACOLoader.decoderConfig = {};
+DRACOLoader.decoderModulePromise = null;
+
+/**
+ * Sets the base path for decoder source files.
+ * @param {string} path
+ */
+DRACOLoader.setDecoderPath = function ( path ) {
+ DRACOLoader.decoderPath = path;
+};
+
+/**
+ * Sets decoder configuration and releases singleton decoder module. Module
+ * will be recreated with the next decoding call.
+ * @param {Object} config
+ */
+DRACOLoader.setDecoderConfig = function ( config ) {
+ var wasmBinary = DRACOLoader.decoderConfig.wasmBinary;
+ DRACOLoader.decoderConfig = config || {};
+ DRACOLoader.releaseDecoderModule();
+
+ // Reuse WASM binary.
+ if ( wasmBinary ) DRACOLoader.decoderConfig.wasmBinary = wasmBinary;
+};
+
+/**
+ * Releases the singleton DracoDecoderModule instance. Module will be recreated
+ * with the next decoding call.
+ */
+DRACOLoader.releaseDecoderModule = function () {
+ DRACOLoader.decoderModulePromise = null;
+};
+
+/**
+ * Gets WebAssembly or asm.js singleton instance of DracoDecoderModule
+ * after testing for browser support. Returns Promise that resolves when
+ * module is available.
+ * @return {Promise<{decoder: DracoDecoderModule}>}
+ */
+DRACOLoader.getDecoderModule = function () {
+ var scope = this;
+ var path = DRACOLoader.decoderPath;
+ var config = DRACOLoader.decoderConfig;
+ var promise = DRACOLoader.decoderModulePromise;
+
+ if ( promise ) return promise;
+
+ // Load source files.
+ if ( typeof DracoDecoderModule !== 'undefined' ) {
+ // Loaded externally.
+ promise = Promise.resolve();
+ } else if ( typeof WebAssembly !== 'object' || config.type === 'js' ) {
+ // Load with asm.js.
+ promise = DRACOLoader._loadScript( path + 'draco_decoder.js' );
+ } else {
+ // Load with WebAssembly.
+ config.wasmBinaryFile = path + 'draco_decoder.wasm';
+ promise = DRACOLoader._loadScript( path + 'draco_wasm_wrapper.js' )
+ .then( function () {
+ return DRACOLoader._loadArrayBuffer( config.wasmBinaryFile );
+ } )
+ .then( function ( wasmBinary ) {
+ config.wasmBinary = wasmBinary;
+ } );
+ }
+
+ // Wait for source files, then create and return a decoder.
+ promise = promise.then( function () {
+ return new Promise( function ( resolve ) {
+ config.onModuleLoaded = function ( decoder ) {
+ scope.timeLoaded = performance.now();
+ // Module is Promise-like. Wrap before resolving to avoid loop.
+ resolve( { decoder: decoder } );
+ };
+ DracoDecoderModule( config );
+ } );
+ } );
+
+ DRACOLoader.decoderModulePromise = promise;
+ return promise;
+};
+
+/**
+ * @param {string} src
+ * @return {Promise}
+ */
+DRACOLoader._loadScript = function ( src ) {
+ var prevScript = document.getElementById( 'decoder_script' );
+ if ( prevScript !== null ) {
+ prevScript.parentNode.removeChild( prevScript );
+ }
+ var head = document.getElementsByTagName( 'head' )[ 0 ];
+ var script = document.createElement( 'script' );
+ script.id = 'decoder_script';
+ script.type = 'text/javascript';
+ script.src = src;
+ return new Promise( function ( resolve ) {
+ script.onload = resolve;
+ head.appendChild( script );
+ });
+};
+
+/**
+ * @param {string} src
+ * @return {Promise}
+ */
+DRACOLoader._loadArrayBuffer = function ( src ) {
+ var loader = new THREE.FileLoader();
+ loader.setResponseType( 'arraybuffer' );
+ return new Promise( function( resolve, reject ) {
+ loader.load( src, resolve, undefined, reject );
+ });
+};
+
+export default DRACOLoader
diff --git a/client/util/vendor/geometryHelper.js b/client/util/vendor/geometryHelper.js
new file mode 100644
index 00000000..7f699722
--- /dev/null
+++ b/client/util/vendor/geometryHelper.js
@@ -0,0 +1,106 @@
+// Copyright 2017 The Draco Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+import * as THREE from 'three';
+
+/**
+ * @fileoverview Helper class implementing various utilities for THREE.js
+ * geometry.
+ */
+
+function GeometryHelper() {}
+
+GeometryHelper.prototype = {
+ constructor: GeometryHelper,
+
+ // Computes vertex normals on THREE.js buffer geometry, even when the mesh
+ // uses triangle strip connectivity.
+ computeVertexNormals: function (bufferGeometry) {
+ if (bufferGeometry.drawMode === THREE.TrianglesDrawMode) {
+ bufferGeometry.computeVertexNormals();
+ return;
+ } else if (bufferGeometry.drawMode === THREE.TriangleStripDrawMode) {
+ if (bufferGeometry.attributes.position === undefined) {
+ return;
+ }
+ const inPositions = bufferGeometry.attributes.position.array;
+ if (bufferGeometry.attributes.normal === undefined) {
+ bufferGeometry.addAttribute(
+ 'normal',
+ new THREE.BufferAttribute(new Float32Array(inPositions.length),
+ 3));
+ } else {
+ // Reset existing normals to zero.
+ const array = bufferGeometry.attributes.normal.array;
+ for (let i = 0; i < array.length; ++i) {
+ array[ i ] = 0;
+ }
+ }
+ let outNormals = bufferGeometry.attributes.normal.array;
+
+ let pos0 = new THREE.Vector3();
+ let pos1 = new THREE.Vector3();
+ let pos2 = new THREE.Vector3();
+ let posDif0 = new THREE.Vector3(), posDif1 = new THREE.Vector3();
+ let localNormal = new THREE.Vector3();
+
+ const stripIndices = bufferGeometry.index.array;
+ for (let i = 2; i < stripIndices.length; ++i) {
+ let index0 = stripIndices[i - 2] * 3;
+ let index1 = stripIndices[i - 1] * 3;
+ let index2 = stripIndices[i] * 3;
+ // Skip degenerate triangles.
+ if (index0 === index1 || index0 === index2 || index1 === index2) {
+ continue;
+ }
+ if ((i & 1) !== 0) {
+ // Swap index 1 and 0 on odd indexed triangles.
+ const tmpIndex = index1;
+ index1 = index2;
+ index2 = tmpIndex;
+ }
+
+ // Get position values.
+ pos0.fromArray(inPositions, index0);
+ pos1.fromArray(inPositions, index1);
+ pos2.fromArray(inPositions, index2);
+
+ // Position differences
+ posDif0.subVectors(pos2, pos0);
+ posDif1.subVectors(pos1, pos0);
+
+ // Weighted normal.
+ localNormal.crossVectors(posDif1, posDif0);
+
+ // Update normals on vertices
+ outNormals[index0] += localNormal.x;
+ outNormals[index0 + 1] += localNormal.y;
+ outNormals[index0 + 2] += localNormal.z;
+
+ outNormals[index1] += localNormal.x;
+ outNormals[index1 + 1] += localNormal.y;
+ outNormals[index1 + 2] += localNormal.z;
+
+ outNormals[index2] += localNormal.x;
+ outNormals[index2 + 1] += localNormal.y;
+ outNormals[index2 + 2] += localNormal.z;
+ }
+ bufferGeometry.normalizeNormals();
+ bufferGeometry.attributes.normal.needsUpdate = true;
+ }
+ },
+};
+
+export default GeometryHelper
diff --git a/client/util/vendor/oktween.js b/client/util/vendor/oktween.js
new file mode 100644
index 00000000..cbf5d835
--- /dev/null
+++ b/client/util/vendor/oktween.js
@@ -0,0 +1,159 @@
+/*
+ oktween.add({
+ obj: el.style,
+ units: "px",
+ from: { left: 0 },
+ to: { left: 100 },
+ duration: 1000,
+ easing: oktween.easing.circ_out,
+ update: function(obj){
+ console.log(obj.left)
+ }
+ finished: function(){
+ console.log("done")
+ }
+ })
+*/
+
+import { lerp } from '../../util/math'
+
+const oktween = {}
+let tweens = []
+
+let last_t = 0
+let id = 0
+
+oktween.speed = 1
+oktween.add = (tween) => {
+ tween.id = id++
+ tween.obj = tween.obj || {}
+ if (tween.easing) {
+ if (typeof tween.easing === "string") {
+ tween.easing = oktween.easing[tween.easing]
+ }
+ } else {
+ tween.easing = oktween.easing.linear
+ }
+ if (!('from' in tween) && !('to' in tween)) {
+ tween.keys = []
+ } else if (!('from' in tween)) {
+ tween.from = {}
+ tween.keys = Object.keys(tween.to)
+ tween.keys.forEach(function(prop) {
+ tween.from[prop] = parseFloat(tween.obj[prop])
+ })
+ } else {
+ tween.keys = Object.keys(tween.from)
+ }
+ tween.delay = tween.delay || 0
+ tween.start = last_t + tween.delay
+ tween.done = false
+ tween.after = tween.after || []
+ tween.then = (fn) => { tween.after.push(fn); return tween }
+ tween.tick = 0
+ tween.skip = tween.skip || 1
+ tween.dt = 0
+ tweens.push(tween)
+ return tween
+}
+oktween.update = (t) => {
+ let done = false
+ requestAnimationFrame(oktween.update)
+ last_t = t * oktween.speed
+ if (tweens.length === 0) return
+ tweens.forEach((tween, i) => {
+ const dt = Math.min(1.0, (t - tween.start) / tween.duration)
+ tween.tick++
+ if (dt < 0 || (dt < 1 && (tween.tick % tween.skip != 0))) return
+ const ddt = tween.easing(dt)
+ tween.dt = ddt
+ tween.keys.forEach((prop) => {
+ let val = lerp(ddt, tween.from[prop], tween.to[prop])
+ if (tween.round) val = Math.round(val)
+ if (tween.units) val = (Math.round(val)) + tween.units
+ tween.obj[prop] = val
+ })
+ tween.update && tween.update(tween.obj, dt)
+ if (dt == 1) {
+ tween.finished && tween.finished(tween)
+ if (tween.after.length) {
+ var twn = tween.after.shift()
+ twn.obj = twn.obj || tween.obj
+ twn.after = tween.after
+ oktween.add(twn)
+ }
+ if (tween.loop) {
+ tween.start = t + tween.delay
+ }
+ else {
+ done = true
+ tween.done = true
+ }
+ }
+ })
+ if (done) {
+ tweens = tweens.filter(function(tween){ return ! tween.done })
+ }
+}
+
+requestAnimationFrame(oktween.update)
+
+oktween.easing = {
+ linear: (t) => {
+ return t
+ },
+ circ_out: (t) => {
+ return Math.sqrt(1 - (t = t - 1) * t)
+ },
+ circ_in: (t) => {
+ return -(Math.sqrt(1 - (t * t)) - 1)
+ },
+ circ_in_out: (t) => {
+ return ((t*=2) < 1) ? -0.5 * (Math.sqrt(1 - t * t) - 1) : 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1)
+ },
+ quad_in: (n) => {
+ return Math.pow(n, 2)
+ },
+ quad_out: (n) => {
+ return n * (n - 2) * -1
+ },
+ quad_in_out: (n) => {
+ n = n * 2
+ if (n < 1) { return Math.pow(n, 2) / 2 }
+ return -1 * ((--n) * (n - 2) - 1) / 2
+ },
+ cubic_bezier: (mX1, mY1, mX2, mY2) => {
+ function A(aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1 }
+ function B(aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1 }
+ function C(aA1) { return 3.0 * aA1 }
+
+ // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
+ function CalcBezier(aT, aA1, aA2) {
+ return ((A(aA1, aA2)*aT + B(aA1, aA2))*aT + C(aA1))*aT
+ }
+
+ // Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2.
+ function GetSlope(aT, aA1, aA2) {
+ return 3.0 * A(aA1, aA2)*aT*aT + 2.0 * B(aA1, aA2) * aT + C(aA1)
+ }
+
+ function GetTForX(aX) {
+ // Newton raphson iteration
+ let aGuessT = aX
+ for (let i = 0; i < 10; ++i) {
+ const currentSlope = GetSlope(aGuessT, mX1, mX2)
+ if (currentSlope == 0.0) return aGuessT
+ const currentX = CalcBezier(aGuessT, mX1, mX2) - aX
+ aGuessT -= currentX / currentSlope
+ }
+ return aGuessT
+ }
+
+ return function (aX) {
+ if (mX1 == mY1 && mX2 == mY2) return aX // linear
+ return CalcBezier(aX, mY1, mY2)
+ }
+ }
+}
+
+export default oktween