├── .gitignore
├── .idea
├── encodings.xml
├── misc.xml
├── modules.xml
├── runConfigurations.xml
└── vcs.xml
├── LICENSE
├── README.md
├── Visualization
├── ARCore_point_cloud.txt
├── ARCore_sensor_pose.txt
├── FigureRotator.m
├── angle2rotmtx.m
├── main_script.m
├── plot_inertial_frame.m
├── plot_sensor_ARCore_frame.m
├── q2r.m
├── r2q.m
├── rotmtx2angle.m
└── test_codes.m
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── pjinkim
│ │ └── arcore_data_logger
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── ic_launcher-web.png
│ ├── java
│ │ └── com
│ │ │ └── pjinkim
│ │ │ └── arcore_data_logger
│ │ │ ├── ARCoreSession.java
│ │ │ ├── AccumulatedPointCloud.java
│ │ │ ├── FileStreamer.java
│ │ │ ├── MainActivity.java
│ │ │ ├── OutputDirectoryManager.java
│ │ │ ├── PointCloudNode.java
│ │ │ └── WorldToScreenTranslator.java
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ ├── gruvi_logo.png
│ │ ├── ic_launcher_background.xml
│ │ └── sfu_logo.png
│ │ ├── font
│ │ ├── roboto.xml
│ │ └── roboto_black.xml
│ │ ├── layout
│ │ └── activity_main.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── font_certs.xml
│ │ ├── preloaded_fonts.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── pjinkim
│ └── arcore_data_logger
│ └── ExampleUnitTest.java
├── build.gradle
├── data_visualization.png
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── screenshot.png
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the ART/Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Text files
12 |
13 | # Generated files
14 | bin/
15 | gen/
16 | out/
17 |
18 | # Gradle files
19 | .gradle/
20 | build/
21 |
22 | # Local configuration file (sdk path, etc)
23 | local.properties
24 |
25 | # Proguard folder generated by Eclipse
26 | proguard/
27 |
28 | # Log Files
29 | *.log
30 |
31 | # Android Studio Navigation editor temp files
32 | .navigation/
33 |
34 | # Android Studio captures folder
35 | captures/
36 |
37 | # IntelliJ
38 | *.iml
39 | .idea/workspace.xml
40 | .idea/tasks.xml
41 | .idea/gradle.xml
42 | .idea/assetWizardSettings.xml
43 | .idea/dictionaries
44 | .idea/libraries
45 | .idea/caches
46 |
47 | # Keystore files
48 | # Uncomment the following line if you do not want to check your keystore files in.
49 | #*.jks
50 |
51 | # External native build folder generated in Android Studio 2.2 and later
52 | .externalNativeBuild
53 |
54 | # Google Services (e.g. APIs or Firebase)
55 | google-services.json
56 |
57 | # Freeline
58 | freeline.py
59 | freeline/
60 | freeline_project_description.json
61 |
62 | # fastlane
63 | fastlane/report.xml
64 | fastlane/Preview.html
65 | fastlane/screenshots
66 | fastlane/test_output
67 | fastlane/readme.md
68 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Pyojin Kim
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ARCore Data Logger #
2 |
3 | This is a simple application to capture ARCore motion estimation (Visual-Inertial Odometry) results on Android devices for offline use.
4 | I want to play around with data from Google's Visual-Inertial Odometry (VIO) solution with ARCore framework API in Android Studio 3.4.2, API level 28 for Android devices.
5 |
6 | 
7 |
8 | For more details, see the ARCore documentation [here](https://developers.google.com/ar/reference/java/arcore/reference/com/google/ar/core/Frame).
9 |
10 |
11 | ## Usage Notes ##
12 |
13 | The txt files are produced automatically after pressing Stop button.
14 | This project is written Java under Android Studio Version 3.4.2 for Android 9.0 (API level 28) tested with Google Pixel 2 XL.
15 | It doesn't currently check for sensor availability before logging.
16 |
17 |
18 | ## Reference Frames and Device Attitude ##
19 |
20 | In the world (reference) coordinate space in ARCore, the Y-axis (up) always has +Y pointing up relative to gravity direction.
21 | For the Z-axis, ARCore chooses a basis vector (0,0,-1) pointing in the direction the device camera faces and perpendicular to the gravity axis.
22 | ARCore chooses a X-axis based on the Z- and Y-axes using the right hand rule.
23 | When a device is held in its default (portrait) orientation, the ARCore Android sensor frame's X-axis is horizontal and points to the right, the Y-axis is vertical and points up, and the Z-axis points toward the outside of the screen face.
24 | In this system, coordinates behind the screen have negative Z values.
25 |
26 |
27 | ## Output Format ##
28 |
29 | I have chosen the following output formats, but they are easy to modify if you find something else more convenient.
30 |
31 | * ARCore 6-DoF Sensor Pose (ARCore_sensor_pose.txt): `timestamp, q_x, q_y, q_z, q_w, t_x, t_y, t_z \n`
32 | * ARCore 3D Point Cloud (ARCore_point_cloud.txt): `position_x, position_y, position_z, color_R, color_G, color_B \n`
33 |
34 | Note that ARCore_sensor_pose.txt contains a N x 8 table, where N is the number of frames of this sequence.
35 | Row i represents the i'th pose of the [Android Sensor Coordinate System](https://developer.android.com/guide/topics/sensors/sensors_overview#sensors-coords) in the world coordinate space for this frame.
36 |
37 |
38 | ## Offline MATLAB Visualization ##
39 |
40 | The ability to experiment with different algorithms to process the ARCore (VIO) motion estimation results is the reason that I created this project in the first place.
41 | I have included an example script that you can use to parse and visualize the data that comes from ARCore Data Logger.
42 | Look under the Visualization directory to check it out.
43 | You can run the script by typing the following in your terminal:
44 |
45 | run main_script.m
46 |
47 | Here's one of the figures produced by the MATLAB script:
48 |
49 | 
50 |
51 |
--------------------------------------------------------------------------------
/Visualization/ARCore_sensor_pose.txt:
--------------------------------------------------------------------------------
1 | # Created at Mon Sep 09 23:13:04 PDT 2019 in Burnaby Canada
2 | 15492865785374 -0.234327 0.253666 0.046505 0.937327 -0.108027 0.009575 0.094321
3 | 15492949094847 -0.235978 0.255137 0.047185 0.936479 -0.109231 0.009118 0.095799
4 | 15492999052122 -0.236993 0.256834 0.048032 0.935716 -0.110436 0.008651 0.097048
5 | 15493049045474 -0.237356 0.258507 0.049047 0.935110 -0.113152 0.008718 0.099078
6 | 15493099002749 -0.237297 0.260893 0.050438 0.934388 -0.115710 0.008375 0.101342
7 | 15493132424307 -0.237523 0.263131 0.051251 0.933658 -0.117721 0.008314 0.103419
8 | 15493182412100 -0.237621 0.267502 0.051791 0.932361 -0.121610 0.008099 0.106655
9 | 15493232369376 -0.237079 0.273510 0.051170 0.930789 -0.126307 0.007773 0.110572
10 | 15493282419710 -0.236064 0.279639 0.050123 0.929281 -0.132243 0.007311 0.114676
11 | 15493315714387 -0.235368 0.283145 0.049592 0.928424 -0.136439 0.006850 0.117891
12 | 15493365570959 -0.235189 0.288851 0.049241 0.926729 -0.143355 0.006754 0.123601
13 | 15493398896154 -0.235546 0.293391 0.049167 0.925215 -0.147894 0.006497 0.127407
14 | 15493465346963 -0.236082 0.303120 0.049594 0.921913 -0.157359 0.006057 0.134440
15 | 15493498672158 -0.235859 0.308029 0.049193 0.920364 -0.162077 0.005873 0.138406
16 | 15493565316487 -0.237223 0.318093 0.047436 0.916674 -0.171900 0.005540 0.146098
17 | 15493615273763 -0.238410 0.325932 0.045233 0.913719 -0.179024 0.005310 0.151880
18 | 15493665227153 -0.240766 0.333624 0.044094 0.910375 -0.186158 0.004843 0.157424
19 | 15493698552348 -0.242054 0.338582 0.043837 0.908213 -0.190381 0.004852 0.161203
20 | 15493748552793 -0.243011 0.344817 0.044391 0.905581 -0.196444 0.004881 0.166576
21 | 15493798510069 -0.243064 0.350280 0.045878 0.903393 -0.201208 0.005237 0.171356
22 | 15493831835264 -0.242869 0.353438 0.046931 0.902161 -0.203537 0.005589 0.174023
23 | 15493865197660 -0.241873 0.356012 0.047702 0.901375 -0.205180 0.005799 0.176252
24 | 15493915154935 -0.240483 0.357923 0.048612 0.900941 -0.206160 0.006329 0.178156
25 | 15493965134158 -0.238822 0.358223 0.048571 0.901267 -0.205250 0.006640 0.179098
26 | 15494015121951 -0.237411 0.356467 0.047881 0.902372 -0.202871 0.006663 0.177819
27 | 15494064993780 -0.237693 0.352550 0.046812 0.903891 -0.199493 0.007755 0.177379
28 | 15494114981573 -0.238245 0.347796 0.045897 0.905633 -0.195384 0.007399 0.173961
29 | 15494164898600 -0.238270 0.342513 0.044570 0.907704 -0.192147 0.006013 0.167928
30 | 15494214855875 -0.238194 0.337391 0.041896 0.909767 -0.186688 0.005291 0.163689
31 | 15494264658393 -0.237738 0.331139 0.039457 0.912289 -0.180718 0.004444 0.159007
32 | 15494314676703 -0.238410 0.324546 0.038136 0.914536 -0.174864 0.003322 0.154415
33 | 15494364601657 -0.239757 0.319521 0.036728 0.916010 -0.169506 0.002472 0.148911
34 | 15494414558933 -0.239187 0.315584 0.034940 0.917592 -0.163315 0.002151 0.144924
35 | 15494447905218 -0.238320 0.312884 0.033815 0.918784 -0.159842 0.002125 0.141591
36 | 15494497862494 -0.234075 0.308821 0.031508 0.921328 -0.153451 0.002191 0.137951
37 | 15494547884794 -0.231207 0.303572 0.030285 0.923835 -0.147393 0.002092 0.133559
38 | 15494597872587 -0.229948 0.297506 0.030046 0.926127 -0.140733 0.001999 0.129777
39 | 15494647869667 -0.228999 0.291147 0.029704 0.928391 -0.133963 0.002267 0.126145
40 | 15494697826942 -0.229074 0.284624 0.029602 0.930397 -0.127413 0.002100 0.121935
41 | 15494747763851 -0.228395 0.279502 0.029086 0.932131 -0.120062 0.001220 0.117452
42 | 15494797751644 -0.226995 0.273747 0.028087 0.934209 -0.113419 0.000696 0.113176
43 | 15494864363867 -0.226848 0.265782 0.027862 0.936549 -0.104435 -0.000737 0.107511
44 | 15494930983740 -0.227201 0.256506 0.028253 0.939035 -0.095770 -0.002313 0.101950
45 | 15494980906590 -0.226431 0.248953 0.028996 0.941228 -0.089761 -0.003757 0.098174
46 | 15495014201267 -0.225253 0.244275 0.029656 0.942715 -0.085465 -0.004850 0.095709
47 | 15495047464738 -0.223608 0.239094 0.030772 0.944398 -0.080636 -0.006425 0.093552
48 | 15495097422013 -0.223261 0.231329 0.031915 0.946373 -0.073969 -0.008310 0.089466
49 | 15495147317285 -0.223222 0.225481 0.031263 0.947815 -0.066949 -0.010153 0.085864
50 | 15495180642480 -0.223214 0.220746 0.031158 0.948934 -0.062288 -0.011360 0.083280
51 | 15495230599756 -0.223106 0.212189 0.031180 0.950909 -0.055329 -0.013004 0.079321
52 | 15495263897871 -0.223142 0.206485 0.030968 0.952162 -0.050971 -0.014108 0.076362
53 | 15495297192549 -0.223348 0.201455 0.030582 0.953203 -0.046245 -0.015004 0.073697
54 | 15495347195358 -0.223223 0.193387 0.030062 0.954918 -0.039369 -0.016035 0.069834
55 | 15495380520554 -0.222255 0.188140 0.029741 0.956202 -0.034439 -0.016636 0.067103
56 | 15495430508347 -0.220625 0.180002 0.029704 0.958145 -0.026815 -0.017223 0.062986
57 | 15495480497265 -0.219133 0.171590 0.029384 0.960039 -0.019044 -0.017393 0.058835
58 | 15495513791943 -0.216946 0.165785 0.028841 0.961571 -0.013345 -0.017277 0.056067
59 | 15495563763344 -0.214380 0.156302 0.027868 0.963760 -0.005270 -0.016745 0.051789
60 | 15495613751137 -0.212777 0.146741 0.025887 0.965672 0.004149 -0.016163 0.047608
61 | 15495647059083 -0.210563 0.139691 0.023977 0.967251 0.009658 -0.015719 0.044103
62 | 15495697016359 -0.207292 0.128578 0.021322 0.969558 0.019498 -0.014478 0.040002
63 | 15495747011021 -0.204241 0.115887 0.018838 0.971854 0.028454 -0.012619 0.035417
64 | 15495796968297 -0.200984 0.103249 0.016284 0.974002 0.038652 -0.010891 0.031497
65 | 15495830262974 -0.199385 0.095178 0.014572 0.975179 0.045500 -0.009776 0.028964
66 | 15495880189062 -0.197066 0.081728 0.012067 0.976903 0.054962 -0.008133 0.024405
67 | 15495930146338 -0.195921 0.066970 0.010280 0.978276 0.063645 -0.006754 0.019936
68 | 15495980105940 -0.196714 0.053197 0.008549 0.978979 0.072782 -0.005755 0.015551
69 | 15496030093733 -0.195990 0.042816 0.005448 0.979656 0.081509 -0.004848 0.011373
70 | 15496079998131 -0.197206 0.034340 0.001732 0.979759 0.089608 -0.003937 0.006884
71 | 15496129955406 -0.198440 0.024321 -0.000590 0.979811 0.097076 -0.003532 0.002258
72 | 15496179956263 -0.198780 0.013930 -0.002475 0.979942 0.104187 -0.002870 -0.001619
73 | 15496229913538 -0.199007 0.005106 -0.004734 0.979973 0.111322 -0.002397 -0.005496
74 | 15496279968097 -0.198091 -0.002748 -0.006950 0.980155 0.117492 -0.001440 -0.008905
75 | 15496346618488 -0.195307 -0.013591 -0.008800 0.980609 0.126218 -0.000218 -0.012752
76 | 15496396613902 -0.193579 -0.020151 -0.010050 0.980826 0.131732 0.001340 -0.015015
77 | 15496446571178 -0.191634 -0.025804 -0.011376 0.981061 0.137147 0.002980 -0.017345
78 | 15496496607103 -0.190224 -0.031669 -0.012462 0.981151 0.141039 0.004875 -0.019869
79 | 15496546564378 -0.189234 -0.036546 -0.013859 0.981154 0.143342 0.006898 -0.021764
80 | 15496579832647 -0.188481 -0.039611 -0.014721 0.981167 0.145541 0.008250 -0.022908
81 | 15496629820440 -0.187575 -0.044753 -0.015478 0.981108 0.148607 0.010373 -0.024340
82 | 15496696342646 -0.185587 -0.050817 -0.015620 0.981189 0.150332 0.013584 -0.025494
83 | 15496729637324 -0.184552 -0.053633 -0.015275 0.981239 0.151628 0.015381 -0.025629
84 | 15496779457481 -0.182446 -0.057391 -0.014870 0.981427 0.151271 0.018590 -0.025737
85 | 15496812782677 -0.180395 -0.059174 -0.014869 0.981700 0.151878 0.021198 -0.025259
86 | 15496862805364 -0.176072 -0.061853 -0.014774 0.982321 0.152518 0.025910 -0.024403
87 | 15496912762639 -0.172073 -0.064955 -0.014464 0.982834 0.152736 0.031147 -0.023369
88 | 15496946087834 -0.169373 -0.067256 -0.014514 0.983147 0.152811 0.035072 -0.023353
89 | 15496996084065 -0.164129 -0.070924 -0.014677 0.983776 0.152587 0.041200 -0.022130
90 | 15497046117665 -0.158370 -0.075505 -0.015486 0.984367 0.152392 0.047798 -0.020911
91 | 15497079412342 -0.155835 -0.078586 -0.016326 0.984517 0.152372 0.052172 -0.020396
92 | 15497129369618 -0.150237 -0.082562 -0.017432 0.985042 0.152309 0.059005 -0.019334
93 | 15497162826742 -0.144793 -0.086640 -0.017502 0.985506 0.151721 0.063997 -0.017881
94 | 15497196121420 -0.138423 -0.090208 -0.017400 0.986103 0.151808 0.068737 -0.016953
95 | 15497246109212 -0.128230 -0.095210 -0.016698 0.987022 0.151625 0.076129 -0.015092
96 | 15497296087338 -0.121024 -0.098939 -0.015610 0.987583 0.151837 0.083318 -0.013457
97 | 15497329382016 -0.116885 -0.100251 -0.015203 0.987956 0.151960 0.088038 -0.012097
98 | 15497379280408 -0.111308 -0.102819 -0.014793 0.988342 0.151572 0.095078 -0.009487
99 | 15497429268201 -0.104961 -0.104619 -0.014327 0.988854 0.151349 0.101755 -0.006248
100 | 15497479243787 -0.100807 -0.103815 -0.014778 0.989364 0.150668 0.108117 -0.002120
101 | 15497529201062 -0.096782 -0.104125 -0.014024 0.989745 0.150320 0.113902 0.002244
102 | 15497562540386 -0.092659 -0.105967 -0.012747 0.989961 0.149944 0.117785 0.005626
103 | 15497612497661 -0.087020 -0.109509 -0.011389 0.990104 0.149843 0.122767 0.010576
104 | 15497645774791 -0.083750 -0.111037 -0.011009 0.990220 0.149573 0.125976 0.014459
105 | 15497695732067 -0.079585 -0.113049 -0.010865 0.990337 0.149789 0.128995 0.019463
106 | 15497729057262 -0.077479 -0.114270 -0.010759 0.990365 0.150163 0.130123 0.022545
107 | 15497779060227 -0.076298 -0.117148 -0.010525 0.990123 0.150778 0.130489 0.026365
108 | 15497829017184 -0.075377 -0.120328 -0.010608 0.989812 0.151753 0.128942 0.029437
109 | 15497878974460 -0.077087 -0.123463 -0.010608 0.989294 0.153099 0.125873 0.031784
110 | 15497912299655 -0.078391 -0.124573 -0.011008 0.989048 0.154098 0.122973 0.032978
111 | 15497962236635 -0.080608 -0.125092 -0.013117 0.988778 0.155828 0.117931 0.035080
112 | 15498012224428 -0.083026 -0.125121 -0.016548 0.988523 0.157843 0.111048 0.035953
113 | 15498045573198 -0.085586 -0.125510 -0.018989 0.988211 0.158869 0.105986 0.036633
114 | 15498078867876 -0.089002 -0.126262 -0.021324 0.987766 0.160151 0.100303 0.036665
115 | 15498112193071 -0.093066 -0.127155 -0.023972 0.987216 0.161374 0.094196 0.036501
116 | 15498178802418 -0.101428 -0.130019 -0.029505 0.985869 0.162952 0.081519 0.036459
117 | 15498212097096 -0.106974 -0.131867 -0.031919 0.984961 0.163929 0.074683 0.035581
118 | 15498245340315 -0.113662 -0.135022 -0.033880 0.983719 0.164554 0.068073 0.035446
119 | 15498295328108 -0.122133 -0.139603 -0.036121 0.981982 0.166124 0.058183 0.033819
120 | 15498328653303 -0.127780 -0.142120 -0.037576 0.980848 0.167251 0.052055 0.032620
121 | 15498378554691 -0.135238 -0.146591 -0.039954 0.979094 0.168476 0.043853 0.032017
122 | 15498411849368 -0.139058 -0.149620 -0.041450 0.978038 0.169570 0.038671 0.030802
123 | 15498445115711 -0.142395 -0.152446 -0.043189 0.977046 0.170544 0.034028 0.032507
124 | 15498495072986 -0.147880 -0.155701 -0.044935 0.975638 0.171972 0.027168 0.030969
125 | 15498528398181 -0.152143 -0.157939 -0.045177 0.974611 0.172693 0.022836 0.029829
126 | 15498561741220 -0.156525 -0.159212 -0.045142 0.973711 0.173016 0.019117 0.030758
127 | 15498611759531 -0.163729 -0.156727 -0.044827 0.972944 0.172733 0.013499 0.029888
128 | 15498645077749 -0.168508 -0.153336 -0.044360 0.972690 0.171792 0.009992 0.029437
129 | 15498695035025 -0.174618 -0.146801 -0.044393 0.972619 0.169571 0.005556 0.028678
130 | 15498728360220 -0.177796 -0.141001 -0.044144 0.972912 0.167393 0.003091 0.028328
131 | 15498761772504 -0.181091 -0.134839 -0.043013 0.973229 0.164670 0.000644 0.026914
132 | 15498811760297 -0.184719 -0.124596 -0.041311 0.973986 0.159938 -0.001904 0.026809
133 | 15498861776820 -0.186767 -0.111652 -0.039561 0.975237 0.154678 -0.004064 0.025972
134 | 15498911734095 -0.187321 -0.098974 -0.036980 0.976600 0.148456 -0.005622 0.026864
135 | 15498945059290 -0.187037 -0.088101 -0.035477 0.977751 0.144050 -0.006628 0.026750
136 | 15498994950048 -0.185407 -0.069365 -0.033790 0.979628 0.136609 -0.007517 0.028007
137 | 15499044838539 -0.183957 -0.049202 -0.031446 0.981198 0.129538 -0.007549 0.027644
138 | 15499094795815 -0.184539 -0.027020 -0.029639 0.982006 0.121607 -0.007769 0.029203
139 | 15499128009737 -0.186233 -0.012426 -0.028087 0.982026 0.115902 -0.008063 0.030025
140 | 15499177997530 -0.189172 0.007048 -0.025815 0.981579 0.106861 -0.008298 0.031901
141 | 15499211322725 -0.190231 0.018050 -0.023882 0.981283 0.100360 -0.008457 0.033665
142 | 15499261398489 -0.190550 0.032303 -0.021123 0.980918 0.090024 -0.008579 0.036707
143 | 15499294723684 -0.190033 0.039706 -0.018943 0.980791 0.082516 -0.008857 0.039477
144 | 15499344687315 -0.189153 0.050655 -0.015526 0.980517 0.071268 -0.009056 0.044253
145 | 15499377981993 -0.188684 0.058959 -0.013480 0.980174 0.062880 -0.009274 0.048149
146 | 15499411307188 -0.188020 0.067293 -0.011832 0.979786 0.054132 -0.009603 0.052488
147 | 15499461344993 -0.187639 0.079460 -0.010116 0.978966 0.040063 -0.009964 0.058304
148 | 15499494670189 -0.187771 0.086536 -0.008765 0.978354 0.030277 -0.010315 0.063024
149 | 15499544608967 -0.189884 0.098903 -0.007056 0.976787 0.014915 -0.010179 0.068708
150 | 15499611228840 -0.192259 0.117971 -0.006090 0.974209 -0.005671 -0.010179 0.079648
151 | 15499644497417 -0.194954 0.127208 -0.005859 0.972511 -0.016201 -0.010221 0.084037
152 | 15499711147807 -0.204572 0.143013 -0.003862 0.968340 -0.037411 -0.010449 0.096774
153 | 15499744515975 -0.206664 0.150789 0.001113 0.966722 -0.048506 -0.011703 0.111064
154 | 15499794473250 -0.210646 0.164736 0.004544 0.963571 -0.064160 -0.011928 0.121790
155 | 15499827798445 -0.212441 0.173865 0.007396 0.961553 -0.074314 -0.012048 0.129328
156 | 15499877888239 -0.217271 0.187122 0.011359 0.957940 -0.091721 -0.005883 0.145180
157 | 15499911213434 -0.217591 0.197503 0.014878 0.955733 -0.100948 -0.005852 0.153076
158 | 15499961179052 -0.216286 0.209633 0.019908 0.953351 -0.114066 -0.006019 0.165645
159 | 15500011166844 -0.217797 0.219359 0.025652 0.950678 -0.126380 -0.006094 0.176643
160 | 15500044502618 -0.219443 0.225721 0.027936 0.948744 -0.133932 -0.003678 0.184572
161 | 15500094459893 -0.221033 0.233073 0.032212 0.946458 -0.145065 -0.003828 0.193692
162 | 15500144482538 -0.225092 0.235902 0.038194 0.944577 -0.155738 -0.004723 0.201537
163 | 15500177807733 -0.228622 0.237863 0.041258 0.943107 -0.162453 -0.004748 0.205916
164 | 15500227765008 -0.230320 0.239034 0.043848 0.942281 -0.172300 -0.004917 0.211911
165 | 15500261078082 -0.231635 0.239550 0.044742 0.941785 -0.178517 -0.005472 0.215630
166 | 15500311096393 -0.233326 0.241178 0.044533 0.940962 -0.187753 -0.005739 0.221382
167 | 15500361026518 -0.234416 0.243510 0.043233 0.940150 -0.198022 -0.006250 0.227269
168 | 15500394321196 -0.236177 0.245807 0.042588 0.939141 -0.203937 -0.006087 0.230968
169 | 15500444237040 -0.238856 0.249396 0.041270 0.937575 -0.212421 -0.005275 0.236927
170 | 15500494194315 -0.240493 0.253788 0.039203 0.936065 -0.220487 -0.004494 0.241856
171 | 15500527521662 -0.241845 0.256870 0.038313 0.934912 -0.226129 -0.003390 0.244488
172 | 15500577509455 -0.242285 0.260417 0.036509 0.933889 -0.233576 -0.002083 0.248834
173 | 15500627466731 -0.242679 0.262216 0.034611 0.933355 -0.240975 -0.000788 0.252778
174 | 15500660829608 -0.243420 0.264113 0.033252 0.932676 -0.245656 0.000424 0.254990
175 | 15500710786883 -0.245825 0.267591 0.032043 0.931095 -0.252438 0.001968 0.258100
176 | 15500760783251 -0.245682 0.270626 0.030274 0.930315 -0.258990 0.004275 0.260886
177 | 15500810740526 -0.243933 0.274253 0.028168 0.929779 -0.264150 0.006457 0.263672
178 | 15500844064170 -0.243042 0.276451 0.027201 0.929390 -0.267461 0.007961 0.264104
179 | 15500894051962 -0.242612 0.279843 0.026174 0.928516 -0.270773 0.010038 0.265387
180 | 15500927377158 -0.242159 0.282375 0.024896 0.927903 -0.271733 0.011134 0.265650
181 | 15500977337399 -0.240339 0.287065 0.021801 0.927014 -0.270627 0.012866 0.264253
182 | 15501010632077 -0.239046 0.290223 0.019615 0.926414 -0.268193 0.012802 0.262466
183 | 15501060589800 -0.240016 0.291997 0.018267 0.925633 -0.262890 0.012152 0.256852
184 | 15501093884478 -0.239986 0.291358 0.017644 0.925854 -0.257794 0.011753 0.253132
185 | 15501143804776 -0.236681 0.289998 0.015488 0.927170 -0.247883 0.011245 0.245982
186 | 15501177160489 -0.236234 0.288790 0.015278 0.927664 -0.241359 0.010877 0.240991
187 | 15501227117764 -0.236253 0.286910 0.014101 0.928261 -0.230687 0.010436 0.233392
188 | 15501276996591 -0.231337 0.282333 0.011345 0.930936 -0.219927 0.010042 0.225630
189 | 15501310321786 -0.227424 0.278629 0.009449 0.933035 -0.211994 0.009486 0.220712
190 | 15501343613499 -0.222721 0.274385 0.007654 0.935441 -0.203127 0.008621 0.215723
191 | 15501393570775 -0.215789 0.268823 0.006392 0.938684 -0.188400 0.006235 0.207770
192 | 15501426865452 -0.214471 0.265977 0.007682 0.939787 -0.177664 0.004479 0.202051
193 | 15501460175311 -0.212046 0.260838 0.009858 0.941755 -0.166678 0.002835 0.196394
194 | 15501510132587 -0.206235 0.254624 0.013169 0.944701 -0.149354 0.000291 0.187981
195 | 15501560011580 -0.204622 0.246921 0.018886 0.946997 -0.132647 -0.001834 0.178909
196 | 15501609999373 -0.201352 0.236717 0.023793 0.950187 -0.116789 -0.003785 0.171070
197 | 15501643103052 -0.200001 0.230340 0.026089 0.951978 -0.106693 -0.004942 0.166737
198 | 15501693060327 -0.200437 0.223265 0.024957 0.953601 -0.091255 -0.006766 0.159942
199 | 15501726416040 -0.199483 0.217640 0.023837 0.955129 -0.081291 -0.008243 0.155885
200 | 15501776400094 -0.200344 0.209475 0.023983 0.956769 -0.067938 -0.010232 0.150029
201 | 15501826357370 -0.200416 0.201771 0.023914 0.958410 -0.054382 -0.012474 0.144678
202 | 15501859728506 -0.201155 0.197204 0.023909 0.959206 -0.046025 -0.013758 0.140103
203 | 15501909685782 -0.203981 0.191601 0.024654 0.959725 -0.032859 -0.015756 0.134789
204 | 15501943014542 -0.203648 0.187951 0.025018 0.960508 -0.024427 -0.016336 0.131729
205 | 15501992971817 -0.202216 0.182359 0.025572 0.961873 -0.011212 -0.017893 0.126354
206 | 15502043000153 -0.201338 0.177130 0.026480 0.963009 0.001658 -0.018618 0.120409
207 | 15502076325349 -0.198524 0.174550 0.026627 0.964060 0.011075 -0.018872 0.116762
208 | 15502126282624 -0.196211 0.168164 0.026445 0.965672 0.025593 -0.018715 0.111563
209 | 15502176309821 -0.195204 0.157222 0.027307 0.967694 0.039548 -0.017825 0.105614
210 | 15502226297614 -0.196550 0.149253 0.025661 0.968728 0.054403 -0.017586 0.099860
211 | 15502259616174 -0.198125 0.144875 0.022514 0.969149 0.062820 -0.017896 0.093876
212 | 15502309603967 -0.202015 0.135824 0.016523 0.969778 0.077555 -0.018640 0.086462
213 | 15502359582090 -0.202831 0.127394 0.011108 0.970828 0.092713 -0.018836 0.078960
214 | 15502409539365 -0.200604 0.122318 0.006572 0.971984 0.107873 -0.020031 0.071644
215 | 15502459532343 -0.197910 0.112732 0.004235 0.973707 0.121919 -0.021446 0.062152
216 | 15502509489618 -0.191934 0.107705 0.006415 0.975459 0.137569 -0.023238 0.054817
217 | 15502542949373 -0.188528 0.107704 0.011185 0.976080 0.148507 -0.024875 0.048390
218 | 15502592937166 -0.182670 0.109510 0.015010 0.976941 0.165399 -0.027444 0.040725
219 | 15502642939693 -0.181156 0.111888 0.013059 0.976982 0.180301 -0.029572 0.031972
220 | 15502692927486 -0.180871 0.112093 0.011274 0.977033 0.195623 -0.031725 0.024509
221 | 15502742864561 -0.180866 0.109126 0.009530 0.977389 0.210328 -0.033410 0.016509
222 | 15502792821837 -0.183281 0.103134 0.009862 0.977586 0.225634 -0.035083 0.009495
223 | 15502826147032 -0.185819 0.098435 0.009481 0.977595 0.234600 -0.036113 0.003900
224 | 15502876159934 -0.186225 0.096741 0.009556 0.977686 0.250417 -0.037675 -0.001540
225 | 15502926117209 -0.188763 0.094142 0.009184 0.977457 0.266235 -0.039190 -0.006191
226 | 15502959594271 -0.189757 0.091645 0.009069 0.977502 0.275423 -0.040420 -0.008777
227 | 15503009551546 -0.186633 0.089368 0.009766 0.978308 0.291577 -0.041874 -0.012860
228 | 15503076146673 -0.186156 0.086184 0.012702 0.978651 0.312848 -0.042156 -0.016986
229 | 15503109471868 -0.184934 0.084407 0.015204 0.979002 0.324056 -0.042068 -0.019876
230 | 15503159406772 -0.180193 0.084320 0.020416 0.979798 0.339335 -0.040838 -0.023646
231 | 15503192701450 -0.178540 0.083881 0.024558 0.980043 0.350102 -0.039939 -0.025953
232 | 15503242729236 -0.176190 0.085192 0.029501 0.980219 0.363821 -0.037405 -0.028495
233 | 15503276023914 -0.173631 0.091133 0.030612 0.980107 0.373432 -0.035797 -0.029453
234 | 15503325981189 -0.171718 0.101589 0.029501 0.979450 0.386905 -0.033243 -0.030203
235 | 15503359332007 -0.169489 0.110543 0.027028 0.978940 0.393670 -0.031050 -0.030229
236 | 15503409289283 -0.165970 0.120503 0.023913 0.978448 0.405046 -0.028614 -0.029946
237 | 15503459292079 -0.164863 0.130190 0.022146 0.977435 0.415258 -0.026131 -0.028884
238 | 15503509279872 -0.165941 0.134793 0.021383 0.976646 0.425129 -0.023670 -0.028722
239 | 15503542571508 -0.167012 0.136668 0.021318 0.976204 0.430535 -0.021470 -0.029081
240 | 15503592559301 -0.166543 0.135829 0.021873 0.976389 0.439757 -0.018515 -0.028733
241 | 15503642516577 -0.164134 0.131112 0.021849 0.977442 0.448565 -0.015023 -0.028295
242 | 15503692548440 -0.163135 0.124690 0.022595 0.978432 0.457661 -0.011298 -0.027711
243 | 15503742464956 -0.162346 0.120571 0.023654 0.979054 0.465666 -0.007056 -0.026512
244 | 15503792452749 -0.161805 0.120442 0.024182 0.979146 0.474238 -0.003100 -0.024777
245 | 15503842410025 -0.160397 0.121905 0.024314 0.979194 0.483013 0.000711 -0.022803
246 | 15503892401507 -0.158621 0.119508 0.023833 0.979790 0.491257 0.005249 -0.020878
247 | 15503942364043 -0.154635 0.116621 0.023201 0.980790 0.499489 0.009450 -0.019479
248 | 15503992351836 -0.151759 0.113393 0.022285 0.981639 0.507249 0.012696 -0.018374
249 | 15504042331552 -0.149300 0.110541 0.020542 0.982379 0.513817 0.015596 -0.017584
250 | 15504092288828 -0.149956 0.110424 0.018106 0.982340 0.518519 0.017212 -0.016602
251 | 15504142306380 -0.150208 0.111645 0.019664 0.982134 0.519344 0.017573 -0.014518
252 | 15504175631575 -0.148074 0.110043 0.021024 0.982610 0.517591 0.017389 -0.012763
253 | 15504225588851 -0.144711 0.106160 0.017831 0.983601 0.512350 0.016393 -0.010366
254 | 15504275589538 -0.149759 0.107978 0.011845 0.982737 0.503907 0.014684 -0.007723
255 | 15504325577331 -0.154886 0.122830 0.005467 0.980252 0.494026 0.011874 -0.004304
256 | 15504358858584 -0.158970 0.138149 0.000671 0.977570 0.486794 0.010747 -0.001616
257 | 15504408846377 -0.166625 0.154193 -0.005078 0.973876 0.473524 0.008818 0.001948
258 | 15504458647471 -0.165480 0.158893 -0.007417 0.973301 0.458739 0.007217 0.006676
259 | 15504491942149 -0.162524 0.158242 -0.006833 0.973909 0.448946 0.006317 0.009607
260 | 15504558854028 -0.157109 0.156851 -0.005388 0.975031 0.429245 0.006219 0.015275
261 | 15504592209740 -0.158203 0.158740 -0.004669 0.974552 0.419582 0.006313 0.017285
262 | 15504625504418 -0.160328 0.162519 -0.003788 0.973585 0.409486 0.006818 0.019431
263 | 15504692005921 -0.155782 0.173064 -0.002970 0.972508 0.386490 0.009039 0.024339
264 | 15504725331117 -0.154345 0.176478 -0.004361 0.972118 0.374499 0.010264 0.027021
265 | 15504758684146 -0.157349 0.175423 -0.005996 0.971819 0.361447 0.011473 0.029029
266 | 15504808641421 -0.160821 0.167700 -0.010153 0.972579 0.342994 0.012199 0.031313
267 | 15504841880412 -0.163948 0.159321 -0.011887 0.973446 0.329812 0.012448 0.032283
268 | 15504908530803 -0.167561 0.141434 -0.014667 0.975553 0.305680 0.012031 0.034822
269 | 15504958416586 -0.176040 0.135844 -0.018216 0.974794 0.288427 0.011917 0.036802
270 | 15505008404379 -0.181360 0.123605 -0.020555 0.975401 0.271534 0.011885 0.039268
271 | 15505041669517 -0.183181 0.117595 -0.021448 0.975785 0.260584 0.011753 0.041117
272 | 15505091657310 -0.188054 0.105859 -0.021685 0.976196 0.244697 0.011525 0.043694
273 | 15505141599818 -0.185789 0.100088 -0.022717 0.977215 0.228443 0.011330 0.047231
274 | 15505191587611 -0.187958 0.100552 -0.024243 0.976715 0.213144 0.010738 0.050657
275 | 15505241498731 -0.188717 0.099170 -0.024334 0.976708 0.197662 0.009552 0.053368
276 | 15505291486524 -0.189009 0.095833 -0.024250 0.976987 0.182873 0.008388 0.056343
277 | 15505324784052 -0.190332 0.097310 -0.024844 0.976569 0.172944 0.008355 0.057816
278 | 15505374741328 -0.190283 0.098600 -0.024789 0.976451 0.158551 0.008041 0.061247
279 | 15505424729121 -0.187709 0.098489 -0.023973 0.976980 0.144298 0.008169 0.065001
280 | 15505474668355 -0.186063 0.104269 -0.022944 0.976720 0.130443 0.009200 0.068562
281 | 15505524625631 -0.183670 0.110365 -0.022856 0.976505 0.116950 0.009706 0.073003
282 | 15505574580152 -0.183439 0.116146 -0.022840 0.975878 0.103253 0.010492 0.077413
283 | 15505624537427 -0.182940 0.119252 -0.022195 0.975612 0.089206 0.010852 0.082404
284 | 15505674478775 -0.182698 0.120679 -0.019879 0.975532 0.074033 0.011604 0.086504
285 | 15505724466568 -0.184012 0.124234 -0.018707 0.974862 0.059092 0.011931 0.091514
286 | 15505774466000 -0.188537 0.129118 -0.018376 0.973368 0.044132 0.012624 0.096482
287 | 15505824423275 -0.192467 0.133722 -0.018076 0.971982 0.029596 0.012630 0.101888
288 | 15505874382682 -0.197093 0.139818 -0.017021 0.970214 0.014661 0.013451 0.107675
289 | 15505924339958 -0.199493 0.144578 -0.014963 0.969059 0.001084 0.013220 0.113389
290 | 15505974289843 -0.202934 0.149746 -0.011671 0.967604 -0.012945 0.013705 0.119222
291 | 15506024277636 -0.204851 0.157185 -0.008911 0.966048 -0.026346 0.013883 0.125546
292 | 15506074213528 -0.206666 0.164446 -0.005939 0.964475 -0.039500 0.013536 0.131628
293 | 15506124170803 -0.210080 0.168355 -0.001403 0.963079 -0.052882 0.012629 0.137671
294 | 15506174130257 -0.215197 0.174160 0.002599 0.960912 -0.067646 0.011512 0.143439
295 | 15506207424935 -0.218203 0.176853 0.005594 0.959729 -0.077146 0.010359 0.147486
296 | 15506257405877 -0.224811 0.182289 0.010056 0.957147 -0.092190 0.008305 0.153170
297 | 15506290700555 -0.229583 0.186429 0.012428 0.955186 -0.102006 0.006689 0.157165
298 | 15506324025750 -0.232968 0.190365 0.014132 0.953565 -0.111683 0.005061 0.161260
299 | 15506374022453 -0.238378 0.198801 0.015793 0.950476 -0.126932 0.002801 0.167405
300 | 15506423979729 -0.244192 0.206553 0.016488 0.947330 -0.141301 0.000779 0.173448
301 | 15506473972973 -0.246668 0.219437 0.015931 0.943795 -0.156746 -0.000470 0.179782
302 | 15506507298168 -0.248802 0.229068 0.016949 0.940924 -0.166397 -0.001249 0.184281
303 | 15506540560076 -0.250110 0.236470 0.018857 0.938707 -0.177086 -0.001859 0.188669
304 | 15506590578387 -0.248801 0.246397 0.022258 0.936424 -0.192103 -0.002601 0.195567
305 | 15506640514940 -0.250404 0.253262 0.027098 0.934035 -0.207830 -0.003413 0.202191
306 | 15506673809618 -0.250212 0.257732 0.028946 0.932808 -0.217508 -0.003604 0.207143
307 | 15506707134813 -0.248621 0.261787 0.029531 0.932085 -0.227088 -0.003683 0.212207
308 | 15506757120783 -0.249156 0.266066 0.031328 0.930671 -0.241809 -0.003551 0.218561
309 | 15506807108576 -0.249855 0.273725 0.032964 0.928203 -0.255308 -0.002500 0.225380
310 | 15506840395980 -0.247778 0.280790 0.031832 0.926687 -0.263717 -0.001271 0.228869
311 | 15506890383773 -0.246290 0.287450 0.030321 0.925091 -0.275672 0.001053 0.235493
312 | 15506940392759 -0.244697 0.294667 0.026942 0.923346 -0.285981 0.004115 0.242245
313 | 15506973687437 -0.243372 0.299072 0.023931 0.922363 -0.292205 0.006381 0.246498
314 | 15507023644712 -0.242361 0.305510 0.019995 0.920611 -0.300371 0.009897 0.252725
315 | 15507056990792 -0.241541 0.309649 0.017458 0.919495 -0.304810 0.012558 0.256469
316 | 15507106948068 -0.239438 0.314393 0.012693 0.918512 -0.310029 0.016023 0.261402
317 | 15507156919645 -0.237358 0.315728 0.008140 0.918646 -0.313068 0.019476 0.264575
318 | 15507190244840 -0.237335 0.315063 0.006101 0.918896 -0.313936 0.021013 0.265168
319 | 15507240174928 -0.236295 0.314215 0.002415 0.919471 -0.311871 0.023330 0.263466
320 | 15507273500124 -0.235805 0.311946 0.000241 0.920372 -0.309164 0.023980 0.261205
321 | 15507306794802 -0.236397 0.308545 -0.001350 0.921366 -0.305395 0.024259 0.257828
322 | 15507356710288 -0.238041 0.303085 -0.003686 0.922747 -0.297715 0.025108 0.251135
323 | 15507406698081 -0.238069 0.295643 -0.006772 0.925134 -0.288358 0.025101 0.243032
324 | 15507439941203 -0.238357 0.290101 -0.007916 0.926803 -0.281343 0.025088 0.236834
325 | 15507489928996 -0.237596 0.282256 -0.008745 0.929410 -0.268898 0.024718 0.226477
326 | 15507523254191 -0.239964 0.277378 -0.008411 0.930273 -0.260368 0.024643 0.218950
327 | 15507573188122 -0.241637 0.268431 -0.010142 0.932445 -0.246130 0.023353 0.208709
328 | 15507606513318 -0.238859 0.261791 -0.011605 0.935028 -0.235840 0.022382 0.201986
329 | 15507656556819 -0.237208 0.253714 -0.012666 0.937657 -0.220356 0.021249 0.190961
330 | 15507689851497 -0.233774 0.248959 -0.013917 0.939774 -0.209124 0.020339 0.184884
331 | 15507739855033 -0.227550 0.244889 -0.012733 0.942384 -0.192346 0.019567 0.175299
332 | 15507773149711 -0.223294 0.242823 -0.010900 0.943959 -0.180795 0.018532 0.169986
333 | 15507806474906 -0.217451 0.239759 -0.008732 0.946126 -0.169185 0.017125 0.165105
334 | 15507839819769 -0.212645 0.235440 -0.004993 0.948327 -0.158292 0.015542 0.159812
335 | 15507889807562 -0.209633 0.233955 0.000749 0.949378 -0.140755 0.013163 0.153039
336 | 15507923102240 -0.207378 0.232061 0.004127 0.950329 -0.129196 0.011216 0.149034
337 | 15507973234320 -0.203908 0.228172 0.011455 0.951960 -0.112724 0.008104 0.143306
338 | 15508006559515 -0.204094 0.231193 0.016986 0.951108 -0.101108 0.005297 0.139855
339 | 15508056590945 -0.201281 0.232326 0.021187 0.951347 -0.085044 0.000606 0.133818
340 | 15508089916141 -0.198106 0.229848 0.022809 0.952577 -0.074964 -0.002432 0.130514
341 | 15508139846531 -0.196414 0.226501 0.025682 0.953656 -0.060482 -0.005903 0.125501
342 | 15508189803807 -0.193482 0.219702 0.027485 0.955793 -0.045918 -0.008788 0.120704
343 | 15508223100424 -0.190990 0.214055 0.028117 0.957556 -0.037204 -0.009685 0.116725
344 | 15508256395102 -0.189012 0.209789 0.028667 0.958875 -0.027877 -0.010728 0.113654
345 | 15508306413412 -0.186646 0.203041 0.028778 0.960786 -0.013812 -0.011968 0.109816
346 | 15508339687633 -0.186011 0.197453 0.029002 0.962066 -0.005196 -0.012149 0.107122
347 | 15508372982311 -0.185876 0.191357 0.028636 0.963334 0.003596 -0.012834 0.104985
348 | 15508422970104 -0.185559 0.184891 0.026859 0.964708 0.016641 -0.013826 0.102492
349 | 15508456232223 -0.185326 0.179368 0.025901 0.965821 0.024876 -0.013787 0.099639
350 | 15508506189499 -0.183938 0.172813 0.024565 0.967315 0.037598 -0.014521 0.097447
351 | 15508556197654 -0.183409 0.168915 0.023641 0.968127 0.050265 -0.014664 0.094701
352 | 15508589522849 -0.183697 0.167225 0.024046 0.968356 0.059280 -0.015634 0.092871
353 | 15508639480929 -0.184440 0.167632 0.025858 0.968097 0.071300 -0.018090 0.087642
354 | 15508689438204 -0.185556 0.164832 0.027910 0.968308 0.085443 -0.021011 0.083435
355 | 15508722732882 -0.185825 0.161700 0.028667 0.968762 0.093496 -0.022910 0.078853
356 | 15508756044371 -0.186174 0.157311 0.029282 0.969399 0.103119 -0.024757 0.075001
357 | 15508806001647 -0.186340 0.149909 0.029154 0.970544 0.117804 -0.027256 0.069123
358 | 15508855921787 -0.186144 0.141018 0.029518 0.971902 0.131022 -0.028755 0.060740
359 | 15508905940098 -0.184011 0.135989 0.029138 0.973035 0.146319 -0.029895 0.055881
360 | 15508955883662 -0.182040 0.130748 0.028764 0.974135 0.160972 -0.029991 0.050088
361 | 15509005840937 -0.181836 0.126585 0.028926 0.974718 0.176449 -0.029922 0.045971
362 | 15509039177838 -0.181211 0.123977 0.028099 0.975194 0.185747 -0.029263 0.042126
363 | 15509089135113 -0.181100 0.122536 0.025011 0.975480 0.201138 -0.028636 0.038417
364 | 15509122460308 -0.180882 0.122473 0.022537 0.975589 0.211537 -0.028383 0.036088
365 | 15509172514957 -0.178478 0.122066 0.018932 0.976159 0.225466 -0.026826 0.031392
366 | 15509205809635 -0.177890 0.121324 0.016796 0.976398 0.236239 -0.026945 0.028988
367 | 15509255809878 -0.175427 0.114028 0.015436 0.977745 0.251768 -0.026937 0.024366
368 | 15509305767154 -0.173417 0.101395 0.016107 0.979483 0.267766 -0.027393 0.019981
369 | 15509355669550 -0.171895 0.086725 0.019292 0.981101 0.283050 -0.027667 0.015569
370 | 15509388994745 -0.170150 0.078441 0.021839 0.982048 0.293226 -0.027815 0.012542
371 | 15509422181933 -0.166955 0.071985 0.024295 0.983033 0.303118 -0.027410 0.009385
372 | 15509472169726 -0.163087 0.064692 0.028639 0.984072 0.318430 -0.026874 0.005205
373 | 15509505494921 -0.161769 0.061294 0.031608 0.984416 0.328602 -0.026322 0.002481
374 | 15509555477195 -0.156430 0.062776 0.034502 0.985088 0.343282 -0.024893 -0.001092
375 | 15509588802390 -0.153836 0.067725 0.036253 0.985106 0.353054 -0.023742 -0.002749
376 | 15509638805368 -0.151934 0.073795 0.037918 0.984902 0.366117 -0.021406 -0.006259
377 | 15509688762644 -0.149797 0.072626 0.037871 0.985318 0.379604 -0.019826 -0.009229
378 | 15509722087839 -0.149871 0.067378 0.037584 0.985691 0.388406 -0.019018 -0.011307
379 | 15509772016110 -0.151479 0.061179 0.036173 0.985902 0.401612 -0.017564 -0.014403
380 | 15509821973385 -0.157443 0.060282 0.033447 0.985119 0.414388 -0.016330 -0.017654
381 | 15509855371982 -0.160895 0.057297 0.032535 0.984770 0.422969 -0.014901 -0.020669
382 | 15509905329257 -0.159311 0.056688 0.031947 0.985082 0.436160 -0.013491 -0.022698
383 | 15509938546591 -0.157385 0.060359 0.031445 0.985189 0.445001 -0.011859 -0.023625
384 | 15509988503867 -0.152782 0.073676 0.029534 0.985067 0.458300 -0.009949 -0.023025
385 | 15510021811398 -0.151230 0.083386 0.028677 0.984558 0.466825 -0.008084 -0.022912
386 | 15510071799191 -0.150559 0.092766 0.029235 0.983805 0.478844 -0.005427 -0.022668
387 | 15510105093869 -0.148716 0.099176 0.029831 0.983442 0.486640 -0.003700 -0.022274
388 | 15510155075445 -0.148731 0.103034 0.031307 0.982997 0.497099 -0.000180 -0.023167
389 | 15510205063238 -0.154019 0.103235 0.030662 0.982182 0.507555 0.003304 -0.023814
390 | 15510238469735 -0.157757 0.102767 0.030583 0.981640 0.515006 0.006264 -0.024323
391 | 15510288427010 -0.155858 0.098217 0.033603 0.982310 0.523998 0.010401 -0.023059
392 | 15510321782723 -0.151783 0.096464 0.035571 0.983052 0.529568 0.013002 -0.021516
393 | 15510371739200 -0.145690 0.097614 0.036606 0.983822 0.536945 0.017914 -0.020098
394 | 15510421696475 -0.140852 0.094025 0.038119 0.984818 0.542946 0.023115 -0.019592
395 | 15510455092538 -0.138630 0.087030 0.039505 0.985721 0.546549 0.026804 -0.019119
396 | 15510505049814 -0.133747 0.077945 0.042070 0.987049 0.550942 0.032228 -0.017906
397 | 15510554964791 -0.129474 0.072434 0.045954 0.987865 0.554087 0.038431 -0.016533
398 | 15510604983102 -0.126332 0.070895 0.049110 0.988232 0.556136 0.043533 -0.013996
399 | 15510638208371 -0.125409 0.070736 0.050045 0.988314 0.556739 0.047186 -0.013319
400 | 15510688165647 -0.125805 0.071190 0.048902 0.988288 0.556788 0.050498 -0.011610
401 | 15510721490842 -0.124723 0.071956 0.046888 0.988467 0.555826 0.051210 -0.010298
402 | 15510771456043 -0.122140 0.072724 0.042682 0.988924 0.551328 0.050399 -0.008482
403 | 15510804781239 -0.120831 0.072560 0.039110 0.989245 0.547517 0.048628 -0.007792
404 | 15510854689884 -0.121342 0.073036 0.032172 0.989397 0.539457 0.045453 -0.008166
405 | 15510904677677 -0.121468 0.069682 0.026920 0.989780 0.528923 0.040266 -0.008716
406 | 15510954653426 -0.125807 0.063723 0.023965 0.989716 0.515369 0.035018 -0.010315
407 | 15510987948104 -0.130339 0.063755 0.021451 0.989185 0.505455 0.030877 -0.010838
408 | 15511021273299 -0.137158 0.064513 0.020061 0.988243 0.494937 0.026517 -0.011516
409 | 15511054516509 -0.146089 0.061149 0.019500 0.987187 0.483038 0.023279 -0.012217
410 | 15511087841704 -0.155043 0.059433 0.018473 0.985945 0.471305 0.019479 -0.013097
411 | 15511121238793 -0.161342 0.059473 0.016911 0.984960 0.458731 0.015891 -0.014161
412 | 15511171257103 -0.167528 0.059039 0.014661 0.983989 0.440130 0.010944 -0.013139
413 | 15511204551781 -0.171360 0.061470 0.014570 0.983181 0.427543 0.008071 -0.011520
414 | 15511254508407 -0.175723 0.058804 0.015567 0.982558 0.408576 0.004835 -0.008845
415 | 15511304496200 -0.179132 0.059248 0.017465 0.981884 0.389910 0.002522 -0.006450
416 | 15511337955002 -0.183138 0.061772 0.018209 0.980976 0.377888 0.002628 -0.005722
417 | 15511387912278 -0.182922 0.067695 0.018119 0.980627 0.359390 0.003100 -0.002789
418 | 15511421237473 -0.183485 0.074686 0.019294 0.979991 0.346998 0.003925 -0.000366
419 | 15511454582114 -0.186168 0.083208 0.019855 0.978787 0.334452 0.005689 0.000922
420 | 15511504569907 -0.187984 0.095596 0.020565 0.977292 0.315529 0.007901 0.003980
421 | 15511554527183 -0.189288 0.101418 0.024706 0.976358 0.297075 0.009806 0.006820
422 | 15511604346172 -0.185439 0.105528 0.029378 0.976531 0.278084 0.012524 0.008757
423 | 15511637625200 -0.183264 0.105531 0.032605 0.976839 0.265490 0.014271 0.010238
424 | 15511687582476 -0.177317 0.109104 0.036236 0.977416 0.247121 0.017294 0.013879
425 | 15511720877154 -0.174962 0.113238 0.037972 0.977304 0.235010 0.019301 0.016578
426 | 15511770931435 -0.175351 0.121061 0.040343 0.976201 0.217200 0.022790 0.019489
427 | 15511820888710 -0.172470 0.128401 0.041854 0.975713 0.199948 0.025543 0.023940
428 | 15511854257929 -0.170551 0.131973 0.042385 0.975550 0.188093 0.027845 0.025492
429 | 15511904215204 -0.170080 0.138598 0.042885 0.974692 0.171135 0.029612 0.029387
430 | 15511954176777 -0.169658 0.143259 0.042468 0.974110 0.153432 0.030929 0.032883
431 | 15512004134052 -0.174347 0.148396 0.040276 0.972604 0.136363 0.030558 0.036505
432 | 15512054167539 -0.181591 0.154971 0.035620 0.970433 0.118936 0.029328 0.039991
433 | 15512104155332 -0.188343 0.156966 0.030139 0.969010 0.101394 0.026689 0.044006
434 | 15512137450010 -0.191964 0.157999 0.026215 0.968245 0.089196 0.025238 0.046084
435 | 15512187431483 -0.192969 0.156741 0.022351 0.968347 0.071110 0.022756 0.051521
436 | 15512237510588 -0.194484 0.159804 0.020389 0.967586 0.053092 0.021445 0.058010
437 | 15512287467863 -0.196723 0.166321 0.019276 0.966057 0.036000 0.020075 0.064788
438 | 15512337465999 -0.197884 0.168787 0.018407 0.965409 0.018240 0.020286 0.070894
439 | 15512387453792 -0.200327 0.174211 0.017370 0.963959 0.002312 0.019922 0.077738
440 | 15512420847145 -0.202540 0.177185 0.016728 0.962966 -0.008749 0.020258 0.082203
441 | 15512470804420 -0.206624 0.179868 0.017374 0.961588 -0.024190 0.019344 0.089231
442 | 15512504129616 -0.208134 0.180750 0.019121 0.961064 -0.034069 0.018327 0.094121
443 | 15512554094553 -0.211265 0.184512 0.021357 0.959618 -0.049407 0.017063 0.100332
444 | 15512587419748 -0.214443 0.191209 0.022892 0.957564 -0.059084 0.016047 0.105718
445 | 15512620744944 -0.217219 0.198571 0.024893 0.955388 -0.068497 0.014889 0.111260
446 | 15512670778466 -0.220052 0.210544 0.027193 0.952108 -0.082835 0.013567 0.119545
447 | 15512704073143 -0.220881 0.218227 0.028453 0.950147 -0.092093 0.012186 0.126109
448 | 15512737381126 -0.220807 0.225013 0.029424 0.948550 -0.101655 0.011012 0.132144
449 | 15512787368919 -0.222935 0.235450 0.031308 0.945454 -0.115817 0.008905 0.141272
450 | 15512820663597 -0.225766 0.242776 0.032240 0.942895 -0.125105 0.007701 0.147199
451 | 15512853974609 -0.229082 0.248104 0.032746 0.940688 -0.134459 0.007296 0.152333
452 | 15512903962402 -0.234774 0.251971 0.034653 0.938185 -0.148379 0.006332 0.160014
453 | 15512937429097 -0.238795 0.254433 0.037882 0.936379 -0.157604 0.006036 0.163438
454 | 15512987386373 -0.242202 0.257473 0.042435 0.934476 -0.170835 0.004771 0.168912
455 | 15513020681051 -0.244749 0.258767 0.045122 0.933328 -0.179600 0.003759 0.172956
456 | 15513054040524 -0.245943 0.259390 0.047875 0.932704 -0.188565 0.003269 0.176050
457 | 15513103997799 -0.244789 0.263284 0.050909 0.931756 -0.200669 0.003082 0.183136
458 | 15513137326339 -0.245664 0.266765 0.052665 0.930436 -0.209391 0.003522 0.185914
459 | 15513187314132 -0.244121 0.270940 0.053919 0.929564 -0.221085 0.004399 0.193259
460 | 15513220608809 -0.241104 0.272620 0.053994 0.929856 -0.228451 0.005075 0.198306
461 | 15513253927340 -0.238576 0.275177 0.053318 0.929793 -0.236114 0.006078 0.202092
462 | 15513303884615 -0.234630 0.279273 0.049922 0.929765 -0.246382 0.007130 0.209234
463 | 15513337130153 -0.231468 0.279698 0.046009 0.930631 -0.252378 0.008393 0.213421
464 | 15513387087429 -0.229296 0.279930 0.040136 0.931371 -0.260934 0.009736 0.219396
465 | 15513437124991 -0.228633 0.279727 0.033426 0.931860 -0.267774 0.012040 0.225216
466 | 15513487143302 -0.227759 0.277538 0.027991 0.932907 -0.274336 0.014177 0.230411
467 | 15513520437979 -0.226459 0.277454 0.026044 0.933304 -0.278168 0.015809 0.234167
468 | 15513570397622 -0.224437 0.276844 0.025767 0.933981 -0.283946 0.019274 0.238130
469 | 15513603722817 -0.223857 0.278213 0.024963 0.933736 -0.286525 0.021522 0.241700
470 | 15513637012379 -0.222761 0.279056 0.022921 0.933799 -0.288780 0.024647 0.245338
471 | 15513687000172 -0.223124 0.278555 0.018597 0.933958 -0.291252 0.028047 0.249689
472 | 15513720294850 -0.225356 0.277538 0.014820 0.933792 -0.292304 0.030032 0.252148
473 | 15513770239262 -0.230260 0.272819 0.009647 0.934054 -0.293165 0.033286 0.253973
474 | 15513803564457 -0.232780 0.268087 0.007240 0.934821 -0.292169 0.034479 0.254867
475 | 15513853492530 -0.234603 0.265105 0.003320 0.935238 -0.288663 0.035613 0.254441
476 | 15513886817726 -0.235937 0.265548 0.001317 0.934782 -0.284380 0.035246 0.252707
477 | 15513936808946 -0.239469 0.264788 0.000488 0.934099 -0.277191 0.033533 0.247473
478 | 15513970103624 -0.241714 0.262811 0.000477 0.934080 -0.271220 0.031557 0.242962
479 | 15514020091417 -0.243079 0.260517 -0.000928 0.934367 -0.260823 0.028095 0.235397
480 | 15514070082169 -0.245019 0.257475 -0.002327 0.934701 -0.249243 0.024563 0.225987
481 | 15514103376846 -0.243471 0.258147 -0.004399 0.934913 -0.240383 0.021937 0.220019
482 | 15514136727735 -0.241575 0.260109 -0.005957 0.934852 -0.231352 0.019741 0.213192
483 | 15514186685010 -0.240550 0.258589 -0.004819 0.935545 -0.217423 0.016645 0.203098
484 | 15514220010205 -0.236636 0.255856 -0.003262 0.937299 -0.207654 0.015160 0.196454
485 | 15514253283267 -0.234658 0.255454 -0.000914 0.937912 -0.197014 0.015073 0.190036
486 | 15514303271060 -0.228916 0.253511 -0.000312 0.939856 -0.181330 0.014032 0.180402
487 | 15514336573469 -0.226863 0.251436 0.000339 0.940911 -0.171308 0.014042 0.173081
488 | 15514369898665 -0.226171 0.249126 0.001260 0.941691 -0.160630 0.012697 0.165857
489 | 15514403193342 -0.224068 0.247760 0.001012 0.942554 -0.149589 0.010705 0.158475
490 | 15514453212958 -0.222615 0.244307 0.000525 0.943799 -0.133721 0.007545 0.146148
491 | 15514503200751 -0.220343 0.231385 -0.000559 0.947581 -0.118032 0.002024 0.133705
492 | 15514536387612 -0.219137 0.225227 -0.001468 0.949342 -0.107923 -0.001273 0.124499
493 | 15514586344887 -0.220065 0.220356 -0.001997 0.950269 -0.091857 -0.007015 0.112861
494 | 15514636207817 -0.218129 0.215345 -0.000933 0.951864 -0.076689 -0.011603 0.101494
495 | 15514669502495 -0.217354 0.213862 0.000646 0.952376 -0.066244 -0.014431 0.094462
496 | 15514702827690 -0.216164 0.211744 0.002899 0.953115 -0.055754 -0.016795 0.087869
497 | 15514752880750 -0.213077 0.205389 0.007762 0.955172 -0.039978 -0.018196 0.080017
498 | 15514802838025 -0.208456 0.206157 0.010987 0.955994 -0.024546 -0.020053 0.072202
499 | 15514836117031 -0.204346 0.207721 0.012172 0.956528 -0.014757 -0.020444 0.066602
500 | 15514886104824 -0.200039 0.204653 0.013453 0.958082 -0.000602 -0.020889 0.059362
501 | 15514936090653 -0.196147 0.198141 0.014532 0.960237 0.012499 -0.019905 0.052515
502 | 15514969415848 -0.193945 0.192584 0.015330 0.961801 0.021787 -0.019331 0.048278
503 | 15515002710526 -0.190673 0.187377 0.015482 0.963480 0.031210 -0.018649 0.044336
504 | 15515052711189 -0.187791 0.180291 0.016701 0.965376 0.044740 -0.016483 0.038464
505 | 15515102698982 -0.183844 0.172221 0.018934 0.967565 0.060075 -0.015868 0.032642
506 | 15515135973922 -0.180686 0.165038 0.021381 0.969359 0.069401 -0.015117 0.028047
507 | 15515185992233 -0.178464 0.145993 0.027994 0.972652 0.084935 -0.015477 0.021519
508 | 15515235931040 -0.179392 0.131702 0.030875 0.974433 0.100364 -0.015425 0.015370
509 | 15515285888315 -0.183952 0.120030 0.031495 0.975070 0.116511 -0.016331 0.009585
510 | 15515319218818 -0.184496 0.113205 0.031777 0.975775 0.126256 -0.016709 0.004736
511 | 15515369176093 -0.182583 0.106409 0.032022 0.976891 0.142194 -0.016547 0.000310
512 | 15515402501288 -0.179637 0.104496 0.032704 0.977620 0.152926 -0.016212 -0.002398
513 | 15515452390308 -0.176100 0.103761 0.034927 0.978265 0.168093 -0.014559 -0.007101
514 | 15515485715503 -0.174761 0.101682 0.037058 0.978645 0.178390 -0.013408 -0.009334
515 | 15515535730428 -0.173010 0.097111 0.039510 0.979324 0.192921 -0.010867 -0.013310
516 | 15515585687703 -0.170837 0.095171 0.038409 0.979940 0.207226 -0.007893 -0.015197
517 | 15515635644979 -0.170344 0.090804 0.036192 0.980524 0.220031 -0.003862 -0.017866
518 | 15515685704551 -0.173753 0.089356 0.033121 0.980168 0.234174 -0.000930 -0.019872
519 | 15515719029747 -0.175513 0.082457 0.031776 0.980503 0.243946 0.000330 -0.021597
520 | 15515769030677 -0.173200 0.062415 0.033063 0.982351 0.258666 0.001425 -0.024845
521 | 15515802355872 -0.171235 0.049735 0.034446 0.983371 0.268540 0.001162 -0.026747
522 | 15515852502413 -0.174358 0.034911 0.035143 0.983436 0.282775 0.001147 -0.031227
523 | 15515885797091 -0.176157 0.029404 0.034578 0.983315 0.292409 0.001134 -0.033296
524 | 15515952320543 -0.181850 0.020250 0.033327 0.982553 0.310600 0.002682 -0.038302
525 | 15515985645738 -0.182496 0.016127 0.032330 0.982542 0.319539 0.003719 -0.040433
526 | 15516035680292 -0.182172 0.009906 0.032778 0.982670 0.332020 0.005955 -0.043856
527 | 15516069005488 -0.180564 0.004246 0.033146 0.982995 0.340366 0.007537 -0.045573
528 | 15516118962763 -0.176743 -0.003999 0.034023 0.983661 0.352380 0.010335 -0.047262
529 | 15516152253176 -0.172002 -0.006658 0.035000 0.984452 0.359813 0.012912 -0.048506
530 | 15516202240969 -0.169325 -0.011485 0.036384 0.984821 0.371349 0.016085 -0.049231
531 | 15516252081517 -0.167186 -0.010581 0.035083 0.985244 0.383428 0.019459 -0.048586
532 | 15516285376195 -0.165576 -0.008031 0.033958 0.985579 0.391135 0.021541 -0.047930
533 | 15516335376616 -0.162765 -0.005067 0.032165 0.986127 0.401567 0.024888 -0.047255
534 | 15516385333892 -0.156854 -0.002285 0.030005 0.987163 0.411688 0.027363 -0.045328
535 | 15516418628570 -0.154690 0.000285 0.029149 0.987533 0.418003 0.028528 -0.043869
536 | 15516451938705 -0.152988 0.004183 0.028894 0.987797 0.423066 0.030587 -0.043355
537 | 15516501895980 -0.149878 0.015979 0.028398 0.988167 0.429533 0.032944 -0.039510
538 | 15516535169164 -0.149236 0.020652 0.028417 0.988177 0.432075 0.034821 -0.037467
539 | 15516585126439 -0.148525 0.021833 0.028177 0.988266 0.434339 0.036547 -0.033986
540 | 15516635067885 -0.143885 0.019593 0.026292 0.989051 0.432857 0.037324 -0.032253
541 | 15516685055678 -0.138409 0.009776 0.024924 0.990013 0.428346 0.035623 -0.032398
542 | 15516734916920 -0.141450 -0.000620 0.025784 0.989609 0.421449 0.033108 -0.035818
543 | 15516768211598 -0.147070 -0.000906 0.027028 0.988756 0.415041 0.030461 -0.037259
544 | 15516818199391 -0.152993 0.005583 0.028625 0.987797 0.403008 0.026043 -0.038348
545 | 15516851351772 -0.157123 0.005134 0.031367 0.987067 0.393274 0.023722 -0.039478
546 | 15516901370082 -0.156925 0.002717 0.037826 0.986882 0.376557 0.018782 -0.039567
547 | 15516934643130 -0.156201 0.002192 0.043402 0.986769 0.363910 0.016018 -0.039821
548 | 15516984600405 -0.160596 0.015142 0.050050 0.985634 0.344069 0.011110 -0.038609
549 | 15517017925601 -0.161163 0.025697 0.051824 0.985231 0.329829 0.008150 -0.037817
550 | 15517067784722 -0.162524 0.035720 0.052772 0.984645 0.306829 0.005678 -0.037999
551 | 15517101079400 -0.162955 0.042529 0.052978 0.984292 0.291132 0.004187 -0.037217
552 | 15517151118723 -0.156371 0.048589 0.051300 0.985168 0.266612 0.003756 -0.036747
553 | 15517184443918 -0.155382 0.053522 0.050273 0.985122 0.250064 0.003408 -0.035942
554 | 15517234290068 -0.155861 0.063006 0.047191 0.984637 0.225019 0.003248 -0.033523
555 | 15517284277861 -0.155565 0.068155 0.042810 0.984541 0.200225 0.003507 -0.030865
556 | 15517317572539 -0.155382 0.074103 0.039457 0.984281 0.183956 0.003798 -0.028173
557 | 15517350925003 -0.154934 0.077035 0.036177 0.984252 0.167625 0.005173 -0.025974
558 | 15517400882279 -0.153936 0.081343 0.032500 0.984190 0.143927 0.006157 -0.021169
559 | 15517434189525 -0.153413 0.086972 0.029752 0.983877 0.128543 0.007698 -0.017613
560 | 15517484177318 -0.155761 0.093318 0.025516 0.983046 0.106045 0.008523 -0.011452
561 | 15517517502513 -0.158206 0.098928 0.022370 0.982183 0.091632 0.008698 -0.006953
562 | 15517567545560 -0.162642 0.108347 0.016708 0.980576 0.070759 0.009405 0.000516
563 | 15517600840238 -0.162039 0.112628 0.012193 0.980260 0.057164 0.009292 0.005430
564 | 15517650700565 -0.162739 0.122392 0.007301 0.979021 0.037120 0.009153 0.013707
565 | 15517700688358 -0.163809 0.132125 0.004001 0.977596 0.017722 0.008351 0.022345
566 | 15517734008080 -0.166333 0.138329 0.002493 0.976316 0.005177 0.008364 0.027828
567 | 15517783995873 -0.170961 0.151964 0.001988 0.973486 -0.012409 0.007972 0.036543
568 | 15517834068010 -0.175001 0.157205 0.004074 0.971928 -0.030326 0.007883 0.044605
569 | 15517867362687 -0.177053 0.162265 0.005744 0.970716 -0.041249 0.007274 0.050282
570 | 15517900687883 -0.179137 0.166170 0.007519 0.969660 -0.051899 0.006252 0.056203
571 | 15517950703409 -0.186667 0.169631 0.010995 0.967605 -0.067926 0.004527 0.063910
572 | 15518000660684 -0.195552 0.179196 0.015392 0.964060 -0.082204 0.001646 0.075081
573 | 15518050680531 -0.202924 0.186539 0.020452 0.961044 -0.096219 -0.001340 0.086492
574 | 15518100637806 -0.204881 0.187493 0.022965 0.960387 -0.111197 -0.004732 0.098567
575 | 15518133915783 -0.207499 0.187719 0.024986 0.959730 -0.121214 -0.006269 0.105258
576 | 15518183903576 -0.210544 0.189598 0.026595 0.958653 -0.135744 -0.008394 0.116002
577 | 15518233851815 -0.212933 0.194073 0.028355 0.957179 -0.149934 -0.009547 0.125192
578 | 15518267177011 -0.214318 0.197074 0.030259 0.956198 -0.159078 -0.009933 0.131755
579 | 15518317164804 -0.215574 0.202410 0.032548 0.954724 -0.172311 -0.010052 0.141369
580 | 15518367179603 -0.216436 0.208094 0.034085 0.953253 -0.186189 -0.009142 0.148907
581 | 15518400504798 -0.217130 0.211300 0.035047 0.952354 -0.194954 -0.008427 0.154553
582 | 15518450427955 -0.217243 0.216500 0.036172 0.951118 -0.207987 -0.006376 0.161641
583 | 15518483722633 -0.217695 0.218544 0.036767 0.950524 -0.217059 -0.005322 0.166833
584 | 15518533752509 -0.216013 0.220640 0.036263 0.950443 -0.231220 -0.003297 0.173721
585 | 15518567047186 -0.215397 0.221466 0.036078 0.950397 -0.240354 -0.001932 0.178792
586 | 15518617034979 -0.213304 0.222825 0.033513 0.950646 -0.253962 0.000327 0.186631
587 | 15518650279928 -0.211332 0.222502 0.030239 0.951271 -0.263509 0.002470 0.190343
588 | 15518700237203 -0.212227 0.225655 0.025418 0.950470 -0.276534 0.005269 0.197407
589 | 15518733567341 -0.212924 0.229904 0.022376 0.949372 -0.284915 0.008056 0.200460
590 | 15518783524617 -0.215473 0.238127 0.019214 0.946836 -0.296660 0.011579 0.206847
591 | 15518816807130 -0.216146 0.242592 0.018011 0.945572 -0.304057 0.014603 0.210863
592 | 15518866794923 -0.216655 0.246347 0.016949 0.944503 -0.314617 0.018549 0.217465
593 | 15518916782716 -0.214662 0.248432 0.014391 0.944454 -0.324098 0.022329 0.224565
594 | 15518950153989 -0.214742 0.250566 0.012340 0.943902 -0.330329 0.025384 0.228407
595 | 15518983448666 -0.213554 0.250616 0.009808 0.944188 -0.336056 0.028129 0.232673
596 | 15519016697687 -0.213148 0.250758 0.006979 0.944266 -0.341222 0.031510 0.236074
597 | 15519066685480 -0.213570 0.251402 0.003167 0.944021 -0.348585 0.035471 0.241887
598 | 15519099980158 -0.210925 0.245229 0.000248 0.946241 -0.353527 0.037318 0.245232
599 | 15519133285430 -0.209832 0.239282 -0.002519 0.948002 -0.357707 0.038738 0.247409
600 | 15519166580108 -0.210986 0.236067 -0.004603 0.948544 -0.360273 0.038904 0.249906
601 | 15519216598418 -0.208514 0.231828 -0.008517 0.950108 -0.360575 0.038067 0.252329
602 | 15519249897580 -0.208676 0.230781 -0.010234 0.950310 -0.358934 0.037442 0.251111
603 | 15519283192258 -0.210110 0.230767 -0.011328 0.949985 -0.356002 0.035468 0.249391
604 | 15519316517453 -0.211535 0.230252 -0.011539 0.949791 -0.352042 0.033074 0.246351
605 | 15519366443775 -0.213471 0.226965 -0.009990 0.950167 -0.344554 0.030385 0.241102
606 | 15519416431568 -0.215809 0.223565 -0.009405 0.950451 -0.332984 0.026079 0.232082
607 | 15519449684456 -0.216368 0.219911 -0.009756 0.951172 -0.324071 0.023511 0.224580
608 | 15519483040169 -0.218190 0.220342 -0.010465 0.950649 -0.313393 0.019958 0.216207
609 | 15519532997445 -0.221928 0.223041 -0.009529 0.949163 -0.294991 0.014597 0.203957
610 | 15519582989432 -0.217426 0.213163 -0.005557 0.952500 -0.276192 0.010291 0.191377
611 | 15519616314627 -0.216199 0.212412 -0.002602 0.952960 -0.262535 0.007551 0.183599
612 | 15519649647047 -0.213246 0.211500 -0.000051 0.953831 -0.248380 0.005844 0.176157
613 | 15519699634840 -0.206997 0.206394 0.005095 0.956309 -0.227750 0.002674 0.164956
614 | 15519732914683 -0.205630 0.208514 0.008379 0.956121 -0.213194 0.002275 0.159364
615 | 15519782902476 -0.200335 0.211053 0.011821 0.956652 -0.192347 0.000679 0.149817
616 | 15519816227671 -0.198408 0.213269 0.015037 0.956517 -0.178404 -0.000278 0.143817
617 | 15519849674721 -0.199515 0.209162 0.020376 0.957094 -0.165625 -0.000299 0.138070
618 | 15519899662514 -0.196308 0.201235 0.020620 0.959449 -0.145649 -0.002565 0.128735
619 | 15519932909564 -0.193516 0.199218 0.017766 0.960494 -0.132285 -0.003363 0.123082
620 | 15519982866840 -0.194789 0.188999 0.018174 0.962292 -0.111198 -0.006064 0.114121
621 | 15520016192035 -0.196322 0.184792 0.018661 0.962788 -0.096644 -0.007897 0.108776
622 | 15520066110653 -0.196008 0.183891 0.017965 0.963038 -0.074822 -0.008956 0.101363
623 | 15520099435848 -0.195373 0.180412 0.018785 0.963809 -0.060079 -0.010036 0.097250
624 | 15520149353617 -0.192491 0.169928 0.020810 0.966250 -0.039609 -0.010395 0.090971
625 | 15520182648295 -0.190220 0.163706 0.021946 0.967747 -0.026233 -0.010781 0.087630
626 | 15520216041251 -0.188499 0.159187 0.022042 0.968835 -0.013873 -0.010191 0.084162
627 | 15520265998526 -0.185691 0.152524 0.020960 0.970472 0.004056 -0.009959 0.080238
628 | 15520299323722 -0.184208 0.147690 0.019747 0.971527 0.015563 -0.009678 0.078033
629 | 15520349385935 -0.180816 0.137209 0.017438 0.973743 0.031491 -0.008172 0.074116
630 | 15520382680612 -0.177943 0.131050 0.014782 0.975163 0.042212 -0.007512 0.072182
631 | 15520432612231 -0.175906 0.124123 0.010474 0.976494 0.058038 -0.005209 0.069098
632 | 15520465906909 -0.175321 0.120392 0.007516 0.977093 0.067727 -0.004431 0.067200
633 | 15520499232104 -0.174296 0.117396 0.004832 0.977658 0.076678 -0.003743 0.065403
634 | 15520549151880 -0.172000 0.112420 0.002773 0.978657 0.088463 -0.001587 0.062391
635 | 15520582446557 -0.172257 0.108099 0.002672 0.979099 0.095560 -0.000526 0.060811
636 | 15520615802270 -0.171643 0.104723 0.002045 0.979575 0.101981 0.000803 0.059723
637 | 15520665760843 -0.170735 0.100063 0.000017 0.980223 0.110157 0.004164 0.057037
638 | 15520699086038 -0.172005 0.098280 -0.001082 0.980181 0.114801 0.005976 0.056385
639 | 15520732383069 -0.172579 0.097942 -0.002733 0.980110 0.118859 0.008264 0.055372
640 | 15520782340344 -0.171426 0.094244 -0.005455 0.980664 0.123435 0.010753 0.055085
641 | 15520832326648 -0.170256 0.090965 -0.006217 0.981173 0.126109 0.013697 0.054974
642 | 15520865621326 -0.169717 0.090939 -0.005860 0.981271 0.127702 0.014713 0.055099
643 | 15520915639636 -0.165199 0.090848 -0.003811 0.982060 0.129338 0.015775 0.055694
644 | 15520965590813 -0.163360 0.097407 -0.001658 0.981745 0.130145 0.017761 0.056449
645 | 15521015548088 -0.162163 0.106599 0.000039 0.980989 0.130526 0.018602 0.057837
646 | 15521065455864 -0.160999 0.114723 0.000667 0.980264 0.129692 0.019962 0.058099
647 | 15521098781059 -0.161462 0.117860 0.000912 0.979815 0.129432 0.019977 0.058546
648 | 15521148781864 -0.162548 0.121773 0.001071 0.979157 0.128383 0.020514 0.058171
649 | 15521198769657 -0.163221 0.125148 0.001252 0.978619 0.127580 0.019919 0.058648
650 | 15521232133266 -0.164223 0.126415 0.001564 0.978288 0.126964 0.020225 0.058637
651 | 15521282090541 -0.164172 0.126173 0.002489 0.978326 0.126137 0.019828 0.059087
652 | 15521315415736 -0.164264 0.126057 0.003063 0.978324 0.125613 0.019508 0.059535
653 | 15521365441870 -0.164692 0.126006 0.003754 0.978256 0.124728 0.020129 0.059854
654 | 15521415399145 -0.165359 0.125588 0.004315 0.978195 0.123954 0.019756 0.060182
655 | 15521465395462 -0.167377 0.126215 0.004787 0.977769 0.123057 0.020012 0.059110
656 | 15521515383255 -0.170182 0.128791 0.005080 0.976947 0.122657 0.019105 0.058919
657 | 15521565336432 -0.171870 0.129843 0.005310 0.976511 0.122121 0.019044 0.057776
658 | 15521615294055 -0.172693 0.130250 0.005260 0.976312 0.121935 0.019017 0.057152
659 | 15521665281848 -0.172195 0.131575 0.004796 0.976224 0.122060 0.018957 0.057401
660 | 15521698576525 -0.172067 0.132549 0.004799 0.976115 0.122198 0.018890 0.057787
661 | 15521748549095 -0.171246 0.133122 0.004647 0.976182 0.122300 0.019221 0.057754
662 | 15521798536888 -0.171124 0.134072 0.004307 0.976075 0.122555 0.019030 0.058278
663 | 15521848488672 -0.171074 0.134648 0.003999 0.976006 0.122521 0.019254 0.058007
664 | 15521898445947 -0.171084 0.135609 0.003323 0.975874 0.122745 0.018894 0.058444
665 | 15521948434283 -0.170878 0.137324 0.002364 0.975673 0.122727 0.019224 0.058214
666 | 15521981728961 -0.171246 0.138263 0.001811 0.975477 0.122870 0.019174 0.058438
667 | 15522031686236 -0.169763 0.139354 0.000558 0.975582 0.123165 0.019062 0.059070
668 |
--------------------------------------------------------------------------------
/Visualization/FigureRotator.m:
--------------------------------------------------------------------------------
1 | classdef FigureRotator < handle
2 |
3 | % FigureRotator
4 | %
5 | % Apply this to a figure to use a mouse to rotate around a target position
6 | % and zoom in and out. This is similar to the Rotate 3D capability found in
7 | % the standard figure menu, but this allows greater flexibility and
8 | % fluidity of movement. An example is provided in example_figure_rotator.m,
9 | % and additional examples are provided below.
10 | %
11 | % Left-click and drag to rotate about the target.
12 | % Scroll the mouse wheel to move towards/away from the target.
13 | % Right-click and drag to zoom the camera in/out, changing the view angle.
14 | % Double-click to reset the "up" vector.
15 | % Press 'r' to reset the view to what it was when the FigureRotator started.
16 | %
17 | % Example:
18 | %
19 | % figure();
20 | % plot3(randn(1, 10), randn(1, 10), randn(1, 10));
21 | % drawnow();
22 | % f = FigureRotator(gca);
23 | %
24 | % The FigureRotator can later be stopped by calling the Stop() function.
25 | %
26 | % f.Stop();
27 | %
28 | % It's often helpful to specify the initial camera parameters, like
29 | % position, target, up vector, and view angle. These can all be passed to
30 | % the constructor. (These are all properties of axes objects in MATLAB. See
31 | % 'axes properties' in the documentation for more.)
32 | %
33 | % f = FigureRotator(gca, 'CameraTarget', [0 0 0], ...
34 | % 'CameraPosition', [15 0 0], ...
35 | % 'CameraUpVector', [0 0 1], ...
36 | % 'CameraViewAngle', 60);
37 | %
38 | % The FigureRotator allows complete 3D rotation, so if you start losing
39 | % track of "up", you can always re-align the camera's up vector with the
40 | % axes' [0 0 1] by calling RestoreUp().
41 | %
42 | % You can also set the up vector with SetUpVector(). This take two arguments.
43 | % The first is the 3D up vector, e.g., [0 0 1]. The second says whether "up"
44 | % should always remain "up" (constrainted rotation).
45 | %
46 | % % Look at peaks sideways.
47 | % peaks();
48 | % f = FigureRotator();
49 | % f.SetUpVector([0 1 0]);
50 | %
51 | % Now try rotating and then double-clicking. Note how double-clicking realigns
52 | % the y axis with up.
53 | %
54 | % Now try:
55 | %
56 | % f.SetUpVector([0 1 0], true); % Keep "up" pointing up.
57 | %
58 | % Note how it now rotates about the y axis.
59 | %
60 | % This object uses the figure's WindowButtonUpFcn, WindowButtonDownFcn,
61 | % WindowButtonMotionFcn, WindowScrollWheelFcn, and KeyPressFcn callbacks. If
62 | % those are necessary for other tasks as well, callbacks can be attached to the
63 | % FigureRotator, which will pass all arguments on to the provided callback
64 | % function.
65 | %
66 | % Example:
67 | %
68 | % f = FigureRotator(gca);
69 | % f.AttachCallback('WindowButtonDownFcn', 'disp(''clicked'');');
70 | %
71 | % Or multiple callbacks can be set with a single call:
72 | %
73 | % f.AttachCallback('WindowButtonDownFcn', 'disp(''down'');', ...
74 | % 'WindowButtonUpFcn', 'disp(''up'');', ...
75 | % 'WindowButtonMotionFcn', 'disp(''moving'');', ...
76 | % 'WindowScrollWheelFcn', @(~, ~) disp('scrolling'), ...
77 | % 'KeyPressFcn', 'disp(''key'');');
78 | %
79 | % A single FigureRotator can control multiple axes, even axes across
80 | % multiple figures.
81 | %
82 | % Example:
83 | %
84 | % figure(1);
85 | % clf();
86 | % ha1 = subplot(2, 1, 1);
87 | % peaks;
88 | % ha2 = subplot(2, 1, 2);
89 | % peaks;
90 | %
91 | % figure(2);
92 | % clf();
93 | % peaks;
94 | % ha3 = gca();
95 | %
96 | % f = FigureRotator([ha1 ha2 ha3]);
97 | %
98 | % The FigureRotator now controls all three figures -- useful for keeping the
99 | % same perspective across multiple objects.
100 | %
101 | % --- Change Log ---
102 | %
103 | % 2014-09-11: Updated for R2014B graphics. Change copyright 2014
104 | % Tucker McClure.
105 | %
106 | % 2014-03-26: Allows view to be reset with 'r', has improved handling of
107 | % callbacks for multiple axes, more examples of using up vector, and more
108 | % comments. Change copyright 2014 Tucker McClure.
109 | %
110 | % Original. Copyrirght 2012, The MathWorks, Inc.
111 | %
112 | % ---
113 | %
114 | % Copyright 2014, The MathWorks, Inc. and Tucker McClure
115 |
116 | properties
117 |
118 | % Figure and axes handles
119 | h_f;
120 | h_a;
121 |
122 | % Current states
123 | rotating = false;
124 | zooming = false;
125 |
126 | % Mouse positions
127 | rotate_start_point = [0 0]; % Mouse position on button down
128 | zoom_start_point = [0 0]; % Mouse position on button down
129 |
130 | wbdf; % Pass-through WindowButtonDownFcn
131 | wbuf; % Pass-through WindowButtonUpFcn
132 | wbmf; % Pass-through WindowButtonMotionFcn
133 | wswf; % Pass-through WindowScrollWheelFcn
134 | kpf; % Pass-through KeyPressFcn
135 |
136 | keep_up = false; % True iff we should always restore up after movement
137 | up = [0; 0; 1]; % Up vector
138 |
139 | is_stopped = false; % True iff we should no longer control the axes.
140 |
141 | % Original states
142 | original;
143 |
144 | end
145 |
146 | methods
147 |
148 | % Construct a FigureRotator for the given axes.
149 | function o = FigureRotator(axes_handle, varargin)
150 |
151 | % Use gca if none is given.
152 | if nargin == 0
153 | axes_handle = gca();
154 | end
155 |
156 | % Record the axes and figure.
157 | o.h_a = axes_handle;
158 | o.h_f = get(axes_handle, 'Parent');
159 | if iscell(o.h_f)
160 | o.h_f = [o.h_f{:}];
161 | end
162 |
163 | % Pass any arguments on to the axes object.
164 | if nargin >= 2
165 | set(o.h_a, varargin{:});
166 | end
167 |
168 | % Get the original view positions.
169 | o.original.position = get(o.h_a, 'CameraPosition');
170 | o.original.target = get(o.h_a, 'CameraTarget');
171 | o.original.up = get(o.h_a, 'CameraUpVector');
172 | o.original.view_angle = get(o.h_a, 'CameraViewAngle');
173 |
174 | % Save the original up vector as the current up vector.
175 | o.up = o.original.up;
176 |
177 | % Set the figure callbacks to register with this object.
178 | set(o.h_f, ...
179 | 'WindowButtonDownFcn', @o.ButtonDown, ...
180 | 'WindowButtonUpFcn', @o.ButtonUp, ...
181 | 'WindowButtonMotionFcn', @o.Move, ...
182 | 'WindowScrollWheelFcn', @o.Wheel, ...
183 | 'KeyPressFcn', @o.Key);
184 |
185 | % Set up the axes object for what we need. We get the last
186 | % word.
187 | set(o.h_a, ...
188 | 'CameraPositionMode', 'manual', ...
189 | 'CameraTargetMode', 'manual', ...
190 | 'CameraUpVectorMode', 'manual', ...
191 | 'CameraViewAngleMode', 'manual', ...
192 | 'XLimMode', 'manual', ...
193 | 'YLimMode', 'manual', ...
194 | 'ZLimMode', 'manual', ...
195 | 'DataAspectRatioMode', 'manual');
196 |
197 | end
198 |
199 | % Called when a user clicks
200 | function ButtonDown(o, h, event, varargin)
201 |
202 | % If the user is clicking in the figure, but not on one of our axes,
203 | % ignore it.
204 | if ~any(ishandle(o.h_a)) || ~any(gca() == o.h_a)
205 | return;
206 | end
207 |
208 | % Get the button type.
209 | switch get(h, 'SelectionType')
210 |
211 | % Rotate around.
212 | case {'normal', 'extend'}
213 |
214 | % Record the starting point and that we're rotating.
215 | o.rotate_start_point = get(h, 'CurrentPoint');
216 | o.rotating = true;
217 |
218 | % Zoom.
219 | case 'alt'
220 |
221 | % Record the starting point and that we're zooming.
222 | o.zoom_start_point = get(h, 'CurrentPoint');
223 | o.zooming = true;
224 |
225 | % When double-clicking, restore up.
226 | case 'open'
227 |
228 | o.RestoreUp();
229 |
230 | end
231 |
232 | % If there's a callback attachment, execute it.
233 | execute_callback(o.wbdf, h, event, varargin{:});
234 |
235 | end
236 |
237 | % Called when user releases a click
238 | function ButtonUp(o, h, event, varargin)
239 |
240 | % If the user is clicking in the figure, but not on one of our axes,
241 | % ignore it.
242 | if ~any(ishandle(o.h_a)) || ~any(gca() == o.h_a)
243 | return;
244 | end
245 |
246 | % Get the button type.
247 | switch get(h, 'SelectionType')
248 |
249 | % Stop rotating.
250 | case {'normal', 'extend'}
251 | o.rotating = false;
252 |
253 | % Stop zooming.
254 | case 'alt'
255 | o.zooming = false;
256 |
257 | end
258 |
259 | % If there's a callback attachment, execute it.
260 | execute_callback(o.wbuf, h, event, varargin{:});
261 |
262 | end
263 |
264 | % Called when mouse moves in figure
265 | function Move(o, h, event, varargin)
266 |
267 | % If the user is clicking in the figure, but not on one of our axes,
268 | % ignore it.
269 | if ~any(ishandle(o.h_a)) || ~any(gca() == o.h_a)
270 | return;
271 | end
272 |
273 | if o.rotating
274 |
275 | % Get the mouse position in the window.
276 | s = feval(@(x) x(3:4), get(h, 'Position'));
277 | p = get(h, 'CurrentPoint');
278 | r = (p - o.rotate_start_point)./s;
279 |
280 | % Get the current state wrt the target and frame.
281 | dar = get(gca(), 'DataAspectRatio')';
282 | r_t0 = get(gca(), 'CameraTarget')' ./ dar;
283 | r_c0 = get(gca(), 'CameraPosition')' ./ dar;
284 | up_hat = get(gca(), 'CameraUpVector')' ./ dar;
285 | r_tc = r_t0 - r_c0;
286 | r_tc_hat = normalize(r_tc);
287 |
288 | % Correct up.
289 | up_hat = normalize(up_hat - r_tc_hat'*up_hat*r_tc_hat);
290 |
291 | % Find "right" (this will be a unit vector since r_tc_hat
292 | % and up_hat are orthonormal).
293 | right_hat = cross(r_tc_hat, up_hat);
294 |
295 | % Calculate where the mouse is in the axes space from its
296 | % location in the 2D figure window.
297 | r_mc = r(2) * up_hat + r(1) * right_hat;
298 |
299 | % Calculate the rotation axis.
300 | a_hat = normalize(cross(r_tc - r_mc, r_tc));
301 |
302 | % Calculate the rotation matrix.
303 | Q = aa2dcm(a_hat, norm(r_mc)*pi);
304 |
305 | % Calculate the new camera position, accounting for
306 | % non-equal aspect ratios.
307 | r_n0 = -Q*r_tc + r_t0;
308 |
309 | % Update the relevant quantities.
310 | for k = 1:length(o.h_a)
311 | if ishandle(o.h_a(k))
312 | set(o.h_a(k), 'CameraPosition', r_n0 .* dar, ...
313 | 'CameraUpVector', Q*up_hat .* dar);
314 | end
315 | end
316 |
317 | % If up should stay up at all time, restore it.
318 | if o.keep_up
319 | o.RestoreUp();
320 | end
321 |
322 | % Update the "last" accounted point.
323 | o.rotate_start_point = p;
324 |
325 | end
326 |
327 | if o.zooming
328 |
329 | % Get the starting view angle.
330 | view_angle = get(gca(), 'CameraViewAngle');
331 |
332 | % Get the mouse position in the window.
333 | s = feval(@(x) x(4), get(h, 'Position'));
334 | p = get(h, 'CurrentPoint');
335 | r = -(p(2) - o.zoom_start_point(2))/s;
336 | new_view_angle = min(2^r*view_angle, 180-eps);
337 |
338 | for k = 1:length(o.h_a)
339 | if ishandle(o.h_a(k))
340 | set(o.h_a(k), 'CameraViewAngle', new_view_angle);
341 | end
342 | end
343 |
344 | % Update the "last" accounted point.
345 | o.zoom_start_point = p;
346 |
347 | end
348 |
349 | % If there's a callback attachment, execute it.
350 | execute_callback(o.wbmf, h, event, varargin{:});
351 |
352 | end
353 |
354 | % Called for scroll wheel
355 | function Wheel(o, h, event, varargin)
356 |
357 | % If the user is clicking in the figure, but not on one of our axes,
358 | % ignore it.
359 | if ~any(ishandle(o.h_a)) || ~any(gca() == o.h_a)
360 | return;
361 | end
362 |
363 | % Scalar to increase/decrease distance to target.
364 | s = 1.2^double(event.VerticalScrollCount);
365 |
366 | % Update what we're currently seeing by the appropriate amount.
367 | t0 = get(gca(), 'CameraTarget');
368 | c0 = get(gca(), 'CameraPosition');
369 | r_n0 = s * (c0 - t0) + t0;
370 |
371 | for k = 1:length(o.h_a)
372 | if ishandle(o.h_a(k))
373 | set(o.h_a(k), 'CameraPosition', r_n0);
374 | end
375 | end
376 |
377 | % If there's a callback attachment, execute it.
378 | execute_callback(o.wswf, h, event, varargin{:});
379 |
380 | end
381 |
382 | % Called when a key is pressed in the figure.
383 | function Key(o, h, event, varargin)
384 |
385 | % If the user is clicking in the figure, but not on one of our axes,
386 | % ignore it.
387 | if ~any(ishandle(o.h_a)) || ~any(gca() == o.h_a)
388 | return;
389 | end
390 |
391 | % See which key.
392 | switch event.Key
393 |
394 | % If 'r', let's reset the view.
395 | case 'r'
396 | if length(o.h_a) == 1
397 | if ishandle(o.h_a)
398 | set(o.h_a, ...
399 | 'CameraTarget', o.original.target, ...
400 | 'CameraPosition', o.original.position, ...
401 | 'CameraUpVector', o.original.up, ...
402 | 'CameraViewAngle', o.original.view_angle);
403 | end
404 | else
405 | for k = 1:length(o.h_a)
406 | if ishandle(o.h_a(k))
407 | set(o.h_a(k), ...
408 | 'CameraTarget', o.original.target{k}, ...
409 | 'CameraPosition', o.original.position{k}, ...
410 | 'CameraUpVector', o.original.up{k}, ...
411 | 'CameraViewAngle', o.original.view_angle{k});
412 | end
413 | end
414 | end
415 |
416 | end
417 |
418 | % If there's a callback, execute it.
419 | execute_callback(o.kpf, h, event, varargin{:});
420 |
421 | end
422 |
423 | % Sometime users like to return "up" to [0 0 1], so we'll give them
424 | % a function to call.
425 | function RestoreUp(o)
426 | if iscell(o.up)
427 | for k = 1:length(o.up)
428 | if ishandle(o.h_a(k))
429 | set(o.h_a(k), 'CameraUpVector', o.up{k});
430 | end
431 | end
432 | else
433 | for k = 1:length(o.h_a)
434 | if ishandle(o.h_a(k))
435 | set(o.h_a(k), 'CameraUpVector', o.up);
436 | end
437 | end
438 | end
439 | end
440 |
441 | % Add a pass-through callback for one of the callbacks
442 | % FigureRotator hogs to itself. This way, a user can still get all
443 | % the info he needs from a figure's callbacks *and* use the
444 | % rotator.
445 | function AttachCallback(o, varargin)
446 |
447 | for k = 2:2:length(varargin)
448 | switch varargin{k-1}
449 | case 'WindowButtonDownFcn'
450 | o.wbdf = varargin{k};
451 | case 'WindowButtonUpFcn'
452 | o.wbuf = varargin{k};
453 | case 'WindowButtonMotionFcn'
454 | o.wbmf = varargin{k};
455 | case 'WindowScrollWheelFcn'
456 | o.wswf = varargin{k};
457 | case 'KeyPressFcn'
458 | o.kpf = varargin{k};
459 | otherwise
460 | warning('Invalid callback attachment.');
461 | end
462 | end
463 |
464 | end
465 |
466 | % Allow the user to specify that up should always be up and to
467 | % specify what up is.
468 | function SetUpVector(o, up, on)
469 | o.up = up;
470 | if nargin >= 3
471 | o.keep_up = logical(on);
472 | end
473 | o.RestoreUp();
474 | end
475 |
476 | % We're done. Get rid of the callbacks. If there were pass-through
477 | % callbacks, replace our callbacks with those.
478 | function Stop(o)
479 | o.is_stopped = true;
480 | for k = 1:length(o.h_f)
481 | if ishandle(o.h_f(k))
482 | set(o.h_f(k), ...
483 | 'WindowButtonDownFcn', o.wbdf, ...
484 | 'WindowButtonUpFcn', o.wbuf, ...
485 | 'WindowButtonMotionFcn', o.wbmf, ...
486 | 'WindowScrollWheelFcn', o.wswf, ...
487 | 'KeyPressFcn', o.kpf);
488 | end
489 | end
490 | end
491 |
492 | end
493 |
494 | end
495 |
496 | % Safely normalize an input vector.
497 | function x_hat = normalize(x)
498 | n = norm(x);
499 | if n > eps
500 | x_hat = x/n;
501 | else
502 | x_hat = x;
503 | end
504 | end
505 |
506 | % Convert the specified axis and angle of rotation to a direction cosine
507 | % matrix.
508 | function M = aa2dcm(ax, an)
509 | M = eye(3)*cos(an) + (1-cos(an))*(ax*ax') + crs(ax)*sin(an);
510 | end
511 |
512 | % Returns a skew-symmetric "cross product" matrix from 3-by-1 vector, v,
513 | % such that cross(v, b) == crs(v)*b.
514 | function M = crs(v)
515 | M = [ 0 -v(3) v(2); ...
516 | v(3) 0 -v(1); ...
517 | -v(2) v(1) 0];
518 | end
519 |
520 | % Execute whatever callback was requested.
521 | function execute_callback(cb, h, event, varargin)
522 |
523 | % If there's anything here...
524 | if ~isempty(cb)
525 |
526 | % If might be a regular function handle. If so, just pass along the
527 | % handle and event.
528 | if isa(cb, 'function_handle')
529 |
530 | cb(h, event);
531 |
532 | % If it's a cell array, it should contain a function handle and
533 | % additional arguments.
534 | elseif iscell(cb)
535 |
536 | cb(h, event, varargin{:});
537 |
538 | % Otherwise, if it's text, evaluate it.
539 | elseif ischar(cb) && ~isempty(cb)
540 |
541 | eval(cb);
542 |
543 | % Otherwise, we don't know what to do.
544 | else
545 |
546 | error('FigureRotator:InvalidCallback', ...
547 | 'Invalid figure callback in FigureRotator.');
548 |
549 | end
550 |
551 | end
552 |
553 | end
554 |
555 |
556 |
--------------------------------------------------------------------------------
/Visualization/angle2rotmtx.m:
--------------------------------------------------------------------------------
1 | function [ R ] = angle2rotmtx(eulerAngle)
2 | % Project: Patch-based Illumination invariant Visual Odometry (PIVO)
3 | % Function: angle2rotmtx
4 | %
5 | % Description:
6 | % This function return the rotation matrix rotMtx
7 | % [Body frame] = rotMtx * [Inertial frame]
8 | % from [phi;theta;psi] angle defined as ZYX sequence to rotation matrix
9 | %
10 | % Example:
11 | % OUTPUT:
12 | % R = rotation matrix (3x3) defined as [body frame] = R * [inertial frame] (R = R_bg)
13 | %
14 | % INPUT:
15 | % eulerAngle: angle vector composed of [phi;theta;psi]
16 | % phi = Rotation angle along x direction in radians
17 | % theta = Rotation angle along y direction in radians
18 | % psi = Rotation angle along z direction in radians
19 | %
20 | % NOTE:
21 | %
22 | % Author: Pyojin Kim
23 | % Email: pjinkim1215@gmail.com
24 | % Website:
25 | %
26 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
27 | % log:
28 | % 2016-08-20:
29 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
30 | %
31 |
32 | % assign roll, pitch, yaw values
33 | phi = eulerAngle(1);
34 | theta = eulerAngle(2);
35 | psi = eulerAngle(3);
36 |
37 | R_x = [1 0 0;0 cos(phi) -sin(phi);0 sin(phi) cos(phi)];
38 |
39 | R_y = [cos(theta) 0 sin(theta);0 1 0;-sin(theta) 0 cos(theta)];
40 |
41 | R_z = [cos(psi) -sin(psi) 0; sin(psi) cos(psi) 0; 0 0 1];
42 |
43 | rotMtxBody = [R_z * R_y * R_x]; % [Inertial frame] = rotMtxBody * [Body frame]
44 |
45 | R = rotMtxBody.'; % [Body frame] = rotMtx * [Inertial frame]
46 |
47 | end
48 |
49 |
--------------------------------------------------------------------------------
/Visualization/main_script.m:
--------------------------------------------------------------------------------
1 | clc;
2 | close all;
3 | clear variables; %clear classes;
4 | rand('state',0); % rand('state',sum(100*clock));
5 | dbstop if error;
6 |
7 |
8 | %% common setting to read text files
9 |
10 | delimiter = ' ';
11 | headerlinesIn = 1;
12 | nanoSecondToSecond = 1000000000;
13 |
14 |
15 | %% 1) parse ARCore sensor pose data
16 |
17 | % parsing ARCore sensor pose data text file
18 | textFileDir = 'ARCore_sensor_pose.txt';
19 | textARCorePoseData = importdata(textFileDir, delimiter, headerlinesIn);
20 | ARCorePoseTime = textARCorePoseData.data(:,1).';
21 | ARCorePoseTime = (ARCorePoseTime - ARCorePoseTime(1)) ./ nanoSecondToSecond;
22 | ARCorePoseRotation = textARCorePoseData.data(:,[5 2 3 4]).';
23 | ARCorePoseTranslation = textARCorePoseData.data(:,[6 7 8]).';
24 |
25 | % ARCore sensor pose with various 6-DoF sensor pose representations
26 | numPose = size(ARCorePoseRotation,2);
27 | R_gb_ARCore = zeros(3,3,numPose);
28 | T_gb_ARCore = cell(1,numPose);
29 | stateEsti_ARCore = zeros(6,numPose);
30 | for k = 1:numPose
31 |
32 | % rigid body transformation matrix (4x4) (rotation matrix SO(3) from quaternion)
33 | R_gb_ARCore(:,:,k) = q2r(ARCorePoseRotation(:,k));
34 | T_gb_ARCore{k} = [R_gb_ARCore(:,:,k), ARCorePoseTranslation(:,k); [0, 0, 0, 1]];
35 |
36 | % state vector and rotation matrix
37 | stateEsti_ARCore(1:3,k) = T_gb_ARCore{k}(1:3,4);
38 | [yaw, pitch, roll] = dcm2angle(R_gb_ARCore(:,:,k));
39 | stateEsti_ARCore(4:6,k) = [roll; pitch; yaw];
40 | end
41 |
42 | % plot update rate of ARCore sensor pose
43 | timeDifference = diff(ARCorePoseTime);
44 | meanUpdateRate = (1/mean(timeDifference));
45 | figure;
46 | plot(ARCorePoseTime(2:end), timeDifference, 'm'); hold on; grid on; axis tight;
47 | set(gcf,'color','w'); hold off;
48 | axis([min(ARCorePoseTime) max(ARCorePoseTime) min(timeDifference) max(timeDifference)]);
49 | set(get(gcf,'CurrentAxes'),'FontName','Times New Roman','FontSize',17);
50 | xlabel('Time [sec]','FontName','Times New Roman','FontSize',17);
51 | ylabel('Time Difference [sec]','FontName','Times New Roman','FontSize',17);
52 | title(['Mean Update Rate: ', num2str(meanUpdateRate), ' Hz'],'FontName','Times New Roman','FontSize',17);
53 | set(gcf,'Units','pixels','Position',[100 200 1800 900]); % modify figure
54 |
55 |
56 | %% 2) parse ARCore point cloud data
57 |
58 | % parsing ARCore point cloud data text file
59 | textFileDir = 'ARCore_point_cloud.txt';
60 | textARCorePointData = importdata(textFileDir, delimiter, headerlinesIn);
61 |
62 | % ARCore 3D point cloud
63 | ARCorePoints = textARCorePointData.data(:,[1:3]).';
64 | ARCoreColors = textARCorePointData.data(:,[4:6]).';
65 | numPoints = size(ARCorePoints,2);
66 |
67 |
68 | %% plot ARCore VIO results
69 |
70 | % 1) play 3D trajectory of ARCore sensor pose
71 | figure(10);
72 | for k = 1:numPose
73 | figure(10); cla;
74 |
75 | % draw moving trajectory
76 | p_gb_ARCore = stateEsti_ARCore(1:3,1:k);
77 | plot3(p_gb_ARCore(1,:), p_gb_ARCore(2,:), p_gb_ARCore(3,:), 'm', 'LineWidth', 2); hold on; grid on; axis equal;
78 |
79 | % draw sensor body and frame
80 | plot_inertial_frame(0.5);
81 | Rgb_ARCore_current = T_gb_ARCore{k}(1:3,1:3);
82 | pgb_ARCore_current = T_gb_ARCore{k}(1:3,4);
83 | plot_sensor_ARCore_frame(Rgb_ARCore_current, pgb_ARCore_current, 0.5, 'm');
84 | refresh; pause(0.01); k
85 | end
86 |
87 |
88 | % 2) plot ARCore VIO motion estimation results
89 | figure;
90 | h_ARCore = plot3(stateEsti_ARCore(1,:),stateEsti_ARCore(2,:),stateEsti_ARCore(3,:),'m','LineWidth',2); hold on; grid on;
91 | scatter3(ARCorePoints(1,:), ARCorePoints(2,:), ARCorePoints(3,:), 50*ones(numPoints,1), (ARCoreColors ./ 255).','.');
92 | plot_inertial_frame(0.5); legend(h_ARCore,{'ARCore'}); axis equal; view(26, 73);
93 | xlabel('x [m]','fontsize',10); ylabel('y [m]','fontsize',10); zlabel('z [m]','fontsize',10); hold off;
94 |
95 | % figure options
96 | f = FigureRotator(gca());
97 |
98 |
99 | % 3) plot roll/pitch/yaw of ARCore device orientation
100 | figure;
101 | subplot(3,1,1);
102 | plot(ARCorePoseTime, stateEsti_ARCore(4,:), 'm'); hold on; grid on; axis tight;
103 | set(gcf,'color','w'); hold off;
104 | axis([min(ARCorePoseTime) max(ARCorePoseTime) min(stateEsti_ARCore(4,:)) max(stateEsti_ARCore(4,:))]);
105 | set(get(gcf,'CurrentAxes'),'FontName','Times New Roman','FontSize',17);
106 | xlabel('Time [sec]','FontName','Times New Roman','FontSize',17);
107 | ylabel('Roll [rad]','FontName','Times New Roman','FontSize',17);
108 | subplot(3,1,2);
109 | plot(ARCorePoseTime, stateEsti_ARCore(5,:), 'm'); hold on; grid on; axis tight;
110 | set(gcf,'color','w'); hold off;
111 | axis([min(ARCorePoseTime) max(ARCorePoseTime) min(stateEsti_ARCore(5,:)) max(stateEsti_ARCore(5,:))]);
112 | set(get(gcf,'CurrentAxes'),'FontName','Times New Roman','FontSize',17);
113 | xlabel('Time [sec]','FontName','Times New Roman','FontSize',17);
114 | ylabel('Pitch [rad]','FontName','Times New Roman','FontSize',17);
115 | subplot(3,1,3);
116 | plot(ARCorePoseTime, stateEsti_ARCore(6,:), 'm'); hold on; grid on; axis tight;
117 | set(gcf,'color','w'); hold off;
118 | axis([min(ARCorePoseTime) max(ARCorePoseTime) min(stateEsti_ARCore(6,:)) max(stateEsti_ARCore(6,:))]);
119 | set(get(gcf,'CurrentAxes'),'FontName','Times New Roman','FontSize',17);
120 | xlabel('Time [sec]','FontName','Times New Roman','FontSize',17);
121 | ylabel('Yaw [rad]','FontName','Times New Roman','FontSize',17);
122 | set(gcf,'Units','pixels','Position',[100 200 1800 900]); % modify figure
123 |
124 |
125 |
--------------------------------------------------------------------------------
/Visualization/plot_inertial_frame.m:
--------------------------------------------------------------------------------
1 | function plot_inertial_frame(magnitude)
2 |
3 |
4 | % center point of inertial frame
5 | X = 0;
6 | Y = 0;
7 | Z = 0;
8 |
9 |
10 | % [X Y Z] axis end points of inertial frame
11 | X_axis = [magnitude;0;0];
12 | Y_axis = [0;magnitude;0];
13 | Z_axis = [0;0;magnitude];
14 |
15 |
16 | % draw inertial frame
17 | line([X_axis(1) X],[X_axis(2) Y],[X_axis(3) Z],'Color','r','LineWidth',4)
18 | line([Y_axis(1) X],[Y_axis(2) Y],[Y_axis(3) Z],'Color','g','LineWidth',4)
19 | line([Z_axis(1) X],[Z_axis(2) Y],[Z_axis(3) Z],'Color','b','LineWidth',4)
20 |
21 |
22 | end
--------------------------------------------------------------------------------
/Visualization/plot_sensor_ARCore_frame.m:
--------------------------------------------------------------------------------
1 | function [h] = plot_sensor_ARCore_frame(R_gc, p_gc, camScale, camBodyColor)
2 | % Project: Astrobee's mapping and localization system
3 | % Function: plot_sensor_ARCore_frame
4 | %
5 | % Description:
6 | % draw current ARKit camera frame in the current figure
7 | %
8 | % Example:
9 | % OUTPUT:
10 | % h: plot handler
11 | %
12 | %
13 | % INPUT:
14 | % R_gc: rotation matrix of camera frame (T_gc(1:3,1:3))
15 | % p_gc: position vector of camera frame (T_gc(1:3,4))
16 | % camScale: scale of camera body and frame
17 | % camBodyColor: color of camera body (NOT FRAME - FRAME is RGB (XYZ))
18 | %
19 | %
20 | %
21 | % NOTE:
22 | % Copyright 2016 Intelligent Robotics Group, NASA ARC
23 | %
24 | % Author: Pyojin Kim
25 | % Email: pjinkim1215@gmail.com
26 | % Website:
27 | %
28 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
29 | % log:
30 | % 2019-06-06: ing
31 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
32 | %
33 | %
34 |
35 |
36 | % option parameters
37 | rows = 960;
38 | cols = 480;
39 |
40 |
41 | % define the camera body and frame
42 | camBody = zeros(3,4);
43 | camBody(:,1) = camScale * [(cols/2);-(rows/2);-(cols-50)] / 1000;
44 | camBody(:,2) = camScale * [-(cols/2);-(rows/2);-(cols-50)] / 1000;
45 | camBody(:,3) = camScale * [-(cols/2);(rows/2);-(cols-50)] / 1000;
46 | camBody(:,4) = camScale * [(cols/2);(rows/2);-(cols-50)] / 1000;
47 |
48 | camFrame = camScale * ((rows/2) / 1000) * eye(3);
49 |
50 |
51 | % rotate the camera body and frame from {C} to {G}
52 | camBody = R_gc * camBody;
53 | camFrame = R_gc * camFrame;
54 |
55 |
56 | % translate the camera body and frame
57 | camBody = camBody + [p_gc(1:3) p_gc(1:3) p_gc(1:3) p_gc(1:3)];
58 | camFrame = camFrame + [p_gc(1:3) p_gc(1:3) p_gc(1:3)];
59 |
60 |
61 | % draw the camera body
62 | line([camBody(1,1) p_gc(1)],[camBody(2,1) p_gc(2)],[camBody(3,1) p_gc(3)],'Color', camBodyColor, 'LineWidth', 2)
63 | line([camBody(1,2) p_gc(1)],[camBody(2,2) p_gc(2)],[camBody(3,2) p_gc(3)],'Color', camBodyColor, 'LineWidth', 2)
64 | line([camBody(1,3) p_gc(1)],[camBody(2,3) p_gc(2)],[camBody(3,3) p_gc(3)],'Color', camBodyColor, 'LineWidth', 2)
65 | line([camBody(1,4) p_gc(1)],[camBody(2,4) p_gc(2)],[camBody(3,4) p_gc(3)],'Color', camBodyColor, 'LineWidth', 2)
66 |
67 | line([camBody(1,1) camBody(1,2)],[camBody(2,1) camBody(2,2)],[camBody(3,1) camBody(3,2)],'Color', camBodyColor, 'LineWidth', 2)
68 | line([camBody(1,2) camBody(1,3)],[camBody(2,2) camBody(2,3)],[camBody(3,2) camBody(3,3)],'Color', camBodyColor, 'LineWidth', 2)
69 | line([camBody(1,3) camBody(1,4)],[camBody(2,3) camBody(2,4)],[camBody(3,3) camBody(3,4)],'Color', camBodyColor, 'LineWidth', 2)
70 | h = line([camBody(1,4) camBody(1,1)],[camBody(2,4) camBody(2,1)],[camBody(3,4) camBody(3,1)],'Color', camBodyColor, 'LineWidth', 2);
71 |
72 |
73 | % draw the camera frame
74 | line([camFrame(1,1) p_gc(1)],[camFrame(2,1) p_gc(2)],[camFrame(3,1) p_gc(3)],'Color','r','LineWidth',2)
75 | line([camFrame(1,2) p_gc(1)],[camFrame(2,2) p_gc(2)],[camFrame(3,2) p_gc(3)],'Color','g','LineWidth',2)
76 | line([camFrame(1,3) p_gc(1)],[camFrame(2,3) p_gc(2)],[camFrame(3,3) p_gc(3)],'Color','b','LineWidth',2)
77 |
78 |
79 | end
--------------------------------------------------------------------------------
/Visualization/q2r.m:
--------------------------------------------------------------------------------
1 | function R = q2r( q )
2 | % Project: Patch-based illumination-variant DVO
3 | % Function: q2r
4 | %
5 | % Description:
6 | % This function convert from unit orientation quaternion to rotation matrix
7 | %
8 | % Example:
9 | % OUTPUT:
10 | % R = rotation matrix (3x3) defined as [inertial frame] = R * [body frame] (R = R_gb)
11 | %
12 | % INPUT:
13 | % quatVector: quaternion vector composed of [qw qx qy qz]
14 | %
15 | % NOTE:
16 | %
17 | % Author: Pyojin Kim
18 | % Email: pjinkim1215@gmail.com
19 | % Website:
20 | %
21 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
22 | % log:
23 | % 2015-02-06: Complete
24 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
25 | %
26 |
27 | % quaternion to dcm
28 | q = q/norm(q);
29 |
30 | a = q(1);
31 | b = q(2);
32 | c = q(3);
33 | d = q(4);
34 | R=[ a*a+b*b-c*c-d*d, 2*(b*c-a*d), 2*(b*d+a*c);
35 | 2*(b*c+a*d), a*a-b*b+c*c-d*d, 2*(c*d-a*b);
36 | 2*(b*d-a*c), 2*(c*d+a*b), a*a-b*b-c*c+d*d; ];
37 |
38 | end
--------------------------------------------------------------------------------
/Visualization/r2q.m:
--------------------------------------------------------------------------------
1 | function [q] = r2q( R )
2 | % Project: Patch-based illumination-variant DVO
3 | % Function: r2q
4 | %
5 | % Description:
6 | % This function convert from rotation matrix to unit orientation quaternion
7 | %
8 | % Example:
9 | % OUTPUT:
10 | % quatVector: quaternion vector composed of [qw qx qy qz]
11 | %
12 | % INPUT:
13 | % R = rotation matrix (3x3) defined as [inertial frame] = R * [body frame] (R = R_gb)
14 | %
15 | % NOTE:
16 | %
17 | % Author: Pyojin Kim
18 | % Email: pjinkim1215@gmail.com
19 | % Website:
20 | %
21 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
22 | % log:
23 | % 2015-02-06: Complete
24 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
25 | %
26 |
27 | q1 = 0.5 * sqrt(1+R(1,1)+R(2,2)+R(3,3));
28 | q2 = (1/(4*q1))*(R(3,2)-R(2,3));
29 | q3 = (1/(4*q1))*(R(1,3)-R(3,1));
30 | q4 = (1/(4*q1))*(R(2,1)-R(1,2));
31 |
32 | q = [q1;q2;q3;q4];
33 |
34 | end
35 |
36 |
--------------------------------------------------------------------------------
/Visualization/rotmtx2angle.m:
--------------------------------------------------------------------------------
1 | function [eulerAngle]= rotmtx2angle( R )
2 | % Project: Patch-based Illumination invariant Visual Odometry (PIVO)
3 | % Function: rotmtx2angle
4 | %
5 | % Description:
6 | % This function return the euler angle along x,y and z direction
7 | % from rotation matrix to [phi;theta;psi] angle defined as ZYX sequence
8 | %
9 | % Example:
10 | % OUTPUT:
11 | % eulerAngle: angle vector composed of [phi;theta;psi]
12 | % phi = Rotation angle along x direction in radians
13 | % theta = Rotation angle along y direction in radians
14 | % psi = Rotation angle along z direction in radians
15 | %
16 | % INPUT:
17 | % R = rotation matrix (3x3) defined as [body frame] = R * [inertial frame] (R = R_bg)
18 | %
19 | % NOTE:
20 | %
21 | % Author: Pyojin Kim
22 | % Email: pjinkim1215@gmail.com
23 | % Website:
24 | %
25 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
26 | % log:
27 | % 2017-02-06: Complete
28 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
29 | %
30 |
31 | % change rotMtxBody / [Inertial frame] = rotMtxBody * [Body frame]
32 | rotMtxBody = R.';
33 |
34 | phi=atan2( rotMtxBody(3,2) , rotMtxBody(3,3) );
35 | theta=asin( -rotMtxBody(3,1) );
36 | psi=atan2( rotMtxBody(2,1) , rotMtxBody(1,1) );
37 |
38 | eulerAngle = [phi;theta;psi];
39 |
40 | end
41 |
42 |
43 |
--------------------------------------------------------------------------------
/Visualization/test_codes.m:
--------------------------------------------------------------------------------
1 |
2 |
3 | % parsing ARCore sensor pose data text file
4 | textFileDir = 'ARCore_point_cloud.txt';
5 | textARCorePointData = importdata(textFileDir, delimiter, headerlinesIn);
6 | ARCorePoseTime = textARCorePointData.data(:,1).';
7 | ARCorePointCloud = textARCorePointData.data(:,[2 3 4]).';
8 |
9 |
10 | figure;
11 | numPoints = size(ARCorePointCloud,2);
12 | scatter3(ARCorePointCloud(1,:), ARCorePointCloud(2,:), ARCorePointCloud(3,:), 40*ones(numPoints,1),'.'); hold on; grid on; axis equal;
13 | plot_inertial_frame(0.5);
14 | xlabel('x [m]','fontsize',10); ylabel('y [m]','fontsize',10); zlabel('z [m]','fontsize',10); hold off;
15 |
16 |
17 | % 2) plot ARKit VIO motion estimation results
18 | figure;
19 | h_ARKit = plot3(stateEsti_ARKit(1,:),stateEsti_ARKit(2,:),stateEsti_ARKit(3,:),'m','LineWidth',2); hold on; grid on;
20 | scatter3(ARKitPoints(1,:), ARKitPoints(2,:), ARKitPoints(3,:), 40*ones(numPoints,1), (ARKitColors ./ 255).','.');
21 | plot_inertial_frame(0.5); legend(h_ARKit,{'ARKit'}); axis equal; view(26, 73);
22 | xlabel('x [m]','fontsize',10); ylabel('y [m]','fontsize',10); zlabel('z [m]','fontsize',10); hold off;
23 |
24 | % figure options
25 | f = FigureRotator(gca());
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | scatter3(X(:),Y(:),Z(:),S(:),C(:),'filled'), view(-60,60)
34 |
35 |
36 |
37 |
38 | scatter3(X3DptsGlobal_k(1,:).' , X3DptsGlobal_k(2,:).' , X3DptsGlobal_k(3,:).' , 100*ones(numPts,1) , X3DptsColor_k.','.');
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Google LLC
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 | apply plugin: 'com.android.application'
16 |
17 | android {
18 | compileSdkVersion 29
19 | buildToolsVersion "29.0.1"
20 | defaultConfig {
21 | applicationId "com.pjinkim.arcore_data_logger"
22 | minSdkVersion 26
23 | targetSdkVersion 29
24 | versionCode 1
25 | versionName "1.0"
26 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
27 | }
28 | compileOptions {
29 | sourceCompatibility JavaVersion.VERSION_1_8
30 | targetCompatibility JavaVersion.VERSION_1_8
31 | }
32 | buildTypes {
33 | release {
34 | minifyEnabled false
35 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
36 | }
37 | }
38 | }
39 |
40 | dependencies {
41 | implementation "com.google.ar.sceneform.ux:sceneform-ux:1.11.0"
42 | implementation fileTree(dir: 'libs', include: ['*.jar'])
43 | implementation 'androidx.appcompat:appcompat:1.0.2'
44 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
45 | testImplementation 'junit:junit:4.12'
46 | androidTestImplementation 'androidx.test:runner:1.2.0'
47 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
48 | }
49 |
50 | apply plugin: 'com.google.ar.sceneform.plugin'
51 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/pjinkim/arcore_data_logger/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.pjinkim.arcore_data_logger;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.test.InstrumentationRegistry;
6 | import androidx.test.runner.AndroidJUnit4;
7 |
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | import static org.junit.Assert.*;
12 |
13 | /**
14 | * Instrumented test, which will execute on an Android device.
15 | *
16 | * @see Testing documentation
17 | */
18 | @RunWith(AndroidJUnit4.class)
19 | public class ExampleInstrumentedTest {
20 | @Test
21 | public void useAppContext() {
22 | // Context of the app under test.
23 | Context appContext = InstrumentationRegistry.getTargetContext();
24 |
25 | assertEquals("com.pjinkim.arcore_data_logger", appContext.getPackageName());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
35 |
36 |
37 |
40 |
41 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyojinKim/ARCore-Data-Logger/7ca9ea331313b36cb00ef2e3a28908540ea2b126/app/src/main/ic_launcher-web.png
--------------------------------------------------------------------------------
/app/src/main/java/com/pjinkim/arcore_data_logger/ARCoreSession.java:
--------------------------------------------------------------------------------
1 | package com.pjinkim.arcore_data_logger;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.BitmapFactory;
6 | import android.graphics.Color;
7 | import android.graphics.ImageFormat;
8 | import android.graphics.Matrix;
9 | import android.graphics.Rect;
10 | import android.graphics.YuvImage;
11 | import android.media.Image;
12 | import android.util.Log;
13 |
14 | import androidx.annotation.NonNull;
15 |
16 | import com.google.ar.core.Camera;
17 | import com.google.ar.core.Frame;
18 | import com.google.ar.core.PointCloud;
19 | import com.google.ar.core.Pose;
20 | import com.google.ar.core.TrackingFailureReason;
21 | import com.google.ar.core.TrackingState;
22 | import com.google.ar.core.exceptions.NotYetAvailableException;
23 | import com.google.ar.sceneform.FrameTime;
24 | import com.google.ar.sceneform.math.Vector3;
25 | import com.google.ar.sceneform.ux.ArFragment;
26 |
27 | import java.io.BufferedWriter;
28 | import java.io.ByteArrayOutputStream;
29 | import java.io.IOException;
30 | import java.nio.ByteBuffer;
31 | import java.nio.FloatBuffer;
32 | import java.nio.IntBuffer;
33 | import java.security.KeyException;
34 | import java.util.ArrayList;
35 | import java.util.Locale;
36 | import java.util.concurrent.atomic.AtomicBoolean;
37 |
38 | public class ARCoreSession {
39 |
40 | // properties
41 | private static final String LOG_TAG = ARCoreSession.class.getName();
42 | private static final long mulSecondToNanoSecond = 1000000000;
43 | private long previousTimestamp = 0;
44 |
45 | private MainActivity mContext;
46 | private ArFragment mArFragment;
47 | private PointCloudNode mPointCloudNode;
48 | private AccumulatedPointCloud mAccumulatedPointCloud;
49 | private WorldToScreenTranslator mWorldToScreenTranslator;
50 | private ARCoreResultStreamer mFileStreamer = null;
51 |
52 | private AtomicBoolean mIsRecording = new AtomicBoolean(false);
53 | private AtomicBoolean mIsWritingFile = new AtomicBoolean(false);
54 |
55 | private int mNumberOfFeatures = 0;
56 | private TrackingState mTrackingState;
57 | private TrackingFailureReason mTrackingFailureReason;
58 | private double mUpdateRate = 0;
59 |
60 |
61 | // constructor
62 | public ARCoreSession(@NonNull MainActivity context) {
63 |
64 | // initialize object and ARCore fragment
65 | mContext = context;
66 | mArFragment = (ArFragment) mContext.getSupportFragmentManager().findFragmentById(R.id.ux_fragment);
67 | mArFragment.getArSceneView().getPlaneRenderer().setVisible(false);
68 | mArFragment.getArSceneView().getScene().addOnUpdateListener(this::onUpdateFrame);
69 |
70 | // render 3D point cloud on the screen
71 | mPointCloudNode = new PointCloudNode(mContext);
72 | mArFragment.getArSceneView().getScene().addChild(mPointCloudNode);
73 | mAccumulatedPointCloud = new AccumulatedPointCloud();
74 | mWorldToScreenTranslator = new WorldToScreenTranslator();
75 | }
76 |
77 |
78 | // methods
79 | public void startSession(String streamFolder) {
80 |
81 | // initialize text file stream
82 | if (streamFolder != null) {
83 | try {
84 | mFileStreamer = new ARCoreResultStreamer(mContext, streamFolder);
85 | mIsWritingFile.set(true);
86 | } catch (IOException e) {
87 | mContext.showToast("Cannot create file for ARCore tracking results.");
88 | e.printStackTrace();
89 | }
90 | }
91 | mIsRecording.set(true);
92 | }
93 |
94 |
95 | public void stopSession() {
96 |
97 | // save ARCore 3D point cloud only for visualization
98 | ArrayList pointsPosition = mAccumulatedPointCloud.getPoints();
99 | ArrayList pointsColor = mAccumulatedPointCloud.getColors();
100 | for (int i = 0; i < pointsPosition.size(); i++) {
101 | Vector3 currentPointPosition = pointsPosition.get(i);
102 | Vector3 currentPointColor = pointsColor.get(i);
103 | float pointX = currentPointPosition.x;
104 | float pointY = currentPointPosition.y;
105 | float pointZ = currentPointPosition.z;
106 | float r = currentPointColor.x;
107 | float g = currentPointColor.y;
108 | float b = currentPointColor.z;
109 | try {
110 | mFileStreamer.addARCorePointRecord(pointX, pointY, pointZ, r, g, b);
111 | } catch (IOException | KeyException e) {
112 | Log.d(LOG_TAG, "stopSession: Something is wrong.");
113 | e.printStackTrace();
114 | }
115 | }
116 |
117 | // close text file and reset variables
118 | if (mIsWritingFile.get()) {
119 | try {
120 | mFileStreamer.endFiles();
121 | } catch (IOException e) {
122 | e.printStackTrace();
123 | }
124 | }
125 | mIsWritingFile.set(false);
126 | mIsRecording.set(false);
127 | }
128 |
129 |
130 | private void onUpdateFrame(FrameTime frameTime) {
131 |
132 | // set some variables
133 | boolean isFileSaved = (mIsRecording.get() && mIsWritingFile.get());
134 |
135 | // obtain current ARCore information
136 | mArFragment.onUpdate(frameTime);
137 | Frame frame = mArFragment.getArSceneView().getArFrame();
138 | Camera camera = frame.getCamera();
139 |
140 | // update ARCore measurements
141 | long timestamp = frame.getTimestamp();
142 | double updateRate = (double) mulSecondToNanoSecond / (double) (timestamp - previousTimestamp);
143 | previousTimestamp = timestamp;
144 |
145 | TrackingState trackingState = camera.getTrackingState();
146 | TrackingFailureReason trackingFailureReason = camera.getTrackingFailureReason();
147 | Pose T_gc = frame.getAndroidSensorPose();
148 |
149 | float qx = T_gc.qx();
150 | float qy = T_gc.qy();
151 | float qz = T_gc.qz();
152 | float qw = T_gc.qw();
153 |
154 | float tx = T_gc.tx();
155 | float ty = T_gc.ty();
156 | float tz = T_gc.tz();
157 |
158 | // update 3D point cloud from ARCore
159 | PointCloud pointCloud = frame.acquirePointCloud();
160 | IntBuffer bufferPointID = pointCloud.getIds();
161 | FloatBuffer bufferPoint3D = pointCloud.getPoints();
162 | mPointCloudNode.visualize(pointCloud);
163 | int numberOfFeatures = mAccumulatedPointCloud.getNumberOfFeatures();
164 | pointCloud.release();
165 |
166 | // display and save ARCore information
167 | try {
168 | mNumberOfFeatures = numberOfFeatures;
169 | mTrackingState = trackingState;
170 | mTrackingFailureReason = trackingFailureReason;
171 | mUpdateRate = updateRate;
172 | if (isFileSaved) {
173 |
174 | // 1) record ARCore 6-DoF sensor pose
175 | mFileStreamer.addARCorePoseRecord(timestamp, qx, qy, qz, qw, tx, ty, tz);
176 |
177 | // 2) record ARCore 3D point cloud only for visualization
178 | Image imageFrame = frame.acquireCameraImage();
179 | Bitmap imageBitmap = imageToBitmap(imageFrame);
180 | imageFrame.close();
181 | for (int i = 0; i < (bufferPoint3D.limit() / 4); i++) {
182 |
183 | // check each point's confidence level
184 | float pointConfidence = bufferPoint3D.get(i * 4 + 3);
185 | if (pointConfidence < 0.5) {
186 | continue;
187 | }
188 |
189 | // obtain point ID and XYZ world position
190 | int pointID = bufferPointID.get(i);
191 | float pointX = bufferPoint3D.get(i * 4);
192 | float pointY = bufferPoint3D.get(i * 4 + 1);
193 | float pointZ = bufferPoint3D.get(i * 4 + 2);
194 |
195 | // get each point RGB color information
196 | float[] worldPosition = new float[]{pointX, pointY, pointZ};
197 | Vector3 pointColor = getScreenPixel(worldPosition, imageBitmap);
198 | if (pointColor == null) {
199 | continue;
200 | }
201 |
202 | // append each point position and color information
203 | mAccumulatedPointCloud.appendPointCloud(pointID, pointX, pointY, pointZ, pointColor.x, pointColor.y, pointColor.z);
204 | }
205 | }
206 | } catch (IOException | KeyException | NotYetAvailableException e) {
207 | Log.d(LOG_TAG, "onUpdateFrame: Something is wrong.");
208 | e.printStackTrace();
209 | }
210 | }
211 |
212 |
213 | private Bitmap imageToBitmap (Image image) {
214 | int width = image.getWidth();
215 | int height = image.getHeight();
216 |
217 | byte[] nv21;
218 | ByteBuffer yBuffer = image.getPlanes()[0].getBuffer();
219 | ByteBuffer uBuffer = image.getPlanes()[1].getBuffer();
220 | ByteBuffer vBuffer = image.getPlanes()[2].getBuffer();
221 |
222 | int ySize = yBuffer.remaining();
223 | int uSize = uBuffer.remaining();
224 | int vSize = vBuffer.remaining();
225 |
226 | nv21 = new byte[ySize + uSize + vSize];
227 |
228 | // U and V are swapped
229 | yBuffer.get(nv21, 0, ySize);
230 | vBuffer.get(nv21, ySize, vSize);
231 | uBuffer.get(nv21, ySize + vSize, uSize);
232 |
233 | YuvImage yuvImage = new YuvImage(nv21, ImageFormat.NV21, width, height, null);
234 | ByteArrayOutputStream os = new ByteArrayOutputStream();
235 | yuvImage.compressToJpeg(new Rect(0, 0, width, height), 100, os);
236 | byte[] jpegByteArray = os.toByteArray();
237 | Bitmap bitmap = BitmapFactory.decodeByteArray(jpegByteArray, 0, jpegByteArray.length);
238 |
239 | Matrix matrix = new Matrix();
240 | matrix.setRotate(90);
241 |
242 | return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
243 | }
244 |
245 |
246 | private Vector3 getScreenPixel(float[] worldPosition, Bitmap imageBitmap) throws NotYetAvailableException {
247 |
248 | // clip to screen space (ViewMatrix * ProjectionMatrix * Anchor Matrix)
249 | double[] pos2D = mWorldToScreenTranslator.worldToScreen(imageBitmap.getWidth(), imageBitmap.getHeight(), mArFragment.getArSceneView().getArFrame().getCamera(), worldPosition);
250 |
251 | // check if inside the screen
252 | if ((pos2D[0] < 0) || (pos2D[0] > imageBitmap.getWidth()) || (pos2D[1] < 0) || (pos2D[1] > imageBitmap.getHeight())) {
253 | return null;
254 | }
255 |
256 | int pixel = imageBitmap.getPixel((int) pos2D[0], (int) pos2D[1]);
257 | int r = Color.red(pixel);
258 | int g = Color.green(pixel);
259 | int b = Color.blue(pixel);
260 | Vector3 pointColor = new Vector3(r, g, b);
261 |
262 | return pointColor;
263 | }
264 |
265 |
266 | // definition of 'ARCoreResultStreamer' class
267 | class ARCoreResultStreamer extends FileStreamer {
268 |
269 | // properties
270 | private BufferedWriter mWriterPose;
271 | private BufferedWriter mWriterPoint;
272 |
273 |
274 | // constructor
275 | ARCoreResultStreamer(final Context context, final String outputFolder) throws IOException {
276 | super(context, outputFolder);
277 | addFile("ARCore_sensor_pose", "ARCore_sensor_pose.txt");
278 | addFile("ARCore_point_cloud", "ARCore_point_cloud.txt");
279 | mWriterPose = getFileWriter("ARCore_sensor_pose");
280 | mWriterPoint = getFileWriter("ARCore_point_cloud");
281 | }
282 |
283 |
284 | // methods
285 | public void addARCorePoseRecord(long timestamp, float qx, float qy, float qz, float qw, float tx, float ty, float tz) throws IOException, KeyException {
286 |
287 | // execute the block with only one thread
288 | synchronized (this) {
289 |
290 | // record timestamp and 6-DoF device pose in text file
291 | StringBuilder stringBuilder = new StringBuilder();
292 | stringBuilder.append(timestamp);
293 | stringBuilder.append(String.format(Locale.US, " %.6f %.6f %.6f %.6f %.6f %.6f %.6f", qx, qy, qz, qw, tx, ty, tz));
294 | stringBuilder.append(" \n");
295 | mWriterPose.write(stringBuilder.toString());
296 | }
297 | }
298 |
299 |
300 | public void addARCorePointRecord(final float pointX, final float pointY, final float pointZ, final float r, final float g, final float b) throws IOException, KeyException {
301 |
302 | // execute the block with only one thread
303 | synchronized (this) {
304 |
305 | // record 3D point cloud in text file
306 | StringBuilder stringBuilder = new StringBuilder();
307 | stringBuilder.append(String.format(Locale.US, "%.6f %.6f %.6f %.2f %.2f %.2f", pointX, pointY, pointZ, r, g, b));
308 | stringBuilder.append(" \n");
309 | mWriterPoint.write(stringBuilder.toString());
310 | }
311 | }
312 |
313 |
314 | @Override
315 | public void endFiles() throws IOException {
316 |
317 | // execute the block with only one thread
318 | synchronized (this) {
319 | mWriterPose.flush();
320 | mWriterPose.close();
321 | mWriterPoint.flush();
322 | mWriterPoint.close();
323 | }
324 | }
325 | }
326 |
327 |
328 | // getter and setter
329 | public int getNumberOfFeatures() {
330 | return mNumberOfFeatures;
331 | }
332 |
333 | public TrackingState getTrackingState() {
334 | return mTrackingState;
335 | }
336 |
337 | public TrackingFailureReason getTrackingFailureReason() {
338 | return mTrackingFailureReason;
339 | }
340 |
341 | public double getUpdateRate() {
342 | return mUpdateRate;
343 | }
344 | }
345 |
--------------------------------------------------------------------------------
/app/src/main/java/com/pjinkim/arcore_data_logger/AccumulatedPointCloud.java:
--------------------------------------------------------------------------------
1 | package com.pjinkim.arcore_data_logger;
2 |
3 | import com.google.ar.sceneform.math.Vector3;
4 |
5 | import java.util.ArrayList;
6 |
7 | public class AccumulatedPointCloud {
8 |
9 | // properties
10 | private static final int BASE_CAPACITY = 100000;
11 | private ArrayList mPoints = new ArrayList();
12 | private ArrayList mColors = new ArrayList();
13 | private int[] mIdentifiedIndices = new int[BASE_CAPACITY];
14 | private int mNumberOfFeatures = 0;
15 |
16 |
17 | // constructor
18 | public AccumulatedPointCloud() {
19 |
20 | // initialize properties
21 | for (int i = 0; i < BASE_CAPACITY; i++) {
22 | mIdentifiedIndices[i] = -99;
23 | }
24 | }
25 |
26 |
27 | // methods
28 | public void appendPointCloud(int pointID, float pointX, float pointY, float pointZ, float r, float g, float b) {
29 | if (mIdentifiedIndices[pointID] != -99) {
30 | int existingIndex = mIdentifiedIndices[pointID];
31 | Vector3 pointPosition = new Vector3(pointX, pointY, pointZ);
32 | Vector3 pointColor = new Vector3(r, g, b);
33 | mPoints.set(existingIndex, pointPosition);
34 | mColors.set(existingIndex, pointColor);
35 | } else {
36 | mIdentifiedIndices[pointID] = mNumberOfFeatures;
37 | Vector3 pointPosition = new Vector3(pointX, pointY, pointZ);
38 | Vector3 pointColor = new Vector3(r, g, b);
39 | mPoints.add(pointPosition);
40 | mColors.add(pointColor);
41 | mNumberOfFeatures++;
42 | }
43 | }
44 |
45 |
46 | // getter and setter
47 | public int getNumberOfFeatures() {
48 | return mNumberOfFeatures;
49 | }
50 |
51 | public ArrayList getPoints() {
52 | return mPoints;
53 | }
54 |
55 | public ArrayList getColors() {
56 | return mColors;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/app/src/main/java/com/pjinkim/arcore_data_logger/FileStreamer.java:
--------------------------------------------------------------------------------
1 | package com.pjinkim.arcore_data_logger;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.net.Uri;
6 | import android.util.Log;
7 |
8 | import java.io.BufferedWriter;
9 | import java.io.File;
10 | import java.io.FileWriter;
11 | import java.io.IOException;
12 | import java.security.KeyException;
13 | import java.util.Calendar;
14 | import java.util.HashMap;
15 | import java.util.Locale;
16 |
17 | public class FileStreamer {
18 |
19 | // properties
20 | private final static String LOG_TAG = FileStreamer.class.getName();
21 |
22 | private Context mContext;
23 | private HashMap mFileWriters = new HashMap<>();
24 | private String mOutputFolder;
25 |
26 |
27 | // constructor
28 | public FileStreamer(Context mContext, final String mOutputFolder) {
29 | this.mContext = mContext;
30 | this.mOutputFolder = mOutputFolder;
31 | }
32 |
33 |
34 | // methods
35 | public void addFile(final String writerId, final String fileName) throws IOException {
36 |
37 | // check if there is a already generated text file
38 | if (mFileWriters.containsKey(writerId)) {
39 | Log.w(LOG_TAG, "addFile: " + writerId + " already exist.");
40 | return;
41 | }
42 |
43 | // get current time information
44 | Calendar fileTimestamp = Calendar.getInstance();
45 | String timeHeader = "# Created at " + fileTimestamp.getTime().toString() + " in Burnaby Canada \n";
46 |
47 | // generate text file
48 | BufferedWriter newWriter = createFile(mOutputFolder + "/" + fileName, timeHeader);
49 | mFileWriters.put(writerId, newWriter);
50 | }
51 |
52 | private BufferedWriter createFile(final String path, final String timeHeader) throws IOException {
53 |
54 | File file = new File(path);
55 | BufferedWriter writer = new BufferedWriter((new FileWriter(file)));
56 |
57 | Intent scanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
58 | scanIntent.setData(Uri.fromFile(file));
59 | mContext.sendBroadcast(scanIntent);
60 | if ((timeHeader != null) && (timeHeader.length() != 0)) {
61 | writer.append(timeHeader);
62 | writer.flush();
63 | }
64 | return writer;
65 | }
66 |
67 | public String getOutputFolder() {
68 | return mOutputFolder;
69 | }
70 |
71 | public BufferedWriter getFileWriter(final String writerId) {
72 | return mFileWriters.get(writerId);
73 | }
74 |
75 | public void addRecord(final long timestamp, final String writerId, final int numValues, final float[] values) throws IOException, KeyException {
76 |
77 | // execute the block with only one thread
78 | synchronized (this) {
79 |
80 | // get BufferedWriter of 'writerId'
81 | BufferedWriter writer = getFileWriter(writerId);
82 | if (writer == null) {
83 | throw new KeyException("addRecord: " + writerId + " not found.");
84 | }
85 |
86 | // record timestamp, and values in text file
87 | StringBuilder stringBuilder = new StringBuilder();
88 | stringBuilder.append(timestamp);
89 | for (int i = 0; i < numValues; ++i) {
90 | stringBuilder.append(String.format(Locale.US, " %.6f", values[i]));
91 | }
92 | stringBuilder.append(" \n");
93 | writer.write(stringBuilder.toString());
94 | }
95 | }
96 |
97 | public void endFiles() throws IOException {
98 |
99 | // execute the block with only one thread
100 | synchronized (this) {
101 | for (BufferedWriter eachWriter : mFileWriters.values()) {
102 | eachWriter.flush();
103 | eachWriter.close();
104 | }
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/app/src/main/java/com/pjinkim/arcore_data_logger/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.pjinkim.arcore_data_logger;
2 |
3 | import androidx.appcompat.app.AppCompatActivity;
4 | import androidx.core.content.ContextCompat;
5 |
6 | import android.Manifest;
7 | import android.app.Activity;
8 | import android.app.ActivityManager;
9 | import android.content.Context;
10 | import android.content.pm.PackageManager;
11 | import android.os.Build;
12 | import android.os.Bundle;
13 | import android.os.Handler;
14 | import android.os.PowerManager;
15 | import android.util.Log;
16 | import android.view.View;
17 | import android.widget.Button;
18 | import android.widget.TextView;
19 | import android.widget.Toast;
20 |
21 | import com.google.ar.core.TrackingFailureReason;
22 | import com.google.ar.core.TrackingState;
23 |
24 | import java.io.IOException;
25 | import java.util.Locale;
26 | import java.util.Timer;
27 | import java.util.TimerTask;
28 | import java.util.concurrent.atomic.AtomicBoolean;
29 |
30 | public class MainActivity extends AppCompatActivity {
31 |
32 | // properties
33 | private static final String LOG_TAG = MainActivity.class.getName();
34 | private static final double MIN_OPENGL_VERSION = 3.0;
35 |
36 | private final static int REQUEST_CODE_ANDROID = 1001;
37 | private static String[] REQUIRED_PERMISSIONS = new String[] {
38 | Manifest.permission.CAMERA,
39 | Manifest.permission.WAKE_LOCK,
40 | Manifest.permission.READ_PHONE_STATE,
41 | Manifest.permission.WRITE_EXTERNAL_STORAGE
42 | };
43 |
44 | private ARCoreSession mARCoreSession;
45 |
46 | private Handler mHandler = new Handler();
47 | private AtomicBoolean mIsRecording = new AtomicBoolean(false);
48 | private PowerManager.WakeLock mWakeLock;
49 |
50 | private TextView mLabelNumberFeatures, mLabelUpdateRate;
51 | private TextView mLabelTrackingStatus, mLabelTrackingFailureReason;
52 |
53 | private Button mStartStopButton;
54 | private TextView mLabelInterfaceTime;
55 | private Timer mInterfaceTimer = new Timer();
56 | private int mSecondCounter = 0;
57 |
58 |
59 | // Android activity lifecycle states
60 | @Override
61 | protected void onCreate(Bundle savedInstanceState) {
62 | super.onCreate(savedInstanceState);
63 | setContentView(R.layout.activity_main);
64 |
65 | // check Android and OpenGL version
66 | if (!checkIsSupportedDeviceOrFinish(this)) {
67 | return;
68 | }
69 |
70 |
71 | // initialize screen labels and buttons
72 | initializeViews();
73 |
74 |
75 | // setup sessions
76 | mARCoreSession = new ARCoreSession(this);
77 |
78 |
79 | // battery power setting
80 | PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
81 | mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "sensors_data_logger:wakelocktag");
82 | mWakeLock.acquire();
83 |
84 |
85 | // monitor ARCore information
86 | displayARCoreInformation();
87 | mLabelInterfaceTime.setText(R.string.ready_title);
88 | }
89 |
90 |
91 | @Override
92 | protected void onResume() {
93 | super.onResume();
94 | if (!hasPermissions(this, REQUIRED_PERMISSIONS)) {
95 | requestPermissions(REQUIRED_PERMISSIONS, REQUEST_CODE_ANDROID);
96 | }
97 | }
98 |
99 |
100 | @Override
101 | protected void onDestroy() {
102 | if (mIsRecording.get()) {
103 | stopRecording();
104 | }
105 | if (mWakeLock.isHeld()) {
106 | mWakeLock.release();
107 | }
108 | super.onDestroy();
109 | }
110 |
111 |
112 | // methods
113 | public void startStopRecording(View view) {
114 | if (!mIsRecording.get()) {
115 |
116 | // start recording sensor measurements when button is pressed
117 | startRecording();
118 |
119 | // start interface timer on display
120 | mSecondCounter = 0;
121 | mInterfaceTimer.schedule(new TimerTask() {
122 | @Override
123 | public void run() {
124 | mSecondCounter += 1;
125 | mLabelInterfaceTime.setText(interfaceIntTime(mSecondCounter));
126 | }
127 | }, 0, 1000);
128 |
129 | } else {
130 |
131 | // stop recording sensor measurements when button is pressed
132 | stopRecording();
133 |
134 | // stop interface timer on display
135 | mInterfaceTimer.cancel();
136 | mLabelInterfaceTime.setText(R.string.ready_title);
137 | }
138 | }
139 |
140 |
141 | private void startRecording() {
142 |
143 | // output directory for text files
144 | String outputFolder = null;
145 | try {
146 | OutputDirectoryManager folder = new OutputDirectoryManager("", "R_pjinkim_ARCore");
147 | outputFolder = folder.getOutputDirectory();
148 | } catch (IOException e) {
149 | Log.e(LOG_TAG, "startRecording: Cannot create output folder.");
150 | e.printStackTrace();
151 | }
152 |
153 | // start ARCore session
154 | mARCoreSession.startSession(outputFolder);
155 | mIsRecording.set(true);
156 |
157 | // update Start/Stop button UI
158 | runOnUiThread(new Runnable() {
159 | @Override
160 | public void run() {
161 | mStartStopButton.setEnabled(true);
162 | mStartStopButton.setText(R.string.stop_title);
163 | }
164 | });
165 | showToast("Recording starts!");
166 | }
167 |
168 |
169 | protected void stopRecording() {
170 | mHandler.post(new Runnable() {
171 | @Override
172 | public void run() {
173 |
174 | // stop ARCore session
175 | mARCoreSession.stopSession();
176 | mIsRecording.set(false);
177 |
178 | // update screen UI and button
179 | showToast("Recording stops!");
180 | resetUI();
181 | }
182 | });
183 | }
184 |
185 |
186 | public void showToast(final String text) {
187 | runOnUiThread(new Runnable() {
188 | @Override
189 | public void run() {
190 | Toast.makeText(MainActivity.this, text, Toast.LENGTH_SHORT).show();
191 | }
192 | });
193 | }
194 |
195 |
196 | private void resetUI() {
197 | runOnUiThread(new Runnable() {
198 | @Override
199 | public void run() {
200 | mLabelNumberFeatures.setText("N/A");
201 | mLabelTrackingStatus.setText("N/A");
202 | mLabelTrackingFailureReason.setText("N/A");
203 | mLabelUpdateRate.setText("N/A");
204 |
205 | mStartStopButton.setEnabled(true);
206 | mStartStopButton.setText(R.string.start_title);
207 | }
208 | });
209 | }
210 |
211 |
212 | public static boolean checkIsSupportedDeviceOrFinish(final Activity activity) {
213 |
214 | // check Android version
215 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
216 | Log.e(LOG_TAG, "Sceneform requires Android N or later");
217 | Toast.makeText(activity, "Sceneform requires Android N or later", Toast.LENGTH_LONG).show();
218 | activity.finish();
219 | return false;
220 | }
221 |
222 | // get current OpenGL version
223 | String openGlVersionString = ((ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE))
224 | .getDeviceConfigurationInfo()
225 | .getGlEsVersion();
226 |
227 | // check OpenGL version
228 | if (Double.parseDouble(openGlVersionString) < MIN_OPENGL_VERSION) {
229 | Log.e(LOG_TAG, "Sceneform requires OpenGL ES 3.0 later");
230 | Toast.makeText(activity, "Sceneform requires OpenGL ES 3.0 or later", Toast.LENGTH_LONG).show();
231 | activity.finish();
232 | return false;
233 | }
234 | return true;
235 | }
236 |
237 |
238 | @Override
239 | public void onBackPressed() {
240 |
241 | // nullify back button when recording starts
242 | if (!mIsRecording.get()) {
243 | super.onBackPressed();
244 | }
245 | }
246 |
247 |
248 | private static boolean hasPermissions(Context context, String... permissions) {
249 |
250 | // check Android hardware permissions
251 | for (String permission : permissions) {
252 | if (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
253 | return false;
254 | }
255 | }
256 | return true;
257 | }
258 |
259 |
260 | private String interfaceIntTime(final int second) {
261 |
262 | // check second input
263 | if (second < 0) {
264 | Log.e(LOG_TAG, "interfaceIntTime: Second cannot be negative.");
265 | return null;
266 | }
267 |
268 | // extract hour, minute, second information from second
269 | int input = second;
270 | int hours = input / 3600;
271 | input = input % 3600;
272 | int mins = input / 60;
273 | int secs = input % 60;
274 |
275 | // return interface int time
276 | return String.format(Locale.US, "%02d:%02d:%02d", hours, mins, secs);
277 | }
278 |
279 |
280 | private void initializeViews() {
281 |
282 | mLabelNumberFeatures = (TextView) findViewById(R.id.label_number_features);
283 | mLabelTrackingStatus = (TextView) findViewById(R.id.label_tracking_status);
284 | mLabelTrackingFailureReason = (TextView) findViewById(R.id.label_tracking_failure_reason);
285 | mLabelUpdateRate = (TextView) findViewById(R.id.label_update_rate);
286 |
287 | mStartStopButton = (Button) findViewById(R.id.button_start_stop);
288 | mLabelInterfaceTime = (TextView) findViewById(R.id.label_interface_time);
289 | }
290 |
291 |
292 | private void displayARCoreInformation() {
293 |
294 | // get ARCore tracking information
295 | int numberOfFeatures = mARCoreSession.getNumberOfFeatures();
296 | TrackingState trackingState = mARCoreSession.getTrackingState();
297 | TrackingFailureReason trackingFailureReason = mARCoreSession.getTrackingFailureReason();
298 | double updateRate = mARCoreSession.getUpdateRate();
299 |
300 | // update current screen (activity)
301 | runOnUiThread(new Runnable() {
302 | @Override
303 | public void run() {
304 |
305 | // determine TrackingState text
306 | String ARCoreTrackingState = "";
307 | if (trackingState == TrackingState.PAUSED) {
308 | ARCoreTrackingState = "PAUSED";
309 | } else if (trackingState == TrackingState.STOPPED) {
310 | ARCoreTrackingState = "STOPPED";
311 | } else if (trackingState == TrackingState.TRACKING) {
312 | ARCoreTrackingState = "TRACKING";
313 | } else {
314 | ARCoreTrackingState = "ERROR?";
315 | }
316 |
317 | // determine TrackingFailureReason text
318 | String ARCoreTrackingFailureReason = "";
319 | if (trackingFailureReason == TrackingFailureReason.BAD_STATE) {
320 | ARCoreTrackingFailureReason = "BAD STATE";
321 | } else if (trackingFailureReason == TrackingFailureReason.EXCESSIVE_MOTION) {
322 | ARCoreTrackingFailureReason = "FAST MOTION";
323 | } else if (trackingFailureReason == TrackingFailureReason.INSUFFICIENT_FEATURES) {
324 | ARCoreTrackingFailureReason = "LOW FEATURES";
325 | } else if (trackingFailureReason == TrackingFailureReason.INSUFFICIENT_LIGHT) {
326 | ARCoreTrackingFailureReason = "LOW LIGHT";
327 | } else if (trackingFailureReason == TrackingFailureReason.NONE) {
328 | ARCoreTrackingFailureReason = "NONE";
329 | } else {
330 | ARCoreTrackingFailureReason = "ERROR?";
331 | }
332 |
333 | // update interface screen labels
334 | mLabelNumberFeatures.setText(String.format(Locale.US, "%05d", numberOfFeatures));
335 | mLabelTrackingStatus.setText(ARCoreTrackingState);
336 | mLabelTrackingFailureReason.setText(ARCoreTrackingFailureReason);
337 | mLabelUpdateRate.setText(String.format(Locale.US, "%.3f Hz", updateRate));
338 | }
339 | });
340 |
341 | // determine display update rate (100 ms)
342 | final long displayInterval = 100;
343 | mHandler.postDelayed(new Runnable() {
344 | @Override
345 | public void run() {
346 | displayARCoreInformation();
347 | }
348 | }, displayInterval);
349 | }
350 | }
351 |
--------------------------------------------------------------------------------
/app/src/main/java/com/pjinkim/arcore_data_logger/OutputDirectoryManager.java:
--------------------------------------------------------------------------------
1 | package com.pjinkim.arcore_data_logger;
2 |
3 | import android.os.Environment;
4 | import android.util.Log;
5 |
6 | import java.io.File;
7 | import java.io.FileNotFoundException;
8 | import java.text.SimpleDateFormat;
9 | import java.util.Calendar;
10 |
11 | public class OutputDirectoryManager {
12 |
13 | // properties
14 | private final static String LOG_TAG = OutputDirectoryManager.class.getName();
15 |
16 | private String mOutputDirectory;
17 |
18 |
19 | // constructors
20 | public OutputDirectoryManager(final String prefix, final String suffix) throws FileNotFoundException {
21 | update(prefix, suffix);
22 | }
23 |
24 | public OutputDirectoryManager(final String prefix) throws FileNotFoundException {
25 | update(prefix);
26 | }
27 |
28 | public OutputDirectoryManager() throws FileNotFoundException {
29 | update();
30 | }
31 |
32 |
33 | // methods
34 | private void update(final String prefix, final String suffix) throws FileNotFoundException {
35 |
36 | // initialize folder name with current time information
37 | Calendar currentTime = Calendar.getInstance();
38 | SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddhhmmss");
39 | File externalDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
40 | String folderName = formatter.format(currentTime.getTime());
41 |
42 | // combine prefix and suffix
43 | if (prefix != null) {
44 | folderName = prefix + folderName;
45 | }
46 | if (suffix != null) {
47 | folderName = folderName + suffix;
48 | }
49 |
50 | // generate output directory folder
51 | File outputDirectory = new File(externalDirectory.getAbsolutePath() + "/" + folderName);
52 | if (!outputDirectory.exists()) {
53 | if (!outputDirectory.mkdir()) {
54 | Log.e(LOG_TAG, "update: Cannot create output directory.");
55 | throw new FileNotFoundException();
56 | }
57 | }
58 | mOutputDirectory = outputDirectory.getAbsolutePath();
59 | Log.i(LOG_TAG, "update: Output directory: " + outputDirectory.getAbsolutePath());
60 | }
61 |
62 | private void update(final String prefix) throws FileNotFoundException {
63 | update(prefix, null);
64 | }
65 |
66 | private void update() throws FileNotFoundException {
67 | update(null, null);
68 | }
69 |
70 |
71 | // getter and setter
72 | public String getOutputDirectory() {
73 | return mOutputDirectory;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/app/src/main/java/com/pjinkim/arcore_data_logger/PointCloudNode.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.pjinkim.arcore_data_logger;
17 |
18 | import android.content.Context;
19 | import android.util.Log;
20 |
21 | import com.google.ar.core.PointCloud;
22 | import com.google.ar.sceneform.Node;
23 | import com.google.ar.sceneform.math.Vector3;
24 | import com.google.ar.sceneform.rendering.Color;
25 | import com.google.ar.sceneform.rendering.Material;
26 | import com.google.ar.sceneform.rendering.MaterialFactory;
27 | import com.google.ar.sceneform.rendering.ModelRenderable;
28 | import com.google.ar.sceneform.rendering.RenderableDefinition;
29 | import com.google.ar.sceneform.rendering.Vertex;
30 |
31 | import java.nio.FloatBuffer;
32 | import java.util.concurrent.CompletableFuture;
33 | import java.util.stream.Collectors;
34 | import java.util.stream.IntStream;
35 | import java.util.stream.Stream;
36 |
37 | /** Renders the ARCore point cloud as a Node. */
38 | public class PointCloudNode extends Node {
39 |
40 | // properties
41 | private static final String LOG_TAG = PointCloudNode.class.getName();
42 |
43 | private long timestamp;
44 | private Vertex[] ptbuffer;
45 | private int[] indexbuffer;
46 | private int numFeatures;
47 |
48 | private CompletableFuture materialHolder;
49 |
50 | // This is the extent of the point
51 | private static final float POINT_DELTA = 0.003f;
52 |
53 |
54 | // constructor
55 | public PointCloudNode(Context context) {
56 | float r = 255 / 255f;
57 | float g = 0 / 255f;
58 | float b = 255 / 255f;
59 |
60 | Color color = new Color(r, g, b, 1.0f);
61 | materialHolder = MaterialFactory.makeOpaqueWithColor(context, color);
62 | }
63 |
64 |
65 | // methods
66 | @SuppressWarnings({"AndroidApiChecker", "FutureReturnValueIgnored"})
67 | public void visualize(PointCloud cloud) {
68 |
69 | if (!isEnabled()) {
70 | return;
71 | }
72 | // If this is the same cloud as last time, skip it. Also, if the material has not loaded yet,
73 | // skip this.
74 | if (this.timestamp != cloud.getTimestamp() && materialHolder.getNow(null) != null) {
75 | timestamp = cloud.getTimestamp();
76 | FloatBuffer buf = cloud.getPoints();
77 |
78 | // Point clouds are 4 values x,y,z and a confidence value.
79 | numFeatures = 0;
80 | numFeatures = (buf.limit() / 4);
81 |
82 | // no features in the cloud
83 | if (numFeatures < 1) {
84 | setRenderable(null);
85 | return;
86 | }
87 |
88 | // Each feature point is drawn as a 4 sided pyramid.
89 | // 4 points per feature.
90 | int vertexPerFeature = 4;
91 | int numFaces = 4;
92 | int numPoints = numFeatures * vertexPerFeature;
93 | int vertexPerFace = 3;
94 |
95 | // draw a triangle per face (4 triangles) per feature.
96 | int indexPerFeature = numFaces * vertexPerFace;
97 |
98 | int numIndices = numFeatures * indexPerFeature;
99 |
100 | // allocate a buffer on the high water mark.
101 | if (ptbuffer == null || ptbuffer.length < numPoints) {
102 | ptbuffer = new Vertex[numPoints];
103 | indexbuffer = new int[numIndices];
104 | }
105 |
106 | Vector3 feature = new Vector3();
107 | Vector3[] p = {new Vector3(), new Vector3(), new Vector3(), new Vector3()};
108 | Vertex.UvCoordinate uv0 = new Vertex.UvCoordinate(0,0);
109 | Vector3 normal0 = new Vector3(0,0,1);
110 | Vector3 normal1 = new Vector3(.7f,0,.7f);
111 | Vector3 normal2 = new Vector3(-.7f,0,.7f);
112 | Vector3 normal3 = new Vector3(0,1,0);
113 |
114 | // Point cloud data is 4 floats per feature, {x,y,z,confidence}
115 | for (int i = 0; i < buf.limit() / 4; i++) {
116 | // feature point
117 | feature.x = buf.get(i * 4);
118 | feature.y = buf.get(i * 4 + 1);
119 | feature.z = buf.get(i * 4 + 2);
120 |
121 | // Top point
122 | p[0].x = feature.x;
123 | p[0].y = feature.y + POINT_DELTA;
124 | p[0].z = feature.z;
125 |
126 | // left pt
127 | p[1].x = feature.x - POINT_DELTA;
128 | p[1].y = feature.y;
129 | p[1].z = feature.z - POINT_DELTA;
130 |
131 | // front point
132 | p[2].x = feature.x;
133 | p[2].y = feature.y;
134 | p[2].z = feature.z + POINT_DELTA;
135 |
136 | // right pt
137 | p[3].x = feature.x + POINT_DELTA;
138 | p[3].y = feature.y;
139 | p[3].z = feature.z - POINT_DELTA;
140 |
141 | int vertexBase = i * vertexPerFeature;
142 |
143 | // Create the vertices. Set the tangent and UV to quiet warnings about material requirements.
144 | ptbuffer[vertexBase] = Vertex.builder().setPosition(p[0])
145 | .setUvCoordinate(uv0)
146 | .setNormal(normal0)
147 | .build();
148 | ptbuffer[vertexBase + 1] = Vertex.builder().setPosition(p[1])
149 | .setUvCoordinate(uv0)
150 | .setNormal(normal1)
151 | .build();
152 |
153 | ptbuffer[vertexBase + 2] = Vertex.builder().setPosition(p[2])
154 | .setUvCoordinate(uv0)
155 | .setNormal(normal2)
156 | .build();
157 |
158 | ptbuffer[vertexBase + 3] = Vertex.builder().setPosition(p[3])
159 | .setUvCoordinate(uv0)
160 | .setNormal(normal3)
161 | .build();
162 |
163 | int featureBase = i * indexPerFeature;
164 |
165 | // The indices of the triangles need to be listed counter clockwise as
166 | // appears when facing the front side of the face.
167 |
168 | // left 0 1 2
169 | indexbuffer[featureBase + 2] = vertexBase;
170 | indexbuffer[featureBase] = vertexBase + 1;
171 | indexbuffer[featureBase + 1] = vertexBase + 2;
172 |
173 | // right 0 2 3
174 | indexbuffer[featureBase + 3] = vertexBase;
175 | indexbuffer[featureBase + 4] = vertexBase + 2;
176 | indexbuffer[featureBase + 5] = vertexBase + 3;
177 |
178 | // back 0 3 1
179 | indexbuffer[featureBase + 6] = vertexBase;
180 | indexbuffer[featureBase + 7] = vertexBase + 3;
181 | indexbuffer[featureBase + 8] = vertexBase + 1;
182 |
183 | // bottom 1,2,3
184 | indexbuffer[featureBase + 9] = vertexBase + 1;
185 | indexbuffer[featureBase + 10] = vertexBase + 2;
186 | indexbuffer[featureBase + 11] = vertexBase + 3;
187 | }
188 |
189 | RenderableDefinition.Submesh submesh =
190 | RenderableDefinition.Submesh.builder()
191 | .setName("pointcloud")
192 | .setMaterial(materialHolder.getNow(null))
193 | .setTriangleIndices(
194 | IntStream.of(indexbuffer)
195 | .limit(numIndices)
196 | .boxed()
197 | .collect(Collectors.toList()))
198 | .build();
199 |
200 | RenderableDefinition def =
201 | RenderableDefinition.builder()
202 | .setVertices(Stream.of(ptbuffer).limit(numPoints).collect(Collectors.toList()))
203 | .setSubmeshes(Stream.of(submesh).collect(Collectors.toList()))
204 | .build();
205 |
206 | ModelRenderable.builder().setSource(def).build().thenAccept(renderable -> {
207 | renderable.setShadowCaster(false);setRenderable(renderable);});
208 | }
209 | }
210 |
211 |
212 | // getter and setter
213 | public int getNumberOfFeatures() {
214 | return numFeatures;
215 | }
216 | }
217 |
--------------------------------------------------------------------------------
/app/src/main/java/com/pjinkim/arcore_data_logger/WorldToScreenTranslator.java:
--------------------------------------------------------------------------------
1 | package com.pjinkim.arcore_data_logger;
2 |
3 | import android.opengl.Matrix;
4 |
5 | import com.google.ar.core.Camera;
6 | import com.google.ar.core.Pose;
7 |
8 | //Source: https://stackoverflow.com/questions/49026297/convert-3d-world-arcore-anchor-pose-to-its-corresponding-2d-screen-coordinates/49066308#49066308
9 |
10 | public class WorldToScreenTranslator {
11 | private float[] calculateWorld2CameraMatrix(float[] modelmtx, float[] viewmtx, float[] prjmtx) {
12 |
13 | float scaleFactor = 1.0f;
14 | float[] scaleMatrix = new float[16];
15 | float[] modelXscale = new float[16];
16 | float[] viewXmodelXscale = new float[16];
17 | float[] world2screenMatrix = new float[16];
18 |
19 | Matrix.setIdentityM(scaleMatrix, 0);
20 | scaleMatrix[0] = scaleFactor;
21 | scaleMatrix[5] = scaleFactor;
22 | scaleMatrix[10] = scaleFactor;
23 |
24 | Matrix.multiplyMM(modelXscale, 0, modelmtx, 0, scaleMatrix, 0);
25 | Matrix.multiplyMM(viewXmodelXscale, 0, viewmtx, 0, modelXscale, 0);
26 | Matrix.multiplyMM(world2screenMatrix, 0, prjmtx, 0, viewXmodelXscale, 0);
27 |
28 | return world2screenMatrix;
29 | }
30 |
31 | private double[] world2Screen(int screenWidth, int screenHeight, float[] world2cameraMatrix)
32 | {
33 | float[] origin = {0f, 0f, 0f, 1f};
34 | float[] ndcCoord = new float[4];
35 | Matrix.multiplyMV(ndcCoord, 0, world2cameraMatrix, 0, origin, 0);
36 |
37 | ndcCoord[0] = ndcCoord[0]/ndcCoord[3];
38 | ndcCoord[1] = ndcCoord[1]/ndcCoord[3];
39 |
40 | double[] pos_2d = new double[]{0,0};
41 | pos_2d[0] = screenWidth * ((ndcCoord[0] + 1.0)/2.0);
42 | pos_2d[1] = screenHeight * (( 1.0 - ndcCoord[1])/2.0);
43 |
44 | return pos_2d;
45 | }
46 |
47 | public double[] worldToScreen(int width, int height, Camera camera, float[] pos3D){
48 |
49 | // Get projection matrix.
50 | float[] projmtx = new float[16];
51 | camera.getProjectionMatrix(projmtx, 0, 0.1f, 100.0f);
52 |
53 | // Get camera matrix and draw.
54 | float[] viewmtx = new float[16];
55 | camera.getViewMatrix(viewmtx, 0);
56 |
57 | float[] anchorMatrix = new float[16];
58 |
59 | Pose pose = Pose.makeTranslation(pos3D[0], pos3D[1], pos3D[2]);
60 | pose.toMatrix(anchorMatrix, 0);
61 |
62 | float[] world2screenMatrix =
63 | calculateWorld2CameraMatrix(anchorMatrix, viewmtx, projmtx);
64 | double[] anchor_2d = world2Screen(width, height, world2screenMatrix);
65 | return anchor_2d;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/gruvi_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyojinKim/ARCore-Data-Logger/7ca9ea331313b36cb00ef2e3a28908540ea2b126/app/src/main/res/drawable/gruvi_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
10 |
12 |
14 |
16 |
18 |
20 |
22 |
24 |
26 |
28 |
30 |
32 |
34 |
36 |
38 |
40 |
42 |
44 |
46 |
48 |
50 |
52 |
54 |
56 |
58 |
60 |
62 |
64 |
66 |
68 |
70 |
72 |
74 |
75 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/sfu_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyojinKim/ARCore-Data-Logger/7ca9ea331313b36cb00ef2e3a28908540ea2b126/app/src/main/res/drawable/sfu_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/font/roboto.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/font/roboto_black.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
23 |
24 |
29 |
30 |
39 |
40 |
54 |
55 |
68 |
69 |
82 |
83 |
96 |
97 |
110 |
111 |
124 |
125 |
138 |
139 |
152 |
153 |
162 |
163 |
180 |
181 |
199 |
200 |
209 |
210 |
219 |
220 |
221 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyojinKim/ARCore-Data-Logger/7ca9ea331313b36cb00ef2e3a28908540ea2b126/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyojinKim/ARCore-Data-Logger/7ca9ea331313b36cb00ef2e3a28908540ea2b126/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyojinKim/ARCore-Data-Logger/7ca9ea331313b36cb00ef2e3a28908540ea2b126/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyojinKim/ARCore-Data-Logger/7ca9ea331313b36cb00ef2e3a28908540ea2b126/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyojinKim/ARCore-Data-Logger/7ca9ea331313b36cb00ef2e3a28908540ea2b126/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyojinKim/ARCore-Data-Logger/7ca9ea331313b36cb00ef2e3a28908540ea2b126/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyojinKim/ARCore-Data-Logger/7ca9ea331313b36cb00ef2e3a28908540ea2b126/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyojinKim/ARCore-Data-Logger/7ca9ea331313b36cb00ef2e3a28908540ea2b126/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyojinKim/ARCore-Data-Logger/7ca9ea331313b36cb00ef2e3a28908540ea2b126/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyojinKim/ARCore-Data-Logger/7ca9ea331313b36cb00ef2e3a28908540ea2b126/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyojinKim/ARCore-Data-Logger/7ca9ea331313b36cb00ef2e3a28908540ea2b126/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyojinKim/ARCore-Data-Logger/7ca9ea331313b36cb00ef2e3a28908540ea2b126/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyojinKim/ARCore-Data-Logger/7ca9ea331313b36cb00ef2e3a28908540ea2b126/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyojinKim/ARCore-Data-Logger/7ca9ea331313b36cb00ef2e3a28908540ea2b126/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyojinKim/ARCore-Data-Logger/7ca9ea331313b36cb00ef2e3a28908540ea2b126/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/font_certs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - @array/com_google_android_gms_fonts_certs_dev
5 | - @array/com_google_android_gms_fonts_certs_prod
6 |
7 |
8 | -
9 | MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVyxW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8XW8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexAcKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkwHQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0cxb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrPzgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXclaXjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05aIskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+aayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUWEv9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs=
10 |
11 |
12 |
13 | -
14 | MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/values/preloaded_fonts.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - @font/roboto
5 | - @font/roboto_black
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | ARCore-Data-Logger
3 | Alert
4 | Start
5 | Ready
6 | Detach
7 | Stop
8 | File
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/pjinkim/arcore_data_logger/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.pjinkim.arcore_data_logger;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018 Google LLC
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 | // Top-level build file where you can add configuration options common to
16 | // all sub-projects/modules.
17 |
18 | buildscript {
19 | repositories {
20 | google()
21 | jcenter()
22 | mavenLocal()
23 | }
24 | dependencies {
25 | classpath 'com.android.tools.build:gradle:3.4.2'
26 | classpath 'com.google.ar.sceneform:plugin:1.11.0'
27 | // NOTE: Do not place your application dependencies here; they belong
28 | // in the individual module build.gradle files
29 | }
30 | }
31 |
32 | allprojects {
33 | repositories {
34 | google()
35 | jcenter()
36 | mavenLocal()
37 | }
38 | }
39 |
40 | task clean(type: Delete) {
41 | delete rootProject.buildDir
42 | }
43 |
--------------------------------------------------------------------------------
/data_visualization.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyojinKim/ARCore-Data-Logger/7ca9ea331313b36cb00ef2e3a28908540ea2b126/data_visualization.png
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 |
21 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyojinKim/ARCore-Data-Logger/7ca9ea331313b36cb00ef2e3a28908540ea2b126/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Aug 30 14:53:17 PDT 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PyojinKim/ARCore-Data-Logger/7ca9ea331313b36cb00ef2e3a28908540ea2b126/screenshot.png
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------