commit a0f877be483b857a586671609c020ee4cf0d863d Author: Nick Fisher Date: Wed Sep 15 20:07:11 2021 +0800 first commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..352f1492 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.a filter=lfs diff=lfs merge=lfs -text diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..41cc7d81 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..ba75c69f --- /dev/null +++ b/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/README.md b/README.md new file mode 100644 index 00000000..5954fcde --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +# mimetic_filament + +A new flutter plugin project. + +## Getting Started + +This project is a starting point for a Flutter +[plug-in package](https://flutter.dev/developing-packages/), +a specialized package that includes platform-specific implementation code for +Android and/or iOS. + +For help getting started with Flutter, view our +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. + +The plugin project was generated without specifying the `--platforms` flag, no platforms are currently supported. +To add platforms, run `flutter create -t plugin --platforms .` under the same +directory. You can also find a detailed instruction on how to add platforms in the `pubspec.yaml` at https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin-platforms. diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 00000000..a5744c1c --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 00000000..c6cbe562 --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,8 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 00000000..f3639bde --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,50 @@ +group 'com.example.mimetic_filament' +version '1.0-SNAPSHOT' + +buildscript { + ext.kotlin_version = '1.3.50' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:4.1.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +rootProject.allprojects { + repositories { + google() + mavenCentral() + } +} + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +android { + compileSdkVersion 30 + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + defaultConfig { + minSdkVersion 16 + } +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 00000000..94adc3a3 --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..3c9d0852 --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 00000000..d593e549 --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'mimetic_filament' diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml new file mode 100644 index 00000000..b47e7009 --- /dev/null +++ b/android/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + diff --git a/android/src/main/kotlin/com/example/mimetic_avatar/MimeticAvatarPlugin.kt b/android/src/main/kotlin/com/example/mimetic_avatar/MimeticAvatarPlugin.kt new file mode 100644 index 00000000..b4147e78 --- /dev/null +++ b/android/src/main/kotlin/com/example/mimetic_avatar/MimeticAvatarPlugin.kt @@ -0,0 +1,35 @@ +package com.example.mimetic_filament + +import androidx.annotation.NonNull + +import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel +import io.flutter.plugin.common.MethodChannel.MethodCallHandler +import io.flutter.plugin.common.MethodChannel.Result + +/** MimeticAvatarPlugin */ +class MimeticAvatarPlugin: FlutterPlugin, MethodCallHandler { + /// The MethodChannel that will the communication between Flutter and native Android + /// + /// This local reference serves to register the plugin with the Flutter Engine and unregister it + /// when the Flutter Engine is detached from the Activity + private lateinit var channel : MethodChannel + + override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { + channel = MethodChannel(flutterPluginBinding.binaryMessenger, "mimetic_filament") + channel.setMethodCallHandler(this) + } + + override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { + if (call.method == "getPlatformVersion") { + result.success("Android ${android.os.Build.VERSION.RELEASE}") + } else { + result.notImplemented() + } + } + + override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { + channel.setMethodCallHandler(null) + } +} diff --git a/android/src/main/kotlin/com/example/mimetic_filament/MimeticFilamentPlugin.kt b/android/src/main/kotlin/com/example/mimetic_filament/MimeticFilamentPlugin.kt new file mode 100644 index 00000000..db222949 --- /dev/null +++ b/android/src/main/kotlin/com/example/mimetic_filament/MimeticFilamentPlugin.kt @@ -0,0 +1,35 @@ +package com.example.mimetic_filament + +import androidx.annotation.NonNull + +import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel +import io.flutter.plugin.common.MethodChannel.MethodCallHandler +import io.flutter.plugin.common.MethodChannel.Result + +/** MimeticFilamentPlugin */ +class MimeticFilamentPlugin: FlutterPlugin, MethodCallHandler { + /// The MethodChannel that will the communication between Flutter and native Android + /// + /// This local reference serves to register the plugin with the Flutter Engine and unregister it + /// when the Flutter Engine is detached from the Activity + private lateinit var channel : MethodChannel + + override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { + channel = MethodChannel(flutterPluginBinding.binaryMessenger, "mimetic_filament") + channel.setMethodCallHandler(this) + } + + override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { + if (call.method == "getPlatformVersion") { + result.success("Android ${android.os.Build.VERSION.RELEASE}") + } else { + result.notImplemented() + } + } + + override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { + channel.setMethodCallHandler(null) + } +} diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 00000000..0fa6b675 --- /dev/null +++ b/example/.gitignore @@ -0,0 +1,46 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/example/.metadata b/example/.metadata new file mode 100644 index 00000000..e8ac3216 --- /dev/null +++ b/example/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: 19c61fed0da681ba2068c97e22add3bde38e51e4 + channel: beta + +project_type: app diff --git a/example/README.md b/example/README.md new file mode 100644 index 00000000..b94554b3 --- /dev/null +++ b/example/README.md @@ -0,0 +1,16 @@ +# mimetic_filament_example + +Demonstrates how to use the mimetic_filament plugin. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) + +For help getting started with Flutter, view our +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml new file mode 100644 index 00000000..61b6c4de --- /dev/null +++ b/example/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/example/android/.gitignore b/example/android/.gitignore new file mode 100644 index 00000000..6f568019 --- /dev/null +++ b/example/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle new file mode 100644 index 00000000..4abd36fc --- /dev/null +++ b/example/android/app/build.gradle @@ -0,0 +1,68 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 30 + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.example.mimetic_filament_example" + minSdkVersion 16 + targetSdkVersion 30 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 00000000..7b9c026e --- /dev/null +++ b/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..9a6f1b89 --- /dev/null +++ b/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + diff --git a/example/android/app/src/main/kotlin/com/example/mimetic_avatar_example/MainActivity.kt b/example/android/app/src/main/kotlin/com/example/mimetic_avatar_example/MainActivity.kt new file mode 100644 index 00000000..071e420a --- /dev/null +++ b/example/android/app/src/main/kotlin/com/example/mimetic_avatar_example/MainActivity.kt @@ -0,0 +1,6 @@ +package com.example.mimetic_filament_example + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/example/android/app/src/main/kotlin/com/example/mimetic_filament_example/MainActivity.kt b/example/android/app/src/main/kotlin/com/example/mimetic_filament_example/MainActivity.kt new file mode 100644 index 00000000..071e420a --- /dev/null +++ b/example/android/app/src/main/kotlin/com/example/mimetic_filament_example/MainActivity.kt @@ -0,0 +1,6 @@ +package com.example.mimetic_filament_example + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/example/android/app/src/main/res/drawable-v21/launch_background.xml b/example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 00000000..f74085f3 --- /dev/null +++ b/example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/example/android/app/src/main/res/drawable/launch_background.xml b/example/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 00000000..304732f8 --- /dev/null +++ b/example/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 00000000..db77bb4b Binary files /dev/null and b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 00000000..17987b79 Binary files /dev/null and b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 00000000..09d43914 Binary files /dev/null and b/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 00000000..d5f1c8d3 Binary files /dev/null and b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 00000000..4d6372ee Binary files /dev/null and b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/values-night/styles.xml b/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 00000000..449a9f93 --- /dev/null +++ b/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml new file mode 100644 index 00000000..d74aa35c --- /dev/null +++ b/example/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/example/android/app/src/profile/AndroidManifest.xml b/example/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 00000000..7b9c026e --- /dev/null +++ b/example/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/example/android/build.gradle b/example/android/build.gradle new file mode 100644 index 00000000..ed45c658 --- /dev/null +++ b/example/android/build.gradle @@ -0,0 +1,29 @@ +buildscript { + ext.kotlin_version = '1.3.50' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:4.1.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/example/android/gradle.properties b/example/android/gradle.properties new file mode 100644 index 00000000..94adc3a3 --- /dev/null +++ b/example/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..bc6a58af --- /dev/null +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jun 23 08:50:38 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip diff --git a/example/android/settings.gradle b/example/android/settings.gradle new file mode 100644 index 00000000..44e62bcf --- /dev/null +++ b/example/android/settings.gradle @@ -0,0 +1,11 @@ +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/example/assets/BusterDrone/scene.bin b/example/assets/BusterDrone/scene.bin new file mode 100644 index 00000000..2f55da9e Binary files /dev/null and b/example/assets/BusterDrone/scene.bin differ diff --git a/example/assets/BusterDrone/scene.gltf b/example/assets/BusterDrone/scene.gltf new file mode 100644 index 00000000..7a554b86 --- /dev/null +++ b/example/assets/BusterDrone/scene.gltf @@ -0,0 +1,11876 @@ +{ + "accessors": [ + { + "bufferView": 0, + "byteOffset": 490368, + "componentType": 5126, + "count": 13, + "max": [ + 2.603024959564209, + 2.6610217094421387, + 0.004141807556152344 + ], + "min": [ + -2.603024959564209, + -2.6610217094421387, + -0.004141807556152344 + ], + "type": "VEC3" + }, + { + "bufferView": 0, + "byteOffset": 490524, + "componentType": 5126, + "count": 13, + "max": [ + 0.000011948728570132516, + 0.000011688098311424255, + 1.1920928955078125e-7 + ], + "min": [ + -0.000011948726751143113, + -0.00001168833114206791, + -1.1920928955078125e-7 + ], + "type": "VEC3" + }, + { + "bufferView": 0, + "byteOffset": 490680, + "componentType": 5126, + "count": 13, + "max": [ + 0.5079542994499207, + 0.44970205426216125, + 0.0007009506225585938 + ], + "min": [ + -0.5079542994499207, + -0.44970205426216125, + -0.0007009506225585938 + ], + "type": "VEC3" + }, + { + "bufferView": 0, + "byteOffset": 490836, + "componentType": 5126, + "count": 13, + "max": [ + 0.000027996353310300037, + 0.000027456902898848057, + 1.1920928955078125e-7 + ], + "min": [ + -0.000027996351491310634, + -0.00002745713572949171, + -5.960464477539063e-8 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "componentType": 5126, + "count": 291, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR", + "byteOffset": 0 + }, + { + "bufferView": 2, + "componentType": 5126, + "count": 291, + "max": [ + 0.766411542892456, + -100, + 34.548770904541016 + ], + "min": [ + 0, + -100, + -92.53174591064453 + ], + "type": "VEC3", + "byteOffset": 0 + }, + { + "bufferView": 1, + "byteOffset": 1164, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 3492, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 1172, + "componentType": 5126, + "count": 528, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "componentType": 5126, + "count": 528, + "max": [ + 3.018394628351309e-17, + 0.4277789890766144, + 1.0350706219500598e-16, + 1 + ], + "min": [ + -5.146233374338227e-17, + -0.4226182699203491, + -8.386759746129016e-17, + 0.9038833975791931 + ], + "type": "VEC4", + "byteOffset": 0 + }, + { + "bufferView": 1, + "byteOffset": 3284, + "componentType": 5126, + "count": 1025, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 3516, + "componentType": 5126, + "count": 1025, + "max": [ + 1.0658141036401503e-14, + 0.00003388613185961731, + 0.000014036095308256336 + ], + "min": [ + -7.105427357601002e-15, + 0.00003388613185961731, + 0.000014036095308256336 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 7384, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 15816, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 7392, + "componentType": 5126, + "count": 1141, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 8448, + "componentType": 5126, + "count": 1141, + "max": [ + 0.5891698598861694, + 9.724578803145729e-17, + 1.0542189266883763e-16, + 0.9999997019767761 + ], + "min": [ + -0.10293252021074295, + -1.8182581299328222e-16, + -1.0802358083610882e-16, + 0.8080092072486877 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 11956, + "componentType": 5126, + "count": 199, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 15840, + "componentType": 5126, + "count": 199, + "max": [ + -0.01574668288230896, + -7.34735107421875, + 0.081717349588871 + ], + "min": [ + -0.01574668288230896, + -11.282759666442871, + 0.081717349588871 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 12752, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 18228, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 12760, + "componentType": 5126, + "count": 1141, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 26704, + "componentType": 5126, + "count": 1141, + "max": [ + 8.326672684688674e-17, + 8.326672684688674e-17, + 9.71445146547012e-17, + 1 + ], + "min": [ + -1.3877787807814457e-16, + -1.1102230246251565e-16, + -5.898059818321144e-17, + 1 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 17324, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 18252, + "componentType": 5126, + "count": 2, + "max": [ + 0, + 0, + 0 + ], + "min": [ + 0, + 0, + 0 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 17332, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 18276, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 17340, + "componentType": 5126, + "count": 1141, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 44960, + "componentType": 5126, + "count": 1141, + "max": [ + 6.941919929111294e-16, + 0.9612559676170349, + 0.8191520571708679, + 1.4523389702766784e-16 + ], + "min": [ + -7.690622409705619e-16, + -0.6991849541664124, + -0.6942344903945923, + -1.3729081909501104e-16 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 21904, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 18300, + "componentType": 5126, + "count": 2, + "max": [ + 0.00007688588812015951, + -22.9158935546875, + 0.02676698938012123 + ], + "min": [ + 0.00007688588812015951, + -22.9158935546875, + 0.02676698938012123 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 21912, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 18324, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 21920, + "componentType": 5126, + "count": 1141, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 63216, + "componentType": 5126, + "count": 1141, + "max": [ + -3.559834546828236e-23, + -4.1108839354819793e-7, + 4.2806183109312906e-17, + 1 + ], + "min": [ + -3.357394022152527e-16, + -0.130526602268219, + -2.134869384146925e-16, + 0.9914448261260986 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 26484, + "componentType": 5126, + "count": 1016, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 18348, + "componentType": 5126, + "count": 1016, + "max": [ + 1.4210854715202004e-14, + -0.00004080753933521919, + 7.105427357601002e-15 + ], + "min": [ + -7.105427357601002e-15, + -0.00004080753933521919, + -3.552713678800501e-15 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 30548, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 30540, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 30556, + "componentType": 5126, + "count": 1132, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 81472, + "componentType": 5126, + "count": 1132, + "max": [ + 0.4777144193649292, + 0.696681559085846, + -0.12097440659999847, + 0.696681559085846 + ], + "min": [ + 0.12097441405057907, + 0.5213338136672974, + -0.4777144193649292, + 0.5213338136672974 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 35084, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 30564, + "componentType": 5126, + "count": 2, + "max": [ + 0.006630018353462219, + -5.510501384735107, + -0.09275053441524506 + ], + "min": [ + 0.006630018353462219, + -5.510501384735107, + -0.09275053441524506 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 35092, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 30588, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 35100, + "componentType": 5126, + "count": 1094, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 99584, + "componentType": 5126, + "count": 1094, + "max": [ + 3.45120668043819e-7, + 0.755711019039154, + 3.411899740513036e-7, + 0.7933533191680908 + ], + "min": [ + -3.018524452613747e-8, + 0.6087614297866821, + -3.14133004053474e-8, + 0.6549052000045776 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 39476, + "componentType": 5126, + "count": 1058, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 30612, + "componentType": 5126, + "count": 1058, + "max": [ + -9.947598300641403e-14, + -0.000002192010470025707, + -9.079604410544562e-7 + ], + "min": [ + -1.2789769243681803e-13, + -0.000002192010470025707, + -9.07960497897875e-7 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 43708, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 43308, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 43716, + "componentType": 5126, + "count": 1140, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 117088, + "componentType": 5126, + "count": 1140, + "max": [ + 0.648327112197876, + 3.148856819734647e-8, + -2.9930738776329235e-8, + 1 + ], + "min": [ + -0.02535569667816162, + 3.4723754982479704e-9, + -4.330498626359258e-8, + 0.7613618969917297 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 48276, + "componentType": 5126, + "count": 199, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 43332, + "componentType": 5126, + "count": 199, + "max": [ + 0.010783359408378601, + -7.360301494598389, + 0.08223113417625427 + ], + "min": [ + 0.010783359408378601, + -11.28590202331543, + 0.08223113417625427 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 49072, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 45720, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 49080, + "componentType": 5126, + "count": 1141, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 135328, + "componentType": 5126, + "count": 1141, + "max": [ + 0.0000031865668006503256, + 1.1102240834163406e-16, + 8.326664082010304e-17, + 1 + ], + "min": [ + 0.0000031865668006503256, + -1.3877787807814457e-16, + -1.1102234216718506e-16, + 1 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 53644, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 45744, + "componentType": 5126, + "count": 2, + "max": [ + 0.004904936067759991, + 0.0029711532406508923, + -0.0014322279021143913 + ], + "min": [ + 0.004904936067759991, + 0.0029711532406508923, + -0.0014322279021143913 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 53652, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 45768, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 53660, + "componentType": 5126, + "count": 1141, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 153584, + "componentType": 5126, + "count": 1141, + "max": [ + 2.946721479681508e-16, + 0.9721843600273132, + 0.7976794838905334, + 1.0801055108709988e-16 + ], + "min": [ + -3.3080178246791135e-16, + -0.7050051093101501, + -0.706916868686676, + -1.4904984662949747e-16 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 58224, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 45792, + "componentType": 5126, + "count": 2, + "max": [ + 0.002689761109650135, + -22.916383743286133, + 0.02693275175988674 + ], + "min": [ + 0.002689761109650135, + -22.916383743286133, + 0.02693275175988674 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 58232, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 45816, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 58240, + "componentType": 5126, + "count": 1141, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 171840, + "componentType": 5126, + "count": 1141, + "max": [ + 5.287900693557693e-16, + 0.0033429425675421953, + 1.98237960436973e-15, + 1 + ], + "min": [ + 2.0879800224670911e-16, + -0.13052618503570557, + -1.0767316595280767e-15, + 0.9914448857307434 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 62804, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 45840, + "componentType": 5126, + "count": 2, + "max": [ + 0, + 0, + 0 + ], + "min": [ + 0, + 0, + 0 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 62812, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 45864, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 62820, + "componentType": 5126, + "count": 1141, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 190096, + "componentType": 5126, + "count": 1141, + "max": [ + 0.6187881231307983, + 1.357208038226058e-16, + 1.405753349317774e-16, + 0.9999972581863403 + ], + "min": [ + 0.0023439584765583277, + -1.4041348546440282e-16, + -1.441803997995203e-16, + 0.7855579257011414 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 67384, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 45888, + "componentType": 5126, + "count": 2, + "max": [ + 2.4154791831970215, + -4.842290878295898, + -0.006652138661593199 + ], + "min": [ + 2.4154791831970215, + -4.842290878295898, + -0.006652138661593199 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 67392, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 45912, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 67400, + "componentType": 5126, + "count": 1074, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 208352, + "componentType": 5126, + "count": 1074, + "max": [ + 2.2694037404499823e-13, + 0.7933533191680908, + 5.5178372315072347e-14, + 0.7933533191680908 + ], + "min": [ + -5.5019523144276064e-14, + 0.6087614297866821, + -2.269895290609933e-13, + 0.6087614297866821 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 71696, + "componentType": 5126, + "count": 23, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 45936, + "componentType": 5126, + "count": 23, + "max": [ + 0.6785002946853638, + -0.37506112456321716, + 4.957866191864014 + ], + "min": [ + 0.6785002946853638, + -0.37506112456321716, + 1.9578661918640137 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 71788, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 46212, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 71796, + "componentType": 5126, + "count": 1058, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 225536, + "componentType": 5126, + "count": 1058, + "max": [ + 1.3877787807814457e-16, + 2.62250594573743e-8, + 7.2789162079351655e-25, + 1 + ], + "min": [ + -1.614377034830916e-16, + 2.62250594573743e-8, + -1.942890293094024e-16, + 1 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 76028, + "componentType": 5126, + "count": 23, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 46236, + "componentType": 5126, + "count": 23, + "max": [ + -19.23712730407715, + -26.57673454284668, + -0.748199999332428 + ], + "min": [ + -26.78019905090332, + -31.10906982421875, + -0.748199999332428 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 76120, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 46512, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 76128, + "componentType": 5126, + "count": 913, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 242464, + "componentType": 5126, + "count": 913, + "max": [ + 0.1889660805463791, + -0.6813896298408508, + 0.1889660656452179, + 0.6813896298408508 + ], + "min": [ + 0.1889660805463791, + -0.6813896298408508, + 0.1889660656452179, + 0.6813896298408508 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 79780, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 46536, + "componentType": 5126, + "count": 2, + "max": [ + 0.01226816326379776, + 0.00024394220963586122, + 0.0002562310837674886 + ], + "min": [ + 0.01226816326379776, + 0.00024394220963586122, + 0.0002562310837674886 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 79788, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 46560, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 79796, + "componentType": 5126, + "count": 1141, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 257072, + "componentType": 5126, + "count": 1141, + "max": [ + 0.7071067690849304, + 2.4532694660665106e-16, + 2.0842673711801571e-16, + 0.9999963045120239 + ], + "min": [ + -0.03856799378991127, + -1.783470619137986e-16, + -1.6837262692005858e-16, + 0.7071067690849304 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 84360, + "componentType": 5126, + "count": 199, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 46584, + "componentType": 5126, + "count": 199, + "max": [ + 0.1428087055683136, + -7.303253173828125, + 0.08181961625814438 + ], + "min": [ + 0.05688011273741722, + -11.236658096313477, + 0.08181961625814438 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 85156, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 48972, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 85164, + "componentType": 5126, + "count": 1141, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 275328, + "componentType": 5126, + "count": 1141, + "max": [ + 0.000012702583262580447, + 0.000003925847522623371, + -0.010920972563326359, + 0.9999403357505798 + ], + "min": [ + 0.000012702583262580447, + 0.000003925847522623371, + -0.010920972563326359, + 0.9999403357505798 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 89728, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 48996, + "componentType": 5126, + "count": 2, + "max": [ + -0.004460121039301157, + 0.000007818744961696211, + 0.00005186478665564209 + ], + "min": [ + -0.004460121039301157, + 0.000007818744961696211, + 0.00005186478665564209 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 89736, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 49020, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 89744, + "componentType": 5126, + "count": 1141, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 293584, + "componentType": 5126, + "count": 1141, + "max": [ + 3.20862724334609e-16, + 0.9717717170715332, + 0.7435351014137268, + 1.614297890189907e-16 + ], + "min": [ + -5.413126573875361e-16, + -0.705666184425354, + -0.6964223980903625, + -8.255817716906362e-17 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 94308, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 49044, + "componentType": 5126, + "count": 2, + "max": [ + 0.0002644560590852052, + -22.915939331054688, + 0.026753252372145653 + ], + "min": [ + 0.0002644560590852052, + -22.915939331054688, + 0.026753252372145653 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 94316, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 49068, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 94324, + "componentType": 5126, + "count": 1141, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 311840, + "componentType": 5126, + "count": 1141, + "max": [ + 1.195374466078647e-9, + 0.1054544672369957, + 2.520133251659052e-14, + 1 + ], + "min": [ + -3.591690217505763e-16, + -0.13052618503570557, + -3.68308051971241e-15, + 0.9914448857307434 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 98888, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 49092, + "componentType": 5126, + "count": 2, + "max": [ + 0, + 0, + 0 + ], + "min": [ + 0, + 0, + 0 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 98896, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 49116, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 98904, + "componentType": 5126, + "count": 1141, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 330096, + "componentType": 5126, + "count": 1141, + "max": [ + 0.5984764695167542, + 1.7411764252628359e-9, + 1.0605889453239737e-16, + 1 + ], + "min": [ + -0.014889945276081562, + -8.390695802355788e-17, + -2.3159511641779318e-8, + 0.8011403679847717 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 103468, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 49140, + "componentType": 5126, + "count": 2, + "max": [ + -2.415479898452759, + -4.84229040145874, + -0.006640426814556122 + ], + "min": [ + -2.415479898452759, + -4.84229040145874, + -0.006640426814556122 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 103476, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 49164, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 103484, + "componentType": 5126, + "count": 1078, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 348352, + "componentType": 5126, + "count": 1078, + "max": [ + 3.052628814873351e-14, + -0.6410675048828125, + 3.0506660701279734e-14, + 0.7674844861030579 + ], + "min": [ + -6.372444347375883e-14, + -0.7933533191680908, + -6.362619442814091e-14, + 0.6087614297866821 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 107796, + "componentType": 5126, + "count": 29, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 49188, + "componentType": 5126, + "count": 29, + "max": [ + -0.6784976124763489, + -0.3758923411369324, + 4.957706451416016 + ], + "min": [ + -0.6784976124763489, + -0.37716609239578247, + 1.9577068090438843 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 107912, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 49536, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 107920, + "componentType": 5126, + "count": 1058, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 365600, + "componentType": 5126, + "count": 1058, + "max": [ + 0.0002122838923241943, + 2.747205307684908e-8, + -5.831902235436948e-12, + 1 + ], + "min": [ + 0.0002122838923241943, + 2.747205307684908e-8, + -5.832034074421122e-12, + 1 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 112152, + "componentType": 5126, + "count": 23, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 49560, + "componentType": 5126, + "count": 23, + "max": [ + 26.78019905090332, + -26.57673454284668, + -0.748199999332428 + ], + "min": [ + 19.23712730407715, + -31.10906982421875, + -0.748199999332428 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 112244, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 49836, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 112252, + "componentType": 5126, + "count": 966, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 382528, + "componentType": 5126, + "count": 966, + "max": [ + 0.1889660805463791, + 0.6813896298408508, + -0.1889660656452179, + 0.6813896298408508 + ], + "min": [ + 0.1889660805463791, + 0.6813896298408508, + -0.1889660656452179, + 0.6813896298408508 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 116116, + "componentType": 5126, + "count": 27, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 49860, + "componentType": 5126, + "count": 27, + "max": [ + -22.55685806274414, + -8.625173568725586, + 0.14466851949691772 + ], + "min": [ + -28.345199584960938, + -10.20475959777832, + 0.14466851949691772 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 116224, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 50184, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 116232, + "componentType": 5126, + "count": 958, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 397984, + "componentType": 5126, + "count": 958, + "max": [ + 0.09390988200902939, + -0.7008429765701294, + 0.09390988200902939, + 0.7008430361747742 + ], + "min": [ + 0.09390988200902939, + -0.7008429765701294, + 0.09390988200902939, + 0.7008430361747742 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 120064, + "componentType": 5126, + "count": 27, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 50208, + "componentType": 5126, + "count": 27, + "max": [ + 28.230527877807617, + -9.64864730834961, + -0.07694999873638153 + ], + "min": [ + 22.442184448242188, + -11.228233337402344, + -0.07694999873638153 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 120172, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 50532, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 120180, + "componentType": 5126, + "count": 971, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 413312, + "componentType": 5126, + "count": 971, + "max": [ + 0.09390988200902939, + 0.7008429765701294, + -0.09390988200902939, + 0.7008430361747742 + ], + "min": [ + 0.09390988200902939, + 0.7008429765701294, + -0.09390988200902939, + 0.7008430361747742 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 124064, + "componentType": 5126, + "count": 859, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 50556, + "componentType": 5126, + "count": 859, + "max": [ + 5.329070518200751e-15, + 38.9765510559082, + -3.073050022125244 + ], + "min": [ + -7.105427357601002e-15, + 33.4765510559082, + -3.073050022125244 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 127500, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 60864, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 127508, + "componentType": 5126, + "count": 998, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 428848, + "componentType": 5126, + "count": 998, + "max": [ + 5.551115123125783e-17, + 1.1102230246251565e-16, + 5.551115123125783e-17, + 1 + ], + "min": [ + -6.938893903907228e-17, + -8.326672684688674e-17, + -5.551115123125783e-17, + 1 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 131500, + "componentType": 5126, + "count": 824, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 60888, + "componentType": 5126, + "count": 824, + "max": [ + 3.552713678800501e-15, + -0.018226023763418198, + -8.908537864685059 + ], + "min": [ + -7.105427357601002e-15, + -0.018226023763418198, + -8.908537864685059 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 134796, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 70776, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 134804, + "componentType": 5126, + "count": 1142, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 444816, + "componentType": 5126, + "count": 1142, + "max": [ + 0.002140095690265298, + 1, + 0.04749816656112671, + 0.08700844645500183 + ], + "min": [ + -0.002407652325928211, + 0.9933873414993286, + -0.02874644845724106, + -0.11379626393318176 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 139372, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 70800, + "componentType": 5126, + "count": 2, + "max": [ + -0.000021889296476729214, + -36.155147552490234, + -10.966755867004395 + ], + "min": [ + -0.000021889296476729214, + -36.155147552490234, + -10.966755867004395 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 139380, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 70824, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 139388, + "componentType": 5126, + "count": 1216, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 463088, + "componentType": 5126, + "count": 1216, + "max": [ + 0.8032023310661316, + 0.6148953437805176, + 0.8032018542289734, + 0.614924967288971 + ], + "min": [ + -0.5634948015213013, + -0.4302464425563812, + -0.5672827363014221, + -0.4291868805885315 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 144252, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 70848, + "componentType": 5126, + "count": 2, + "max": [ + 19.973400115966797, + 24.55685043334961, + -6.007046699523926 + ], + "min": [ + 19.973400115966797, + 24.55685043334961, + -6.007046699523926 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 144260, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 70872, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 144268, + "componentType": 5126, + "count": 1174, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 482544, + "componentType": 5126, + "count": 1174, + "max": [ + 0.6970846056938171, + 0.6561663746833801, + 0.6592297554016113, + 0.6099482774734497 + ], + "min": [ + -0.6526466608047485, + -0.652332067489624, + -0.6243375539779663, + -0.49005988240242004 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 148964, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 70896, + "componentType": 5126, + "count": 2, + "max": [ + 0.000051651491958182305, + -36.1551513671875, + -10.966744422912598 + ], + "min": [ + 0.000051651491958182305, + -36.1551513671875, + -10.966744422912598 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 148972, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 70920, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 148980, + "componentType": 5126, + "count": 1216, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 501328, + "componentType": 5126, + "count": 1216, + "max": [ + 0.8032023310661316, + 0.6148953437805176, + 0.8032018542289734, + 0.614924967288971 + ], + "min": [ + -0.5647227764129639, + -0.45769572257995605, + -0.567910373210907, + -0.431105375289917 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 153844, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 70944, + "componentType": 5126, + "count": 2, + "max": [ + -19.973400115966797, + 24.556848526000977, + -6.007049560546875 + ], + "min": [ + -19.973400115966797, + 24.556848526000977, + -6.007049560546875 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 153852, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 70968, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 153860, + "componentType": 5126, + "count": 1174, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 520784, + "componentType": 5126, + "count": 1174, + "max": [ + 0.697971761226654, + 0.5453841686248779, + 0.6412535309791565, + 0.6099480986595154 + ], + "min": [ + -0.49868083000183105, + -0.5850634574890137, + -0.5260052680969238, + -0.4971546530723572 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 158556, + "componentType": 5126, + "count": 1044, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 70992, + "componentType": 5126, + "count": 1044, + "max": [ + 0.30994635820388794, + 88.4124526977539, + 3.357822895050049 + ], + "min": [ + 0, + 0.11856790632009506, + -12.910046577453613 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 162732, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 83520, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 162740, + "componentType": 5126, + "count": 1059, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 539568, + "componentType": 5126, + "count": 1059, + "max": [ + 0.8156683444976807, + 0.10349708050489426, + 0.07732612639665604, + 0.7898205518722534 + ], + "min": [ + 0.6133379936218262, + -0.15155479311943054, + -0.13645820319652557, + 0.5717355012893677 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 166976, + "componentType": 5126, + "count": 279, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 83544, + "componentType": 5126, + "count": 279, + "max": [ + 0, + 0, + 21.310029983520508 + ], + "min": [ + 0, + 0, + -42.39506530761719 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 168092, + "componentType": 5126, + "count": 2, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 2, + "byteOffset": 86892, + "componentType": 5126, + "count": 2, + "max": [ + 1, + 1, + 1 + ], + "min": [ + 1, + 1, + 1 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "byteOffset": 168100, + "componentType": 5126, + "count": 367, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 3, + "byteOffset": 556512, + "componentType": 5126, + "count": 367, + "max": [ + 8.359196102885279e-17, + 0.9999496340751648, + 8.359196102885279e-17, + 1 + ], + "min": [ + -8.659445459885287e-17, + -0.8649336099624634, + -8.659445459885287e-17, + -0.4979607164859772 + ], + "type": "VEC4" + }, + { + "bufferView": 1, + "byteOffset": 169568, + "componentType": 5126, + "count": 509, + "max": [ + 25 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "bufferView": 1, + "byteOffset": 171604, + "componentType": 5126, + "count": 1018, + "max": [ + 1 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5125, + "count": 18, + "max": [ + 6 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 7, + "max": [ + 0, + 0, + -1 + ], + "min": [ + 0, + 0, + -1 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 7, + "max": [ + 180, + 155.8845672607422, + 0 + ], + "min": [ + -180, + -155.8845672607422, + 0 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 7, + "max": [ + 1, + 6.123234262925839e-17, + 0, + 1 + ], + "min": [ + 1, + 0, + 0, + 1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 7, + "max": [ + 1, + 0.9330127239227295 + ], + "min": [ + 0, + 0.06698727607727051 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 7, + "max": [ + 1, + 0.9330127239227295 + ], + "min": [ + 0, + 0.06698727607727051 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 6624, + "max": [ + 1597 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 1598, + "max": [ + 0.9999938607215881, + 1, + 1 + ], + "min": [ + -0.9999938607215881, + -1, + -1 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 1598, + "max": [ + 24.47800064086914, + 40.60210037231445, + 43.78879928588867 + ], + "min": [ + -24.47800064086914, + -39.97100067138672, + -32.2958984375 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 1598, + "max": [ + 1, + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1, + -1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 1598, + "max": [ + 0.9494781494140625, + 0.9984400272369385 + ], + "min": [ + 0.050515174865722656, + 0.00115966796875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1598, + "max": [ + 0.9494781494140625, + 0.9984400272369385 + ], + "min": [ + 0.050515174865722656, + 0.00115966796875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1598, + "max": [ + 0.9494781494140625, + 0.9984400272369385 + ], + "min": [ + 0.050515174865722656, + 0.00115966796875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1598, + "max": [ + 0.9494781494140625, + 0.9984400272369385 + ], + "min": [ + 0.050515174865722656, + 0.00115966796875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1598, + "max": [ + 0.9494781494140625, + 0.9984400272369385 + ], + "min": [ + 0.050515174865722656, + 0.00115966796875 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 192, + "max": [ + 59 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.9462724328041077, + 0.00002637715806486085, + 0.9238796234130859 + ], + "min": [ + -0.9462687969207764, + -0.9339421987533569, + -0.9238765835762024 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 3.158555030822754, + 0, + 2.7355499267578125 + ], + "min": [ + -3.158555030822754, + -4.489299774169922, + -2.7355499267578125 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.6912197470664978, + 0.8588380813598633, + 0.9905509352684021, + 1 + ], + "min": [ + -0.6913442611694336, + -1, + -0.6705939173698425, + 1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.9791259765625, + 0.6324005126953125 + ], + "min": [ + 0.0011079907417297363, + 0.229583740234375 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.9791259765625, + 0.6324005126953125 + ], + "min": [ + 0.0011079907417297363, + 0.229583740234375 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.9791259765625, + 0.6324005126953125 + ], + "min": [ + 0.0011079907417297363, + 0.229583740234375 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.9791259765625, + 0.6324005126953125 + ], + "min": [ + 0.0011079907417297363, + 0.229583740234375 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 2736, + "max": [ + 668 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 2.8378965854644775, + 2.837851047515869, + 3.1812357902526855 + ], + "min": [ + -2.8378965854644775, + -2.837851047515869, + -3.1812357902526855 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9705880284309387, + 0.970474362373352, + 0.9334462881088257, + 1 + ], + "min": [ + -0.9705855250358582, + -0.9705935716629028, + -0.9999987483024597, + 1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.98150634765625, + 0.9824953079223633 + ], + "min": [ + 0.011077880859375, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.98150634765625, + 0.9824953079223633 + ], + "min": [ + 0.011077880859375, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.98150634765625, + 0.9824953079223633 + ], + "min": [ + 0.011077880859375, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.98150634765625, + 0.9824953079223633 + ], + "min": [ + 0.011077880859375, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 4872, + "max": [ + 1512 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 1513, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 1513, + "max": [ + 3.8741042613983154, + 1.7617741823196411, + 3.0504395961761475 + ], + "min": [ + -3.9423036575317383, + -22.016210556030273, + -4.033937454223633 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 1513, + "max": [ + 1, + 0.9999995827674866, + 0.9992985129356384, + 1 + ], + "min": [ + -1, + -1, + -0.9996975660324097, + 1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 1513, + "max": [ + 0.998046875, + 0.9980460405349731 + ], + "min": [ + 0.0011079907417297363, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1513, + "max": [ + 0.998046875, + 0.9980460405349731 + ], + "min": [ + 0.0011079907417297363, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1513, + "max": [ + 0.998046875, + 0.9980460405349731 + ], + "min": [ + 0.0011079907417297363, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1513, + "max": [ + 0.998046875, + 0.9980460405349731 + ], + "min": [ + 0.0011079907417297363, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 2736, + "max": [ + 668 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 2.805872678756714, + 2.503127098083496, + 2.5030744075775146 + ], + "min": [ + -2.8058857917785645, + -2.503126382827759, + -2.5030744075775146 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9999423027038574, + 0.9715058207511902, + 0.9705548882484436, + 1 + ], + "min": [ + -0.9334924221038818, + -0.970590353012085, + -0.970612645149231, + 1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.8618621826171875, + 0.997859001159668 + ], + "min": [ + 0.03627777099609375, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.8618621826171875, + 0.997859001159668 + ], + "min": [ + 0.03627777099609375, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.8618621826171875, + 0.997859001159668 + ], + "min": [ + 0.03627777099609375, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.8618621826171875, + 0.997859001159668 + ], + "min": [ + 0.03627777099609375, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 4128, + "max": [ + 1189 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 1190, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 1190, + "max": [ + 6.140176773071289, + 7.9751200675964355, + 9.091413497924805 + ], + "min": [ + -6.138557434082031, + -12.837050437927246, + -2.774056911468506 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 1190, + "max": [ + 0.9986715912818909, + 1, + 0.932191789150238, + 1 + ], + "min": [ + -1, + -1, + -0.9457818865776062, + 1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 1190, + "max": [ + 0.9821929931640625, + 0.9979920387268066 + ], + "min": [ + 0.0011079907417297363, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1190, + "max": [ + 0.9821929931640625, + 0.9979920387268066 + ], + "min": [ + 0.0011079907417297363, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1190, + "max": [ + 0.9821929931640625, + 0.9979920387268066 + ], + "min": [ + 0.0011079907417297363, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1190, + "max": [ + 0.9821929931640625, + 0.9979920387268066 + ], + "min": [ + 0.0011079907417297363, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 192, + "max": [ + 59 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.9462646245956421, + 0.00006607881368836388, + 0.9043453931808472 + ], + "min": [ + -0.9462745189666748, + -0.9273982048034668, + -0.904350757598877 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 2.1719820499420166, + -4.440892098500626e-16, + 2.284182071685791 + ], + "min": [ + -2.1719820499420166, + -8.425638198852539, + -2.284182071685791 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.6912132501602173, + 0.8564750552177429, + 0.9907793998718262, + 1 + ], + "min": [ + -0.6912415623664856, + -1, + -0.6705775260925293, + 1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.5562744140625, + 0.8839511871337891 + ], + "min": [ + 0.24591445922851562, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.5562744140625, + 0.8839511871337891 + ], + "min": [ + 0.24591445922851562, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.5562744140625, + 0.8839511871337891 + ], + "min": [ + 0.24591445922851562, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.5562744140625, + 0.8839511871337891 + ], + "min": [ + 0.24591445922851562, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 2736, + "max": [ + 668 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 2.805861473083496, + 2.503016948699951, + 2.503002166748047 + ], + "min": [ + -2.8058581352233887, + -2.5030174255371094, + -2.503002166748047 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9334709048271179, + 0.9943826198577881, + 0.9705744981765747, + 1 + ], + "min": [ + -0.9999973773956299, + -0.9915933012962341, + -0.9705973267555237, + 1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9977874755859375, + 0.9978030323982239 + ], + "min": [ + 0.011077880859375, + 0.0384979248046875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9977874755859375, + 0.9978030323982239 + ], + "min": [ + 0.011077880859375, + 0.0384979248046875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9977874755859375, + 0.9978030323982239 + ], + "min": [ + 0.011077880859375, + 0.0384979248046875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9977874755859375, + 0.9978030323982239 + ], + "min": [ + 0.011077880859375, + 0.0384979248046875 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 3792, + "max": [ + 1128 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 1129, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -0.7994152307510376, + -1 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 1129, + "max": [ + 6.352692127227783, + 7.272726058959961, + 6.740703105926514 + ], + "min": [ + -6.337584495544434, + -25.38535499572754, + -2.6904826164245605 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 1129, + "max": [ + 0.9445861577987671, + 1, + 0.9917267560958862, + 1 + ], + "min": [ + -0.999998927116394, + -1, + -0.9903827905654907, + 1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 1129, + "max": [ + 0.99798583984375, + 0.9980530142784119 + ], + "min": [ + 0.0011079907417297363, + 0.054290771484375 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1129, + "max": [ + 0.99798583984375, + 0.9980530142784119 + ], + "min": [ + 0.0011079907417297363, + 0.054290771484375 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1129, + "max": [ + 0.99798583984375, + 0.9980530142784119 + ], + "min": [ + 0.0011079907417297363, + 0.054290771484375 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1129, + "max": [ + 0.99798583984375, + 0.9980530142784119 + ], + "min": [ + 0.0011079907417297363, + 0.054290771484375 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 858, + "max": [ + 209 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 210, + "max": [ + 0.9999997615814209, + 0.9804182648658752, + 0.9717621207237244 + ], + "min": [ + -0.9999998211860657, + -0.9752709269523621, + -0.9717718958854675 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 210, + "max": [ + 22.505199432373047, + 16.118268966674805, + 0.014730009250342846 + ], + "min": [ + -19.017000198364258, + -16.7448787689209, + -12.467525482177734 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 210, + "max": [ + 0.7160800695419312, + 0.9986988306045532, + 0.9983847141265869, + 1 + ], + "min": [ + -0.9804638028144836, + -0.9959992170333862, + -0.9985447525978088, + 1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 210, + "max": [ + 0.3270263671875, + 0.9985079765319824 + ], + "min": [ + 0.013447999954223633, + 0.1425018310546875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 210, + "max": [ + 0.3270263671875, + 0.9985079765319824 + ], + "min": [ + 0.013447999954223633, + 0.1425018310546875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 210, + "max": [ + 0.3270263671875, + 0.9985079765319824 + ], + "min": [ + 0.013447999954223633, + 0.1425018310546875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 210, + "max": [ + 0.3270263671875, + 0.9985079765319824 + ], + "min": [ + 0.013447999954223633, + 0.1425018310546875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 210, + "max": [ + 0.3270263671875, + 0.9985079765319824 + ], + "min": [ + 0.013447999954223633, + 0.1425018310546875 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 684, + "max": [ + 181 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 182, + "max": [ + 0.9812500476837158, + 0.98042231798172, + 0.9717556834220886 + ], + "min": [ + -0.9812499284744263, + -0.9752631783485413, + -0.9804222583770752 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 182, + "max": [ + 22.49679946899414, + 16.087291717529297, + 0.015429569408297539 + ], + "min": [ + -22.48870086669922, + -16.78112030029297, + -7.136616230010986 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 182, + "max": [ + 0.6957566142082214, + 0.9961112141609192, + 0.9974663853645325, + 1 + ], + "min": [ + -0.7036730647087097, + -0.9936772584915161, + -0.9909072518348694, + 1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 182, + "max": [ + 0.34142303466796875, + 0.9984639883041382 + ], + "min": [ + 0.04077434539794922, + 0.16192626953125 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 182, + "max": [ + 0.34142303466796875, + 0.9984639883041382 + ], + "min": [ + 0.04077434539794922, + 0.16192626953125 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 182, + "max": [ + 0.34142303466796875, + 0.9984639883041382 + ], + "min": [ + 0.04077434539794922, + 0.16192626953125 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 182, + "max": [ + 0.34142303466796875, + 0.9984639883041382 + ], + "min": [ + 0.04077434539794922, + 0.16192626953125 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 182, + "max": [ + 0.34142303466796875, + 0.9984639883041382 + ], + "min": [ + 0.04077434539794922, + 0.16192626953125 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 192, + "max": [ + 59 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.9203706383705139, + 0.08052149415016174, + 0.9462553858757019 + ], + "min": [ + -0.9203649163246155, + -0.9591649174690247, + -0.9462553858757019 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 5.528401851654053, + 0.8937630653381348, + 3.1585500240325928 + ], + "min": [ + -0.07819413393735886, + -3.9569084644317627, + -3.1585500240325928 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.6124923825263977, + 0.8991279602050781, + 0.6859615445137024, + -1 + ], + "min": [ + -0.9984928965568542, + -0.9968492984771729, + -0.6886578798294067, + -1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.511199951171875, + 0.9978490471839905 + ], + "min": [ + 0.0011079907417297363, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.511199951171875, + 0.9978490471839905 + ], + "min": [ + 0.0011079907417297363, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.511199951171875, + 0.9978490471839905 + ], + "min": [ + 0.0011079907417297363, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.511199951171875, + 0.9978490471839905 + ], + "min": [ + 0.0011079907417297363, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 2736, + "max": [ + 668 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 3.1813101768493652, + 2.837909698486328, + 2.837886095046997 + ], + "min": [ + -3.181349039077759, + -2.837909698486328, + -2.837886095046997 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9334935545921326, + 0.9705367088317871, + 0.9705860018730164, + -1 + ], + "min": [ + -0.9999493956565857, + -0.9706282615661621, + -0.9704942107200623, + -1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9978485107421875, + 0.9978020191192627 + ], + "min": [ + 0.005537986755371094, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9978485107421875, + 0.9978020191192627 + ], + "min": [ + 0.005537986755371094, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9978485107421875, + 0.9978020191192627 + ], + "min": [ + 0.005537986755371094, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9978485107421875, + 0.9978020191192627 + ], + "min": [ + 0.005537986755371094, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 4872, + "max": [ + 1512 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 1513, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 1513, + "max": [ + 3.944369077682495, + 1.761652946472168, + 3.050240993499756 + ], + "min": [ + -3.873849868774414, + -22.01688575744629, + -4.03395414352417 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 1513, + "max": [ + 1, + 0.9999986886978149, + 0.9997035264968872, + -1 + ], + "min": [ + -1, + -1, + -0.999306321144104, + -1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 1513, + "max": [ + 0.998046875, + 0.9980520009994507 + ], + "min": [ + 0.0011079907417297363, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1513, + "max": [ + 0.998046875, + 0.9980520009994507 + ], + "min": [ + 0.0011079907417297363, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1513, + "max": [ + 0.998046875, + 0.9980520009994507 + ], + "min": [ + 0.0011079907417297363, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1513, + "max": [ + 0.998046875, + 0.9980520009994507 + ], + "min": [ + 0.0011079907417297363, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 2736, + "max": [ + 668 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 2.8058583736419678, + 2.503086566925049, + 2.5030324459075928 + ], + "min": [ + -2.8059232234954834, + -2.5030837059020996, + -2.5030324459075928 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9334862232208252, + 0.9717816710472107, + 0.9705871343612671, + -1 + ], + "min": [ + -0.9999354481697083, + -0.9705882668495178, + -0.9704800844192505, + -1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9752960205078125, + 0.997859001159668 + ], + "min": [ + 0.04209327697753906, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9752960205078125, + 0.997859001159668 + ], + "min": [ + 0.04209327697753906, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9752960205078125, + 0.997859001159668 + ], + "min": [ + 0.04209327697753906, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9752960205078125, + 0.997859001159668 + ], + "min": [ + 0.04209327697753906, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 4128, + "max": [ + 1189 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 1190, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 1190, + "max": [ + 6.134866237640381, + 7.972114086151123, + 9.091575622558594 + ], + "min": [ + -6.143898010253906, + -12.839866638183594, + -2.773728847503662 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 1190, + "max": [ + 1, + 1, + 0.9237611889839172, + -1 + ], + "min": [ + -0.9987005591392517, + -1, + -0.9384349584579468, + -1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 1190, + "max": [ + 0.9978179931640625, + 0.9980350136756897 + ], + "min": [ + 0.011077880859375, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1190, + "max": [ + 0.9978179931640625, + 0.9980350136756897 + ], + "min": [ + 0.011077880859375, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1190, + "max": [ + 0.9978179931640625, + 0.9980350136756897 + ], + "min": [ + 0.011077880859375, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1190, + "max": [ + 0.9978179931640625, + 0.9980350136756897 + ], + "min": [ + 0.011077880859375, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 192, + "max": [ + 59 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.9462710022926331, + 0.00001421624983777292, + 0.9043461680412292 + ], + "min": [ + -0.9462725520133972, + -0.9274078011512756, + -0.9043479561805725 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 2.1718454360961914, + -3.3487545825520645e-14, + 2.2841246128082275 + ], + "min": [ + -2.1718454360961914, + -8.425505638122559, + -2.2841246128082275 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.6912213563919067, + 0.8617174625396729, + 0.9907815456390381, + -1 + ], + "min": [ + -0.6912751197814941, + -1, + -0.6705557703971863, + -1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.39041900634765625, + 0.9979940056800842 + ], + "min": [ + 0.029907703399658203, + 0.2353973388671875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.39041900634765625, + 0.9979940056800842 + ], + "min": [ + 0.029907703399658203, + 0.2353973388671875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.39041900634765625, + 0.9979940056800842 + ], + "min": [ + 0.029907703399658203, + 0.2353973388671875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.39041900634765625, + 0.9979940056800842 + ], + "min": [ + 0.029907703399658203, + 0.2353973388671875 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 2736, + "max": [ + 668 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 2.805884599685669, + 2.503038167953491, + 2.5029942989349365 + ], + "min": [ + -2.8058927059173584, + -2.503038167953491, + -2.5029942989349365 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9334784746170044, + 0.9944380521774292, + 0.9705901741981506, + -1 + ], + "min": [ + -0.9999455809593201, + -0.9916284084320068, + -0.9705632925033569, + -1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9980010986328125, + 0.9980420470237732 + ], + "min": [ + 0.023261547088623047, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9980010986328125, + 0.9980420470237732 + ], + "min": [ + 0.023261547088623047, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9980010986328125, + 0.9980420470237732 + ], + "min": [ + 0.023261547088623047, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9980010986328125, + 0.9980420470237732 + ], + "min": [ + 0.023261547088623047, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 3792, + "max": [ + 1128 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 1129, + "max": [ + 0.9999979138374329, + 0.9999978542327881, + 1 + ], + "min": [ + -0.9999978542327881, + -0.8000316023826599, + -1 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 1129, + "max": [ + 6.356159210205078, + 7.2811970710754395, + 6.741055011749268 + ], + "min": [ + -6.334109306335449, + -25.386653900146484, + -2.690229654312134 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 1129, + "max": [ + 0.9999987483024597, + 0.9999978542327881, + 0.9899260401725769, + -1 + ], + "min": [ + -0.9441795349121094, + -0.9999979138374329, + -0.9884353280067444, + -1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 1129, + "max": [ + 0.97637939453125, + 0.9980530142784119 + ], + "min": [ + 0.0011079907417297363, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1129, + "max": [ + 0.97637939453125, + 0.9980530142784119 + ], + "min": [ + 0.0011079907417297363, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1129, + "max": [ + 0.97637939453125, + 0.9980530142784119 + ], + "min": [ + 0.0011079907417297363, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1129, + "max": [ + 0.97637939453125, + 0.9980530142784119 + ], + "min": [ + 0.0011079907417297363, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 858, + "max": [ + 209 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 210, + "max": [ + 0.9999998211860657, + 0.9804549217224121, + 0.9718173146247864 + ], + "min": [ + -0.9999997615814209, + -0.9752144813537598, + -0.9718270897865295 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 210, + "max": [ + 19.017000198364258, + 16.119686126708984, + 0.017575301229953766 + ], + "min": [ + -22.505199432373047, + -16.74315071105957, + -12.473467826843262 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 210, + "max": [ + 0.7163847088813782, + 0.9960353970527649, + 0.9985222220420837, + 1 + ], + "min": [ + -0.9804637432098389, + -0.9986840486526489, + -0.9983941912651062, + 1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 210, + "max": [ + 0.986541748046875, + 0.9985079765319824 + ], + "min": [ + 0.6729278564453125, + 0.1425018310546875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 210, + "max": [ + 0.986541748046875, + 0.9985079765319824 + ], + "min": [ + 0.6729278564453125, + 0.1425018310546875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 210, + "max": [ + 0.986541748046875, + 0.9985079765319824 + ], + "min": [ + 0.6729278564453125, + 0.1425018310546875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 210, + "max": [ + 0.986541748046875, + 0.9985079765319824 + ], + "min": [ + 0.6729278564453125, + 0.1425018310546875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 210, + "max": [ + 0.986541748046875, + 0.9985079765319824 + ], + "min": [ + 0.6729278564453125, + 0.1425018310546875 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 684, + "max": [ + 181 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 182, + "max": [ + 0.9812499284744263, + 0.9804224371910095, + 0.9717556834220886 + ], + "min": [ + -0.9812500476837158, + -0.9752631783485413, + -0.9804222583770752 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 182, + "max": [ + 22.488698959350586, + 16.08729362487793, + 0.015429651364684105 + ], + "min": [ + -22.49679946899414, + -16.7811222076416, + -7.136616230010986 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 182, + "max": [ + 0.7016284465789795, + 0.993878185749054, + 0.9915738105773926, + 1 + ], + "min": [ + -0.7065384984016418, + -0.9980297684669495, + -0.9960659742355347, + 1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 182, + "max": [ + 0.9592132568359375, + 0.9984700083732605 + ], + "min": [ + 0.6585693359375, + 0.16192626953125 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 182, + "max": [ + 0.9592132568359375, + 0.9984700083732605 + ], + "min": [ + 0.6585693359375, + 0.16192626953125 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 182, + "max": [ + 0.9592132568359375, + 0.9984700083732605 + ], + "min": [ + 0.6585693359375, + 0.16192626953125 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 182, + "max": [ + 0.9592132568359375, + 0.9984700083732605 + ], + "min": [ + 0.6585693359375, + 0.16192626953125 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 182, + "max": [ + 0.9592132568359375, + 0.9984700083732605 + ], + "min": [ + 0.6585693359375, + 0.16192626953125 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 192, + "max": [ + 59 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.9203649163246155, + 0.08052149415016174, + 0.9462553858757019 + ], + "min": [ + -0.9203706383705139, + -0.9591649174690247, + -0.9462553858757019 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.07819413393735886, + 0.8937630653381348, + 3.1585500240325928 + ], + "min": [ + -5.528401851654053, + -3.9569084644317627, + -3.1585500240325928 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.9984928965568542, + 0.900946855545044, + 0.6860498189926147, + 1 + ], + "min": [ + -0.6124934554100037, + -0.9968502521514893, + -0.6886581778526306, + 1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.997955322265625, + 0.9978790283203125 + ], + "min": [ + 0.14649581909179688, + 0.2284698486328125 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.997955322265625, + 0.9978790283203125 + ], + "min": [ + 0.14649581909179688, + 0.2284698486328125 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.997955322265625, + 0.9978790283203125 + ], + "min": [ + 0.14649581909179688, + 0.2284698486328125 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.997955322265625, + 0.9978790283203125 + ], + "min": [ + 0.14649581909179688, + 0.2284698486328125 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 2736, + "max": [ + 668 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 3.181283950805664, + 2.837909698486328, + 2.837886095046997 + ], + "min": [ + -3.181283950805664, + -2.837909698486328, + -2.837886095046997 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9999459981918335, + 0.9705654978752136, + 0.9705774784088135, + 1 + ], + "min": [ + -0.9334567785263062, + -0.9705967903137207, + -0.9705893993377686, + 1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9878692626953125, + 0.9980520009994507 + ], + "min": [ + 0.037108421325683594, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9878692626953125, + 0.9980520009994507 + ], + "min": [ + 0.037108421325683594, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9878692626953125, + 0.9980520009994507 + ], + "min": [ + 0.037108421325683594, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9878692626953125, + 0.9980520009994507 + ], + "min": [ + 0.037108421325683594, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 4872, + "max": [ + 1456 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 1457, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 1457, + "max": [ + 3.8742127418518066, + 1.7617069482803345, + 3.050255298614502 + ], + "min": [ + -3.9421558380126953, + -22.016254425048828, + -4.034045219421387 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 1457, + "max": [ + 1, + 0.9999988079071045, + 0.999301552772522, + 1 + ], + "min": [ + -1, + -1, + -0.9996956586837769, + 1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 1457, + "max": [ + 0.998046875, + 0.997937023639679 + ], + "min": [ + 0.0011079907417297363, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1457, + "max": [ + 0.998046875, + 0.997937023639679 + ], + "min": [ + 0.0011079907417297363, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1457, + "max": [ + 0.998046875, + 0.997937023639679 + ], + "min": [ + 0.0011079907417297363, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1457, + "max": [ + 0.998046875, + 0.997937023639679 + ], + "min": [ + 0.0011079907417297363, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 2736, + "max": [ + 668 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 1, + 0.9987014532089233, + 0.9986981749534607 + ], + "min": [ + -1, + -0.9987033605575562, + -0.9986976981163025 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 2.805882692337036, + 2.4998340606689453, + 2.4997761249542236 + ], + "min": [ + -2.805903196334839, + -2.4998345375061035, + -2.4997763633728027 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9999436140060425, + 0.9696629643440247, + 0.969977080821991, + 1 + ], + "min": [ + -0.9334853291511536, + -0.9695192575454712, + -0.9701146483421326, + 1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9880218505859375, + 0.9815201759338379 + ], + "min": [ + 0.03489208221435547, + 0.0645294189453125 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9880218505859375, + 0.9815201759338379 + ], + "min": [ + 0.03489208221435547, + 0.0645294189453125 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9880218505859375, + 0.9815201759338379 + ], + "min": [ + 0.03489208221435547, + 0.0645294189453125 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9880218505859375, + 0.9815201759338379 + ], + "min": [ + 0.03489208221435547, + 0.0645294189453125 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 4128, + "max": [ + 1189 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 1190, + "max": [ + 1, + 1, + 1 + ], + "min": [ + -1, + -1, + -1 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 1190, + "max": [ + 6.144484043121338, + 7.975264549255371, + 9.09143352508545 + ], + "min": [ + -6.134279727935791, + -12.836573600769043, + -2.7741191387176514 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 1190, + "max": [ + 0.9986706972122192, + 1, + 0.9321260452270508, + 1 + ], + "min": [ + -1, + -1, + -0.9456865191459656, + 1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 1190, + "max": [ + 0.969451904296875, + 0.9978950023651123 + ], + "min": [ + 0.0011079907417297363, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1190, + "max": [ + 0.969451904296875, + 0.9978950023651123 + ], + "min": [ + 0.0011079907417297363, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1190, + "max": [ + 0.969451904296875, + 0.9978950023651123 + ], + "min": [ + 0.0011079907417297363, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1190, + "max": [ + 0.969451904296875, + 0.9978950023651123 + ], + "min": [ + 0.0011079907417297363, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 192, + "max": [ + 59 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.9460511803627014, + 0.020673329010605812, + 0.9043437838554382 + ], + "min": [ + -0.9460495114326477, + -0.9307563900947571, + -0.9043524265289307 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 2.2373239994049072, + -5.258825228852005e-15, + 2.284106492996216 + ], + "min": [ + -2.2373239994049072, + -8.501056671142578, + -2.284106492996216 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.7061308026313782, + 0.8601000308990479, + 0.9907819628715515, + 1 + ], + "min": [ + -0.6760434508323669, + -0.9998863935470581, + -0.6705630421638489, + 1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.882232666015625, + 0.9979940056800842 + ], + "min": [ + 0.14953994750976562, + 0.4477996826171875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.882232666015625, + 0.9979940056800842 + ], + "min": [ + 0.14953994750976562, + 0.4477996826171875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.882232666015625, + 0.9979940056800842 + ], + "min": [ + 0.14953994750976562, + 0.4477996826171875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 60, + "max": [ + 0.882232666015625, + 0.9979940056800842 + ], + "min": [ + 0.14953994750976562, + 0.4477996826171875 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 2736, + "max": [ + 668 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9997609853744507, + 0.9997621774673462, + 1 + ], + "min": [ + -0.9997605681419373, + -0.9997619986534119, + -1 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 2.814361095428467, + 2.5024404525756836, + 2.5029942989349365 + ], + "min": [ + -2.814356803894043, + -2.5024402141571045, + -2.5029942989349365 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.9410112500190735, + 0.9957802891731262, + 0.9705701470375061, + 1 + ], + "min": [ + -0.9999746680259705, + -0.9924947619438171, + -0.9705852270126343, + 1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.950103759765625, + 0.9767532348632812 + ], + "min": [ + 0.005815982818603516, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.950103759765625, + 0.9767532348632812 + ], + "min": [ + 0.005815982818603516, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.950103759765625, + 0.9767532348632812 + ], + "min": [ + 0.005815982818603516, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 669, + "max": [ + 0.950103759765625, + 0.9767532348632812 + ], + "min": [ + 0.005815982818603516, + 0.0011138916015625 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 3792, + "max": [ + 1128 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 1129, + "max": [ + 0.9997616410255432, + 0.9998812675476074, + 0.9998799562454224 + ], + "min": [ + -0.9997621774673462, + -0.8040514588356018, + -0.9998816251754761 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 1129, + "max": [ + 6.410318374633789, + 7.331780910491943, + 6.804128170013428 + ], + "min": [ + -6.276950359344482, + -25.39360809326172, + -2.7138583660125732 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 1129, + "max": [ + 0.9514473676681519, + 0.9999784231185913, + 0.9934725165367126, + 1 + ], + "min": [ + -0.9999739527702332, + -0.9999784827232361, + -0.9883590936660767, + 1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 1129, + "max": [ + 0.971954345703125, + 0.9980530142784119 + ], + "min": [ + 0.0011079907417297363, + 0.0448760986328125 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1129, + "max": [ + 0.971954345703125, + 0.9980530142784119 + ], + "min": [ + 0.0011079907417297363, + 0.0448760986328125 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1129, + "max": [ + 0.971954345703125, + 0.9980530142784119 + ], + "min": [ + 0.0011079907417297363, + 0.0448760986328125 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1129, + "max": [ + 0.971954345703125, + 0.9980530142784119 + ], + "min": [ + 0.0011079907417297363, + 0.0448760986328125 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 198, + "max": [ + 47 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 48, + "max": [ + 0.9800400733947754, + 0.9815039038658142, + 0.9692381620407104 + ], + "min": [ + -0.9800400733947754, + -0.9815108776092529, + -0.009044792503118515 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 48, + "max": [ + 22.25983238220215, + 3.6992530785930143e-16, + 5.172183990478516 + ], + "min": [ + -22.703067779541016, + -2.035116672515869, + -4.854550361633301 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 48, + "max": [ + 0.711862325668335, + 0.009239021688699722, + 1, + 1 + ], + "min": [ + -0.7126688957214355, + -0.9862862229347229, + -0.9999582171440125, + 1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 48, + "max": [ + 0.34128570556640625, + 0.3702392578125 + ], + "min": [ + 0.2750701904296875, + 0.1679534912109375 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 48, + "max": [ + 0.34128570556640625, + 0.3702392578125 + ], + "min": [ + 0.2750701904296875, + 0.1679534912109375 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 48, + "max": [ + 0.34128570556640625, + 0.3702392578125 + ], + "min": [ + 0.2750701904296875, + 0.1679534912109375 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 48, + "max": [ + 0.34128570556640625, + 0.3702392578125 + ], + "min": [ + 0.2750701904296875, + 0.1679534912109375 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 48, + "max": [ + 0.34128570556640625, + 0.3702392578125 + ], + "min": [ + 0.2750701904296875, + 0.1679534912109375 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 198, + "max": [ + 47 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 48, + "max": [ + 0.9800400733947754, + 0.9815039038658142, + 0.9692381620407104 + ], + "min": [ + -0.9800400733947754, + -0.9815108776092529, + -0.00904479343444109 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 48, + "max": [ + 22.481449127197266, + 1.0175585746765137, + 5.013367176055908 + ], + "min": [ + -22.481449127197266, + -1.0175585746765137, + -5.013367176055908 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 48, + "max": [ + 0.7118115425109863, + 0.9863398671150208, + 0.999962568283081, + 1 + ], + "min": [ + -0.7124660015106201, + -0.0099171232432127, + -0.9999998807907104, + 1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 48, + "max": [ + 0.72491455078125, + 0.37017822265625 + ], + "min": [ + 0.658599853515625, + 0.1679534912109375 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 48, + "max": [ + 0.72491455078125, + 0.37017822265625 + ], + "min": [ + 0.658599853515625, + 0.1679534912109375 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 48, + "max": [ + 0.72491455078125, + 0.37017822265625 + ], + "min": [ + 0.658599853515625, + 0.1679534912109375 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 48, + "max": [ + 0.72491455078125, + 0.37017822265625 + ], + "min": [ + 0.658599853515625, + 0.1679534912109375 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 48, + "max": [ + 0.72491455078125, + 0.37017822265625 + ], + "min": [ + 0.658599853515625, + 0.1679534912109375 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 198, + "max": [ + 51 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 52, + "max": [ + 0.980785071849823, + 0.963556170463562, + 0.980782151222229 + ], + "min": [ + -0.980785071849823, + 0, + -0.9807981252670288 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 52, + "max": [ + 5.742680072784424, + 7.674149990081787, + 16.58354949951172 + ], + "min": [ + -5.742680072784424, + -7.674149990081787, + -16.58354949951172 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 52, + "max": [ + -0.000021119056327734143, + 1, + 0.7062842845916748, + 1 + ], + "min": [ + -0.9896541237831116, + -1, + -0.7070288062095642, + 1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 52, + "max": [ + 0.27196502685546875, + 0.6553955078125 + ], + "min": [ + 0.14226150512695312, + 0.457794189453125 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 52, + "max": [ + 0.27196502685546875, + 0.6553955078125 + ], + "min": [ + 0.14226150512695312, + 0.457794189453125 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 52, + "max": [ + 0.27196502685546875, + 0.6553955078125 + ], + "min": [ + 0.14226150512695312, + 0.457794189453125 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 52, + "max": [ + 0.27196502685546875, + 0.6553955078125 + ], + "min": [ + 0.14226150512695312, + 0.457794189453125 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 52, + "max": [ + 0.27196502685546875, + 0.6553955078125 + ], + "min": [ + 0.14226150512695312, + 0.457794189453125 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 324, + "max": [ + 60 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 61, + "max": [ + 0.9253151416778564, + 0.9224124550819397, + 0.9999999403953552 + ], + "min": [ + -0.9253151416778564, + -0.922246515750885, + 0.3791990280151367 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 61, + "max": [ + 3.422339916229248, + 3.4983837604522705, + 1.0367803573608398 + ], + "min": [ + -3.422339916229248, + -3.4988362789154053, + -0.0045692152343690395 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 61, + "max": [ + -0.37807369232177734, + 0.16984163224697113, + 0.923377513885498, + 1 + ], + "min": [ + -1, + -0.25828370451927185, + -0.922590970993042, + 1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 61, + "max": [ + 0.5171157717704773, + 0.4423835277557373 + ], + "min": [ + 0.48266905546188354, + 0.4072730541229248 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 61, + "max": [ + 0.5171157717704773, + 0.4423835277557373 + ], + "min": [ + 0.48266905546188354, + 0.4072730541229248 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 61, + "max": [ + 0.5171157717704773, + 0.4423835277557373 + ], + "min": [ + 0.48266905546188354, + 0.4072730541229248 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 61, + "max": [ + 0.5171157717704773, + 0.4423835277557373 + ], + "min": [ + 0.48266905546188354, + 0.4072730541229248 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 61, + "max": [ + 0.5171157717704773, + 0.4423835277557373 + ], + "min": [ + 0.48266905546188354, + 0.4072730541229248 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 36, + "max": [ + 12 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 13, + "max": [ + 0.00007492514851037413, + -0.0015094821574166417, + -0.9999987483024597 + ], + "min": [ + -0.00007492515578633174, + -0.0015759739326313138, + -0.999998927116394 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 13, + "max": [ + 0.5206102132797241, + 0.5319043397903442, + -9.52561092376709 + ], + "min": [ + -0.5205997824668884, + -0.5325044393539429, + -9.527267456054688 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 13, + "max": [ + 1, + 0.0016572648892179132, + 0.00007750481745461002, + 1 + ], + "min": [ + 0.9999986290931702, + -0.001644145231693983, + -0.000077527787652798, + 1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 13, + "max": [ + 0.5069850087165833, + 0.45840930938720703 + ], + "min": [ + 0.492599755525589, + 0.44371509552001953 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 13, + "max": [ + 0.5069850087165833, + 0.45840930938720703 + ], + "min": [ + 0.492599755525589, + 0.44371509552001953 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 13, + "max": [ + 0.5069850087165833, + 0.45840930938720703 + ], + "min": [ + 0.492599755525589, + 0.44371509552001953 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 13, + "max": [ + 0.5069850087165833, + 0.45840930938720703 + ], + "min": [ + 0.492599755525589, + 0.44371509552001953 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 13, + "max": [ + 0.5069850087165833, + 0.45840930938720703 + ], + "min": [ + 0.492599755525589, + 0.44371509552001953 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 6768, + "max": [ + 1394 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 1395, + "max": [ + 1, + 0.9952672719955444, + 0.9951035976409912 + ], + "min": [ + -1, + -0.9998235106468201, + -0.999494731426239 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 1395, + "max": [ + 37.781776428222656, + 7.368798732757568, + 5.318163871765137 + ], + "min": [ + -37.78172302246094, + -72.43864440917969, + -26.958742141723633 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 1395, + "max": [ + 0.9951720833778381, + 0.9952999949455261, + 0.9998140335083008, + 1 + ], + "min": [ + -0.9951794147491455, + -0.9994944930076599, + -0.9952681660652161, + 1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 1395, + "max": [ + 0.9985809326171875, + 0.998432993888855 + ], + "min": [ + 0.587646484375, + 0.00115966796875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1395, + "max": [ + 0.9985809326171875, + 0.998432993888855 + ], + "min": [ + 0.587646484375, + 0.00115966796875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1395, + "max": [ + 0.9985809326171875, + 0.998432993888855 + ], + "min": [ + 0.587646484375, + 0.00115966796875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1395, + "max": [ + 0.9985809326171875, + 0.998432993888855 + ], + "min": [ + 0.587646484375, + 0.00115966796875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1395, + "max": [ + 0.9985809326171875, + 0.998432993888855 + ], + "min": [ + 0.587646484375, + 0.00115966796875 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 4896, + "max": [ + 1187 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 1188, + "max": [ + 0.9985098242759705, + 1, + 0.9985183477401733 + ], + "min": [ + -0.9985097050666809, + -1, + -0.9985119104385376 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 1188, + "max": [ + 32.348899841308594, + 5.063899993896484, + 32.348899841308594 + ], + "min": [ + -32.348899841308594, + -5.063899993896484, + -32.34890365600586 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 1188, + "max": [ + 0.9971701502799988, + 0.9981505870819092, + 1, + 1 + ], + "min": [ + -0.9971259236335754, + -0.9981384873390198, + -1, + 1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 1188, + "max": [ + 0.9985198974609375, + 0.9985760450363159 + ], + "min": [ + 0.5195770263671875, + 0.00115966796875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1188, + "max": [ + 0.9985198974609375, + 0.9985760450363159 + ], + "min": [ + 0.5195770263671875, + 0.00115966796875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1188, + "max": [ + 0.9985198974609375, + 0.9985760450363159 + ], + "min": [ + 0.5195770263671875, + 0.00115966796875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1188, + "max": [ + 0.9985198974609375, + 0.9985760450363159 + ], + "min": [ + 0.5195770263671875, + 0.00115966796875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1188, + "max": [ + 0.9985198974609375, + 0.9985760450363159 + ], + "min": [ + 0.5195770263671875, + 0.00115966796875 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 6768, + "max": [ + 1394 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 1395, + "max": [ + 1, + 0.9952672719955444, + 0.9951035976409912 + ], + "min": [ + -1, + -0.9998235702514648, + -0.999494731426239 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 1395, + "max": [ + 37.78175354003906, + 7.36879825592041, + 5.3181657791137695 + ], + "min": [ + -37.7817497253418, + -72.43864440917969, + -26.958721160888672 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 1395, + "max": [ + 0.9951218962669373, + 0.9994943141937256, + 0.9952681660652161, + 1 + ], + "min": [ + -0.9952056407928467, + -0.9952999949455261, + -0.9998233318328857, + 1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 1395, + "max": [ + 0.4123382568359375, + 0.9985350370407104 + ], + "min": [ + 0.001408994197845459, + 0.00115966796875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1395, + "max": [ + 0.4123382568359375, + 0.9985350370407104 + ], + "min": [ + 0.001408994197845459, + 0.00115966796875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1395, + "max": [ + 0.4123382568359375, + 0.9985350370407104 + ], + "min": [ + 0.001408994197845459, + 0.00115966796875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1395, + "max": [ + 0.4123382568359375, + 0.9985350370407104 + ], + "min": [ + 0.001408994197845459, + 0.00115966796875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1395, + "max": [ + 0.4123382568359375, + 0.9985350370407104 + ], + "min": [ + 0.001408994197845459, + 0.00115966796875 + ], + "type": "VEC2" + }, + { + "componentType": 5125, + "count": 4896, + "max": [ + 1187 + ], + "min": [ + 0 + ], + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 1188, + "max": [ + 0.9985119104385376, + 1, + 0.9985097646713257 + ], + "min": [ + -0.9985182881355286, + -1, + -0.9985126256942749 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 1188, + "max": [ + 32.348899841308594, + 5.063899993896484, + 32.348899841308594 + ], + "min": [ + -32.348899841308594, + -5.063899993896484, + -32.348899841308594 + ], + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 1188, + "max": [ + 0.9999992251396179, + 0.9981393814086914, + 0.9971155524253845, + 1 + ], + "min": [ + -0.9999951124191284, + -0.998076856136322, + -0.9971383810043335, + 1 + ], + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 1188, + "max": [ + 0.48041534423828125, + 0.9985830187797546 + ], + "min": [ + 0.001475989818572998, + 0.00115966796875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1188, + "max": [ + 0.48041534423828125, + 0.9985830187797546 + ], + "min": [ + 0.001475989818572998, + 0.00115966796875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1188, + "max": [ + 0.48041534423828125, + 0.9985830187797546 + ], + "min": [ + 0.001475989818572998, + 0.00115966796875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1188, + "max": [ + 0.48041534423828125, + 0.9985830187797546 + ], + "min": [ + 0.001475989818572998, + 0.00115966796875 + ], + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 1188, + "max": [ + 0.48041534423828125, + 0.9985830187797546 + ], + "min": [ + 0.001475989818572998, + 0.00115966796875 + ], + "type": "VEC2" + } + ], + "animations": [ + { + "channels": [ + { + "sampler": 0, + "target": { + "node": 10, + "path": "translation" + } + }, + { + "sampler": 1, + "target": { + "node": 10, + "path": "scale" + } + }, + { + "sampler": 2, + "target": { + "node": 10, + "path": "rotation" + } + }, + { + "sampler": 3, + "target": { + "node": 33, + "path": "translation" + } + }, + { + "sampler": 4, + "target": { + "node": 33, + "path": "scale" + } + }, + { + "sampler": 5, + "target": { + "node": 33, + "path": "rotation" + } + }, + { + "sampler": 6, + "target": { + "node": 29, + "path": "translation" + } + }, + { + "sampler": 7, + "target": { + "node": 29, + "path": "scale" + } + }, + { + "sampler": 8, + "target": { + "node": 29, + "path": "rotation" + } + }, + { + "sampler": 9, + "target": { + "node": 27, + "path": "translation" + } + }, + { + "sampler": 10, + "target": { + "node": 27, + "path": "scale" + } + }, + { + "sampler": 11, + "target": { + "node": 27, + "path": "rotation" + } + }, + { + "sampler": 12, + "target": { + "node": 25, + "path": "translation" + } + }, + { + "sampler": 13, + "target": { + "node": 25, + "path": "scale" + } + }, + { + "sampler": 14, + "target": { + "node": 25, + "path": "rotation" + } + }, + { + "sampler": 15, + "target": { + "node": 23, + "path": "translation" + } + }, + { + "sampler": 16, + "target": { + "node": 23, + "path": "scale" + } + }, + { + "sampler": 17, + "target": { + "node": 23, + "path": "rotation" + } + }, + { + "sampler": 18, + "target": { + "node": 21, + "path": "translation" + } + }, + { + "sampler": 19, + "target": { + "node": 21, + "path": "scale" + } + }, + { + "sampler": 20, + "target": { + "node": 21, + "path": "rotation" + } + }, + { + "sampler": 21, + "target": { + "node": 53, + "path": "translation" + } + }, + { + "sampler": 22, + "target": { + "node": 53, + "path": "scale" + } + }, + { + "sampler": 23, + "target": { + "node": 53, + "path": "rotation" + } + }, + { + "sampler": 24, + "target": { + "node": 49, + "path": "translation" + } + }, + { + "sampler": 25, + "target": { + "node": 49, + "path": "scale" + } + }, + { + "sampler": 26, + "target": { + "node": 49, + "path": "rotation" + } + }, + { + "sampler": 27, + "target": { + "node": 47, + "path": "translation" + } + }, + { + "sampler": 28, + "target": { + "node": 47, + "path": "scale" + } + }, + { + "sampler": 29, + "target": { + "node": 47, + "path": "rotation" + } + }, + { + "sampler": 30, + "target": { + "node": 45, + "path": "translation" + } + }, + { + "sampler": 31, + "target": { + "node": 45, + "path": "scale" + } + }, + { + "sampler": 32, + "target": { + "node": 45, + "path": "rotation" + } + }, + { + "sampler": 33, + "target": { + "node": 43, + "path": "translation" + } + }, + { + "sampler": 34, + "target": { + "node": 43, + "path": "scale" + } + }, + { + "sampler": 35, + "target": { + "node": 43, + "path": "rotation" + } + }, + { + "sampler": 36, + "target": { + "node": 41, + "path": "translation" + } + }, + { + "sampler": 37, + "target": { + "node": 41, + "path": "scale" + } + }, + { + "sampler": 38, + "target": { + "node": 41, + "path": "rotation" + } + }, + { + "sampler": 39, + "target": { + "node": 37, + "path": "translation" + } + }, + { + "sampler": 40, + "target": { + "node": 37, + "path": "scale" + } + }, + { + "sampler": 41, + "target": { + "node": 37, + "path": "rotation" + } + }, + { + "sampler": 42, + "target": { + "node": 35, + "path": "translation" + } + }, + { + "sampler": 43, + "target": { + "node": 35, + "path": "scale" + } + }, + { + "sampler": 44, + "target": { + "node": 35, + "path": "rotation" + } + }, + { + "sampler": 45, + "target": { + "node": 73, + "path": "translation" + } + }, + { + "sampler": 46, + "target": { + "node": 73, + "path": "scale" + } + }, + { + "sampler": 47, + "target": { + "node": 73, + "path": "rotation" + } + }, + { + "sampler": 48, + "target": { + "node": 69, + "path": "translation" + } + }, + { + "sampler": 49, + "target": { + "node": 69, + "path": "scale" + } + }, + { + "sampler": 50, + "target": { + "node": 69, + "path": "rotation" + } + }, + { + "sampler": 51, + "target": { + "node": 67, + "path": "translation" + } + }, + { + "sampler": 52, + "target": { + "node": 67, + "path": "scale" + } + }, + { + "sampler": 53, + "target": { + "node": 67, + "path": "rotation" + } + }, + { + "sampler": 54, + "target": { + "node": 65, + "path": "translation" + } + }, + { + "sampler": 55, + "target": { + "node": 65, + "path": "scale" + } + }, + { + "sampler": 56, + "target": { + "node": 65, + "path": "rotation" + } + }, + { + "sampler": 57, + "target": { + "node": 63, + "path": "translation" + } + }, + { + "sampler": 58, + "target": { + "node": 63, + "path": "scale" + } + }, + { + "sampler": 59, + "target": { + "node": 63, + "path": "rotation" + } + }, + { + "sampler": 60, + "target": { + "node": 61, + "path": "translation" + } + }, + { + "sampler": 61, + "target": { + "node": 61, + "path": "scale" + } + }, + { + "sampler": 62, + "target": { + "node": 61, + "path": "rotation" + } + }, + { + "sampler": 63, + "target": { + "node": 57, + "path": "translation" + } + }, + { + "sampler": 64, + "target": { + "node": 57, + "path": "scale" + } + }, + { + "sampler": 65, + "target": { + "node": 57, + "path": "rotation" + } + }, + { + "sampler": 66, + "target": { + "node": 55, + "path": "translation" + } + }, + { + "sampler": 67, + "target": { + "node": 55, + "path": "scale" + } + }, + { + "sampler": 68, + "target": { + "node": 55, + "path": "rotation" + } + }, + { + "sampler": 69, + "target": { + "node": 75, + "path": "translation" + } + }, + { + "sampler": 70, + "target": { + "node": 75, + "path": "scale" + } + }, + { + "sampler": 71, + "target": { + "node": 75, + "path": "rotation" + } + }, + { + "sampler": 72, + "target": { + "node": 77, + "path": "translation" + } + }, + { + "sampler": 73, + "target": { + "node": 77, + "path": "scale" + } + }, + { + "sampler": 74, + "target": { + "node": 77, + "path": "rotation" + } + }, + { + "sampler": 75, + "target": { + "node": 79, + "path": "translation" + } + }, + { + "sampler": 76, + "target": { + "node": 79, + "path": "scale" + } + }, + { + "sampler": 77, + "target": { + "node": 79, + "path": "rotation" + } + }, + { + "sampler": 78, + "target": { + "node": 83, + "path": "translation" + } + }, + { + "sampler": 79, + "target": { + "node": 83, + "path": "scale" + } + }, + { + "sampler": 80, + "target": { + "node": 83, + "path": "rotation" + } + }, + { + "sampler": 81, + "target": { + "node": 87, + "path": "translation" + } + }, + { + "sampler": 82, + "target": { + "node": 87, + "path": "scale" + } + }, + { + "sampler": 83, + "target": { + "node": 87, + "path": "rotation" + } + }, + { + "sampler": 84, + "target": { + "node": 85, + "path": "translation" + } + }, + { + "sampler": 85, + "target": { + "node": 85, + "path": "scale" + } + }, + { + "sampler": 86, + "target": { + "node": 85, + "path": "rotation" + } + }, + { + "sampler": 87, + "target": { + "node": 91, + "path": "translation" + } + }, + { + "sampler": 88, + "target": { + "node": 91, + "path": "scale" + } + }, + { + "sampler": 89, + "target": { + "node": 91, + "path": "rotation" + } + }, + { + "sampler": 90, + "target": { + "node": 89, + "path": "translation" + } + }, + { + "sampler": 91, + "target": { + "node": 89, + "path": "scale" + } + }, + { + "sampler": 92, + "target": { + "node": 89, + "path": "rotation" + } + }, + { + "sampler": 93, + "target": { + "node": 13, + "path": "translation" + } + }, + { + "sampler": 94, + "target": { + "node": 13, + "path": "scale" + } + }, + { + "sampler": 95, + "target": { + "node": 13, + "path": "rotation" + } + }, + { + "sampler": 96, + "target": { + "node": 9, + "path": "translation" + } + }, + { + "sampler": 97, + "target": { + "node": 9, + "path": "scale" + } + }, + { + "sampler": 98, + "target": { + "node": 9, + "path": "rotation" + } + }, + { + "sampler": 99, + "target": { + "node": 84, + "path": "weights" + } + } + ], + "name": "CINEMA_4D_Basis", + "samplers": [ + { + "input": 4, + "interpolation": "LINEAR", + "output": 5 + }, + { + "input": 6, + "interpolation": "LINEAR", + "output": 7 + }, + { + "input": 8, + "interpolation": "LINEAR", + "output": 9 + }, + { + "input": 10, + "interpolation": "LINEAR", + "output": 11 + }, + { + "input": 12, + "interpolation": "LINEAR", + "output": 13 + }, + { + "input": 14, + "interpolation": "LINEAR", + "output": 15 + }, + { + "input": 16, + "interpolation": "LINEAR", + "output": 17 + }, + { + "input": 18, + "interpolation": "LINEAR", + "output": 19 + }, + { + "input": 20, + "interpolation": "LINEAR", + "output": 21 + }, + { + "input": 22, + "interpolation": "LINEAR", + "output": 23 + }, + { + "input": 24, + "interpolation": "LINEAR", + "output": 25 + }, + { + "input": 26, + "interpolation": "LINEAR", + "output": 27 + }, + { + "input": 28, + "interpolation": "LINEAR", + "output": 29 + }, + { + "input": 30, + "interpolation": "LINEAR", + "output": 31 + }, + { + "input": 32, + "interpolation": "LINEAR", + "output": 33 + }, + { + "input": 34, + "interpolation": "LINEAR", + "output": 35 + }, + { + "input": 36, + "interpolation": "LINEAR", + "output": 37 + }, + { + "input": 38, + "interpolation": "LINEAR", + "output": 39 + }, + { + "input": 40, + "interpolation": "LINEAR", + "output": 41 + }, + { + "input": 42, + "interpolation": "LINEAR", + "output": 43 + }, + { + "input": 44, + "interpolation": "LINEAR", + "output": 45 + }, + { + "input": 46, + "interpolation": "LINEAR", + "output": 47 + }, + { + "input": 48, + "interpolation": "LINEAR", + "output": 49 + }, + { + "input": 50, + "interpolation": "LINEAR", + "output": 51 + }, + { + "input": 52, + "interpolation": "LINEAR", + "output": 53 + }, + { + "input": 54, + "interpolation": "LINEAR", + "output": 55 + }, + { + "input": 56, + "interpolation": "LINEAR", + "output": 57 + }, + { + "input": 58, + "interpolation": "LINEAR", + "output": 59 + }, + { + "input": 60, + "interpolation": "LINEAR", + "output": 61 + }, + { + "input": 62, + "interpolation": "LINEAR", + "output": 63 + }, + { + "input": 64, + "interpolation": "LINEAR", + "output": 65 + }, + { + "input": 66, + "interpolation": "LINEAR", + "output": 67 + }, + { + "input": 68, + "interpolation": "LINEAR", + "output": 69 + }, + { + "input": 70, + "interpolation": "LINEAR", + "output": 71 + }, + { + "input": 72, + "interpolation": "LINEAR", + "output": 73 + }, + { + "input": 74, + "interpolation": "LINEAR", + "output": 75 + }, + { + "input": 76, + "interpolation": "LINEAR", + "output": 77 + }, + { + "input": 78, + "interpolation": "LINEAR", + "output": 79 + }, + { + "input": 80, + "interpolation": "LINEAR", + "output": 81 + }, + { + "input": 82, + "interpolation": "LINEAR", + "output": 83 + }, + { + "input": 84, + "interpolation": "LINEAR", + "output": 85 + }, + { + "input": 86, + "interpolation": "LINEAR", + "output": 87 + }, + { + "input": 88, + "interpolation": "LINEAR", + "output": 89 + }, + { + "input": 90, + "interpolation": "LINEAR", + "output": 91 + }, + { + "input": 92, + "interpolation": "LINEAR", + "output": 93 + }, + { + "input": 94, + "interpolation": "LINEAR", + "output": 95 + }, + { + "input": 96, + "interpolation": "LINEAR", + "output": 97 + }, + { + "input": 98, + "interpolation": "LINEAR", + "output": 99 + }, + { + "input": 100, + "interpolation": "LINEAR", + "output": 101 + }, + { + "input": 102, + "interpolation": "LINEAR", + "output": 103 + }, + { + "input": 104, + "interpolation": "LINEAR", + "output": 105 + }, + { + "input": 106, + "interpolation": "LINEAR", + "output": 107 + }, + { + "input": 108, + "interpolation": "LINEAR", + "output": 109 + }, + { + "input": 110, + "interpolation": "LINEAR", + "output": 111 + }, + { + "input": 112, + "interpolation": "LINEAR", + "output": 113 + }, + { + "input": 114, + "interpolation": "LINEAR", + "output": 115 + }, + { + "input": 116, + "interpolation": "LINEAR", + "output": 117 + }, + { + "input": 118, + "interpolation": "LINEAR", + "output": 119 + }, + { + "input": 120, + "interpolation": "LINEAR", + "output": 121 + }, + { + "input": 122, + "interpolation": "LINEAR", + "output": 123 + }, + { + "input": 124, + "interpolation": "LINEAR", + "output": 125 + }, + { + "input": 126, + "interpolation": "LINEAR", + "output": 127 + }, + { + "input": 128, + "interpolation": "LINEAR", + "output": 129 + }, + { + "input": 130, + "interpolation": "LINEAR", + "output": 131 + }, + { + "input": 132, + "interpolation": "LINEAR", + "output": 133 + }, + { + "input": 134, + "interpolation": "LINEAR", + "output": 135 + }, + { + "input": 136, + "interpolation": "LINEAR", + "output": 137 + }, + { + "input": 138, + "interpolation": "LINEAR", + "output": 139 + }, + { + "input": 140, + "interpolation": "LINEAR", + "output": 141 + }, + { + "input": 142, + "interpolation": "LINEAR", + "output": 143 + }, + { + "input": 144, + "interpolation": "LINEAR", + "output": 145 + }, + { + "input": 146, + "interpolation": "LINEAR", + "output": 147 + }, + { + "input": 148, + "interpolation": "LINEAR", + "output": 149 + }, + { + "input": 150, + "interpolation": "LINEAR", + "output": 151 + }, + { + "input": 152, + "interpolation": "LINEAR", + "output": 153 + }, + { + "input": 154, + "interpolation": "LINEAR", + "output": 155 + }, + { + "input": 156, + "interpolation": "LINEAR", + "output": 157 + }, + { + "input": 158, + "interpolation": "LINEAR", + "output": 159 + }, + { + "input": 160, + "interpolation": "LINEAR", + "output": 161 + }, + { + "input": 162, + "interpolation": "LINEAR", + "output": 163 + }, + { + "input": 164, + "interpolation": "LINEAR", + "output": 165 + }, + { + "input": 166, + "interpolation": "LINEAR", + "output": 167 + }, + { + "input": 168, + "interpolation": "LINEAR", + "output": 169 + }, + { + "input": 170, + "interpolation": "LINEAR", + "output": 171 + }, + { + "input": 172, + "interpolation": "LINEAR", + "output": 173 + }, + { + "input": 174, + "interpolation": "LINEAR", + "output": 175 + }, + { + "input": 176, + "interpolation": "LINEAR", + "output": 177 + }, + { + "input": 178, + "interpolation": "LINEAR", + "output": 179 + }, + { + "input": 180, + "interpolation": "LINEAR", + "output": 181 + }, + { + "input": 182, + "interpolation": "LINEAR", + "output": 183 + }, + { + "input": 184, + "interpolation": "LINEAR", + "output": 185 + }, + { + "input": 186, + "interpolation": "LINEAR", + "output": 187 + }, + { + "input": 188, + "interpolation": "LINEAR", + "output": 189 + }, + { + "input": 190, + "interpolation": "LINEAR", + "output": 191 + }, + { + "input": 192, + "interpolation": "LINEAR", + "output": 193 + }, + { + "input": 194, + "interpolation": "LINEAR", + "output": 195 + }, + { + "input": 196, + "interpolation": "LINEAR", + "output": 197 + }, + { + "input": 198, + "interpolation": "LINEAR", + "output": 199 + }, + { + "input": 200, + "interpolation": "LINEAR", + "output": 201 + }, + { + "input": 202, + "interpolation": "LINEAR", + "output": 203 + } + ] + } + ], + "asset": { + "extras": { + "author": "LaVADraGoN (https://sketchfab.com/lavadragon)", + "license": "CC-BY-NC-4.0 (http://creativecommons.org/licenses/by-nc/4.0/)", + "source": "https://sketchfab.com/3d-models/buster-drone-294e79652f494130ad2ab00a13fdbafd", + "title": "Buster Drone" + }, + "generator": "Sketchfab-7.38.0", + "version": "2.0" + }, + "bufferViews": [ + { + "buffer": 0, + "byteLength": 614976, + "byteOffset": 0, + "byteStride": 12, + "name": "floatBufferViews", + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 175676, + "byteOffset": 614976, + "name": "floatBufferViews" + }, + { + "buffer": 0, + "byteLength": 86916, + "byteOffset": 790652, + "byteStride": 12, + "name": "floatBufferViews" + }, + { + "buffer": 0, + "byteLength": 562384, + "byteOffset": 877568, + "byteStride": 16, + "name": "floatBufferViews" + }, + { + "buffer": 0, + "byteOffset": 1439952, + "byteLength": 354 + }, + { + "buffer": 0, + "byteOffset": 1440308, + "byteLength": 24007 + }, + { + "buffer": 0, + "byteOffset": 1464316, + "byteLength": 1381 + }, + { + "buffer": 0, + "byteOffset": 1465700, + "byteLength": 9138 + }, + { + "buffer": 0, + "byteOffset": 1474840, + "byteLength": 20853 + }, + { + "buffer": 0, + "byteOffset": 1495696, + "byteLength": 9029 + }, + { + "buffer": 0, + "byteOffset": 1504728, + "byteLength": 16918 + }, + { + "buffer": 0, + "byteOffset": 1521648, + "byteLength": 1435 + }, + { + "buffer": 0, + "byteOffset": 1523084, + "byteLength": 9013 + }, + { + "buffer": 0, + "byteOffset": 1532100, + "byteLength": 15509 + }, + { + "buffer": 0, + "byteOffset": 1547612, + "byteLength": 4226 + }, + { + "buffer": 0, + "byteOffset": 1551840, + "byteLength": 3250 + }, + { + "buffer": 0, + "byteOffset": 1555092, + "byteLength": 1382 + }, + { + "buffer": 0, + "byteOffset": 1556476, + "byteLength": 9352 + }, + { + "buffer": 0, + "byteOffset": 1565828, + "byteLength": 21051 + }, + { + "buffer": 0, + "byteOffset": 1586880, + "byteLength": 9282 + }, + { + "buffer": 0, + "byteOffset": 1596164, + "byteLength": 16939 + }, + { + "buffer": 0, + "byteOffset": 1613104, + "byteLength": 1384 + }, + { + "buffer": 0, + "byteOffset": 1614488, + "byteLength": 9284 + }, + { + "buffer": 0, + "byteOffset": 1623772, + "byteLength": 15464 + }, + { + "buffer": 0, + "byteOffset": 1639236, + "byteLength": 4109 + }, + { + "buffer": 0, + "byteOffset": 1643348, + "byteLength": 3147 + }, + { + "buffer": 0, + "byteOffset": 1646496, + "byteLength": 1396 + }, + { + "buffer": 0, + "byteOffset": 1647892, + "byteLength": 9088 + }, + { + "buffer": 0, + "byteOffset": 1656980, + "byteLength": 20226 + }, + { + "buffer": 0, + "byteOffset": 1677208, + "byteLength": 9096 + }, + { + "buffer": 0, + "byteOffset": 1686304, + "byteLength": 16935 + }, + { + "buffer": 0, + "byteOffset": 1703240, + "byteLength": 1378 + }, + { + "buffer": 0, + "byteOffset": 1704620, + "byteLength": 9040 + }, + { + "buffer": 0, + "byteOffset": 1713660, + "byteLength": 15560 + }, + { + "buffer": 0, + "byteOffset": 1729220, + "byteLength": 1310 + }, + { + "buffer": 0, + "byteOffset": 1730532, + "byteLength": 1346 + }, + { + "buffer": 0, + "byteOffset": 1731880, + "byteLength": 1325 + }, + { + "buffer": 0, + "byteOffset": 1733208, + "byteLength": 1847 + }, + { + "buffer": 0, + "byteOffset": 1735056, + "byteLength": 719 + }, + { + "buffer": 0, + "byteOffset": 1735776, + "byteLength": 18693 + }, + { + "buffer": 0, + "byteOffset": 1754472, + "byteLength": 17770 + }, + { + "buffer": 0, + "byteOffset": 1772244, + "byteLength": 20941 + }, + { + "buffer": 0, + "byteOffset": 1793188, + "byteLength": 17590 + } + ], + "buffers": [ + { + "name": "scene", + "byteLength": 1810780, + "uri": "scene.bin" + } + ], + "images": [ + { + "name": "Boden_baseColor", + "uri": "textures/Boden_baseColor.jpg" + }, + { + "name": "Boden_metallicRoughness", + "uri": "textures/Boden_metallicRoughness.jpg" + }, + { + "name": "Boden_normal", + "uri": "textures/Boden_normal.jpg" + }, + { + "name": "body_baseColor", + "uri": "textures/body_baseColor.jpg" + }, + { + "name": "body_metallicRoughness", + "uri": "textures/body_metallicRoughness.jpg" + }, + { + "name": "body_emissive", + "uri": "textures/body_emissive.jpg" + }, + { + "name": "body_normal", + "uri": "textures/body_normal.jpg" + }, + { + "name": "material_baseColor", + "uri": "textures/material_baseColor.jpg" + }, + { + "name": "material_metallicRoughness", + "uri": "textures/material_metallicRoughness.jpg" + }, + { + "name": "material_normal", + "uri": "textures/material_normal.jpg" + } + ], + "materials": [ + { + "doubleSided": false, + "name": "Boden", + "normalTexture": { + "index": 2, + "scale": 1, + "texCoord": 0 + }, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 1, + 1, + 1, + 1 + ], + "baseColorTexture": { + "index": 0, + "texCoord": 0 + }, + "metallicFactor": 1, + "metallicRoughnessTexture": { + "index": 1, + "texCoord": 0 + }, + "roughnessFactor": 1 + }, + "emissiveFactor": [ + 0, + 0, + 0 + ] + }, + { + "doubleSided": false, + "emissiveFactor": [ + 1, + 1, + 1 + ], + "emissiveTexture": { + "index": 5, + "texCoord": 0 + }, + "name": "body", + "normalTexture": { + "index": 6, + "scale": 1, + "texCoord": 0 + }, + "occlusionTexture": { + "index": 4, + "strength": 1, + "texCoord": 0 + }, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 1, + 1, + 1, + 1 + ], + "baseColorTexture": { + "index": 3, + "texCoord": 0 + }, + "metallicFactor": 1, + "metallicRoughnessTexture": { + "index": 4, + "texCoord": 0 + }, + "roughnessFactor": 1 + } + }, + { + "doubleSided": false, + "name": "material", + "normalTexture": { + "index": 9, + "scale": 1, + "texCoord": 0 + }, + "occlusionTexture": { + "index": 8, + "strength": 1, + "texCoord": 0 + }, + "pbrMetallicRoughness": { + "baseColorFactor": [ + 1, + 1, + 1, + 1 + ], + "baseColorTexture": { + "index": 7, + "texCoord": 0 + }, + "metallicFactor": 1, + "metallicRoughnessTexture": { + "index": 8, + "texCoord": 0 + }, + "roughnessFactor": 1 + }, + "emissiveFactor": [ + 0, + 0, + 0 + ], + "alphaMode": "OPAQUE" + } + ], + "meshes": [ + { + "name": "Scheibe_Boden_0", + "primitives": [ + { + "attributes": { + "NORMAL": 205, + "POSITION": 206, + "TANGENT": 207, + "TEXCOORD_0": 208, + "TEXCOORD_1": 209 + }, + "indices": 204, + "material": 0, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 4, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4 + } + } + } + } + ] + }, + { + "name": "Drone_Body_body_0", + "primitives": [ + { + "attributes": { + "NORMAL": 211, + "POSITION": 212, + "TANGENT": 213, + "TEXCOORD_0": 214, + "TEXCOORD_1": 215, + "TEXCOORD_2": 216, + "TEXCOORD_3": 217, + "TEXCOORD_4": 218 + }, + "indices": 210, + "material": 1, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 5, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6, + "TEXCOORD_4": 7 + } + } + } + } + ] + }, + { + "name": "Drone_leg_F_leg_0", + "primitives": [ + { + "attributes": { + "NORMAL": 220, + "POSITION": 221, + "TANGENT": 222, + "TEXCOORD_0": 223, + "TEXCOORD_1": 224, + "TEXCOORD_2": 225, + "TEXCOORD_3": 226 + }, + "indices": 219, + "material": 2, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 6, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6 + } + } + } + } + ] + }, + { + "name": "F_P1_G_leg_0", + "primitives": [ + { + "attributes": { + "NORMAL": 228, + "POSITION": 229, + "TANGENT": 230, + "TEXCOORD_0": 231, + "TEXCOORD_1": 232, + "TEXCOORD_2": 233, + "TEXCOORD_3": 234 + }, + "indices": 227, + "material": 2, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 7, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6 + } + } + } + } + ] + }, + { + "name": "F_P2_leg_0", + "primitives": [ + { + "attributes": { + "NORMAL": 236, + "POSITION": 237, + "TANGENT": 238, + "TEXCOORD_0": 239, + "TEXCOORD_1": 240, + "TEXCOORD_2": 241, + "TEXCOORD_3": 242 + }, + "indices": 235, + "material": 2, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 8, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6 + } + } + } + } + ] + }, + { + "name": "F_P3_G_leg_0", + "primitives": [ + { + "attributes": { + "NORMAL": 244, + "POSITION": 245, + "TANGENT": 246, + "TEXCOORD_0": 247, + "TEXCOORD_1": 248, + "TEXCOORD_2": 249, + "TEXCOORD_3": 250 + }, + "indices": 243, + "material": 2, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 9, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6 + } + } + } + } + ] + }, + { + "name": "F_P4_leg_0", + "primitives": [ + { + "attributes": { + "NORMAL": 252, + "POSITION": 253, + "TANGENT": 254, + "TEXCOORD_0": 255, + "TEXCOORD_1": 256, + "TEXCOORD_2": 257, + "TEXCOORD_3": 258 + }, + "indices": 251, + "material": 2, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 10, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6 + } + } + } + } + ] + }, + { + "name": "F_P5_M_leg_0", + "primitives": [ + { + "attributes": { + "NORMAL": 260, + "POSITION": 261, + "TANGENT": 262, + "TEXCOORD_0": 263, + "TEXCOORD_1": 264, + "TEXCOORD_2": 265, + "TEXCOORD_3": 266 + }, + "indices": 259, + "material": 2, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 11, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6 + } + } + } + } + ] + }, + { + "name": "F_P6_G_leg_0", + "primitives": [ + { + "attributes": { + "NORMAL": 268, + "POSITION": 269, + "TANGENT": 270, + "TEXCOORD_0": 271, + "TEXCOORD_1": 272, + "TEXCOORD_2": 273, + "TEXCOORD_3": 274 + }, + "indices": 267, + "material": 2, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 12, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6 + } + } + } + } + ] + }, + { + "name": "F_P7_leg_0", + "primitives": [ + { + "attributes": { + "NORMAL": 276, + "POSITION": 277, + "TANGENT": 278, + "TEXCOORD_0": 279, + "TEXCOORD_1": 280, + "TEXCOORD_2": 281, + "TEXCOORD_3": 282 + }, + "indices": 275, + "material": 2, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 13, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6 + } + } + } + } + ] + }, + { + "name": "0", + "primitives": [ + { + "attributes": { + "NORMAL": 284, + "POSITION": 285, + "TANGENT": 286, + "TEXCOORD_0": 287, + "TEXCOORD_1": 288, + "TEXCOORD_2": 289, + "TEXCOORD_3": 290, + "TEXCOORD_4": 291 + }, + "indices": 283, + "material": 1, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 14, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6, + "TEXCOORD_4": 7 + } + } + } + } + ] + }, + { + "name": "Drone_Panel_R_body_0", + "primitives": [ + { + "attributes": { + "NORMAL": 293, + "POSITION": 294, + "TANGENT": 295, + "TEXCOORD_0": 296, + "TEXCOORD_1": 297, + "TEXCOORD_2": 298, + "TEXCOORD_3": 299, + "TEXCOORD_4": 300 + }, + "indices": 292, + "material": 1, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 15, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6, + "TEXCOORD_4": 7 + } + } + } + } + ] + }, + { + "name": "Drone_leg_R_leg_0", + "primitives": [ + { + "attributes": { + "NORMAL": 302, + "POSITION": 303, + "TANGENT": 304, + "TEXCOORD_0": 305, + "TEXCOORD_1": 306, + "TEXCOORD_2": 307, + "TEXCOORD_3": 308 + }, + "indices": 301, + "material": 2, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 16, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6 + } + } + } + } + ] + }, + { + "name": "R_P1_G_leg_0", + "primitives": [ + { + "attributes": { + "NORMAL": 310, + "POSITION": 311, + "TANGENT": 312, + "TEXCOORD_0": 313, + "TEXCOORD_1": 314, + "TEXCOORD_2": 315, + "TEXCOORD_3": 316 + }, + "indices": 309, + "material": 2, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 17, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6 + } + } + } + } + ] + }, + { + "name": "R_P2_leg_0", + "primitives": [ + { + "attributes": { + "NORMAL": 318, + "POSITION": 319, + "TANGENT": 320, + "TEXCOORD_0": 321, + "TEXCOORD_1": 322, + "TEXCOORD_2": 323, + "TEXCOORD_3": 324 + }, + "indices": 317, + "material": 2, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 18, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6 + } + } + } + } + ] + }, + { + "name": "R_P3_G_leg_0", + "primitives": [ + { + "attributes": { + "NORMAL": 326, + "POSITION": 327, + "TANGENT": 328, + "TEXCOORD_0": 329, + "TEXCOORD_1": 330, + "TEXCOORD_2": 331, + "TEXCOORD_3": 332 + }, + "indices": 325, + "material": 2, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 19, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6 + } + } + } + } + ] + }, + { + "name": "R_P4_leg_0", + "primitives": [ + { + "attributes": { + "NORMAL": 334, + "POSITION": 335, + "TANGENT": 336, + "TEXCOORD_0": 337, + "TEXCOORD_1": 338, + "TEXCOORD_2": 339, + "TEXCOORD_3": 340 + }, + "indices": 333, + "material": 2, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 20, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6 + } + } + } + } + ] + }, + { + "name": "R_P5_M_leg_0", + "primitives": [ + { + "attributes": { + "NORMAL": 342, + "POSITION": 343, + "TANGENT": 344, + "TEXCOORD_0": 345, + "TEXCOORD_1": 346, + "TEXCOORD_2": 347, + "TEXCOORD_3": 348 + }, + "indices": 341, + "material": 2, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 21, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6 + } + } + } + } + ] + }, + { + "name": "R_P6_G_leg_0", + "primitives": [ + { + "attributes": { + "NORMAL": 350, + "POSITION": 351, + "TANGENT": 352, + "TEXCOORD_0": 353, + "TEXCOORD_1": 354, + "TEXCOORD_2": 355, + "TEXCOORD_3": 356 + }, + "indices": 349, + "material": 2, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 22, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6 + } + } + } + } + ] + }, + { + "name": "R_P7_leg_0", + "primitives": [ + { + "attributes": { + "NORMAL": 358, + "POSITION": 359, + "TANGENT": 360, + "TEXCOORD_0": 361, + "TEXCOORD_1": 362, + "TEXCOORD_2": 363, + "TEXCOORD_3": 364 + }, + "indices": 357, + "material": 2, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 23, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6 + } + } + } + } + ] + }, + { + "name": "Drone_Gen_L_body_0", + "primitives": [ + { + "attributes": { + "NORMAL": 366, + "POSITION": 367, + "TANGENT": 368, + "TEXCOORD_0": 369, + "TEXCOORD_1": 370, + "TEXCOORD_2": 371, + "TEXCOORD_3": 372, + "TEXCOORD_4": 373 + }, + "indices": 365, + "material": 1, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 24, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6, + "TEXCOORD_4": 7 + } + } + } + } + ] + }, + { + "name": "Drone_Panel_L_body_0", + "primitives": [ + { + "attributes": { + "NORMAL": 375, + "POSITION": 376, + "TANGENT": 377, + "TEXCOORD_0": 378, + "TEXCOORD_1": 379, + "TEXCOORD_2": 380, + "TEXCOORD_3": 381, + "TEXCOORD_4": 382 + }, + "indices": 374, + "material": 1, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 25, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6, + "TEXCOORD_4": 7 + } + } + } + } + ] + }, + { + "name": "Drone_leg_L_leg_0", + "primitives": [ + { + "attributes": { + "NORMAL": 384, + "POSITION": 385, + "TANGENT": 386, + "TEXCOORD_0": 387, + "TEXCOORD_1": 388, + "TEXCOORD_2": 389, + "TEXCOORD_3": 390 + }, + "indices": 383, + "material": 2, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 26, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6 + } + } + } + } + ] + }, + { + "name": "L_P1_G_leg_0", + "primitives": [ + { + "attributes": { + "NORMAL": 392, + "POSITION": 393, + "TANGENT": 394, + "TEXCOORD_0": 395, + "TEXCOORD_1": 396, + "TEXCOORD_2": 397, + "TEXCOORD_3": 398 + }, + "indices": 391, + "material": 2, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 27, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6 + } + } + } + } + ] + }, + { + "name": "L_P2_leg_0", + "primitives": [ + { + "attributes": { + "NORMAL": 400, + "POSITION": 401, + "TANGENT": 402, + "TEXCOORD_0": 403, + "TEXCOORD_1": 404, + "TEXCOORD_2": 405, + "TEXCOORD_3": 406 + }, + "indices": 399, + "material": 2, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 28, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6 + } + } + } + } + ] + }, + { + "name": "L_P3_G_leg_0", + "primitives": [ + { + "attributes": { + "NORMAL": 408, + "POSITION": 409, + "TANGENT": 410, + "TEXCOORD_0": 411, + "TEXCOORD_1": 412, + "TEXCOORD_2": 413, + "TEXCOORD_3": 414 + }, + "indices": 407, + "material": 2, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 29, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6 + } + } + } + } + ] + }, + { + "name": "L_P4_leg_0", + "primitives": [ + { + "attributes": { + "NORMAL": 416, + "POSITION": 417, + "TANGENT": 418, + "TEXCOORD_0": 419, + "TEXCOORD_1": 420, + "TEXCOORD_2": 421, + "TEXCOORD_3": 422 + }, + "indices": 415, + "material": 2, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 30, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6 + } + } + } + } + ] + }, + { + "name": "L_P5_M_leg_0", + "primitives": [ + { + "attributes": { + "NORMAL": 424, + "POSITION": 425, + "TANGENT": 426, + "TEXCOORD_0": 427, + "TEXCOORD_1": 428, + "TEXCOORD_2": 429, + "TEXCOORD_3": 430 + }, + "indices": 423, + "material": 2, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 31, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6 + } + } + } + } + ] + }, + { + "name": "L_P6_G_leg_0", + "primitives": [ + { + "attributes": { + "NORMAL": 432, + "POSITION": 433, + "TANGENT": 434, + "TEXCOORD_0": 435, + "TEXCOORD_1": 436, + "TEXCOORD_2": 437, + "TEXCOORD_3": 438 + }, + "indices": 431, + "material": 2, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 32, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6 + } + } + } + } + ] + }, + { + "name": "L_P7_leg_0", + "primitives": [ + { + "attributes": { + "NORMAL": 440, + "POSITION": 441, + "TANGENT": 442, + "TEXCOORD_0": 443, + "TEXCOORD_1": 444, + "TEXCOORD_2": 445, + "TEXCOORD_3": 446 + }, + "indices": 439, + "material": 2, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 33, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6 + } + } + } + } + ] + }, + { + "name": "Drone_UPanel_R_body_0", + "primitives": [ + { + "attributes": { + "NORMAL": 448, + "POSITION": 449, + "TANGENT": 450, + "TEXCOORD_0": 451, + "TEXCOORD_1": 452, + "TEXCOORD_2": 453, + "TEXCOORD_3": 454, + "TEXCOORD_4": 455 + }, + "indices": 447, + "material": 1, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 34, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6, + "TEXCOORD_4": 7 + } + } + } + } + ] + }, + { + "name": "Drone_UPanel_L_body_0", + "primitives": [ + { + "attributes": { + "NORMAL": 457, + "POSITION": 458, + "TANGENT": 459, + "TEXCOORD_0": 460, + "TEXCOORD_1": 461, + "TEXCOORD_2": 462, + "TEXCOORD_3": 463, + "TEXCOORD_4": 464 + }, + "indices": 456, + "material": 1, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 35, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6, + "TEXCOORD_4": 7 + } + } + } + } + ] + }, + { + "name": "Drone_UPart_body_0", + "primitives": [ + { + "attributes": { + "NORMAL": 466, + "POSITION": 467, + "TANGENT": 468, + "TEXCOORD_0": 469, + "TEXCOORD_1": 470, + "TEXCOORD_2": 471, + "TEXCOORD_3": 472, + "TEXCOORD_4": 473 + }, + "indices": 465, + "material": 1, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 36, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6, + "TEXCOORD_4": 7 + } + } + } + } + ] + }, + { + "name": "Drone_ILens_body_0", + "primitives": [ + { + "attributes": { + "NORMAL": 475, + "POSITION": 476, + "TANGENT": 477, + "TEXCOORD_0": 478, + "TEXCOORD_1": 479, + "TEXCOORD_2": 480, + "TEXCOORD_3": 481, + "TEXCOORD_4": 482 + }, + "indices": 474, + "material": 1, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 37, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6, + "TEXCOORD_4": 7 + } + } + } + } + ] + }, + { + "name": "1", + "primitives": [ + { + "attributes": { + "NORMAL": 484, + "POSITION": 485, + "TANGENT": 486, + "TEXCOORD_0": 487, + "TEXCOORD_1": 488, + "TEXCOORD_2": 489, + "TEXCOORD_3": 490, + "TEXCOORD_4": 491 + }, + "indices": 483, + "material": 1, + "mode": 4, + "targets": [ + { + "NORMAL": 1, + "POSITION": 0 + }, + { + "NORMAL": 3, + "POSITION": 2 + } + ], + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 38, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6, + "TEXCOORD_4": 7 + } + } + } + } + ], + "weights": [ + 0, + 0 + ] + }, + { + "name": "Drone_Turb_M_L_body_0", + "primitives": [ + { + "attributes": { + "NORMAL": 493, + "POSITION": 494, + "TANGENT": 495, + "TEXCOORD_0": 496, + "TEXCOORD_1": 497, + "TEXCOORD_2": 498, + "TEXCOORD_3": 499, + "TEXCOORD_4": 500 + }, + "indices": 492, + "material": 1, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 39, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6, + "TEXCOORD_4": 7 + } + } + } + } + ] + }, + { + "name": "Drone_Turb_Blade_L_body_0", + "primitives": [ + { + "attributes": { + "NORMAL": 502, + "POSITION": 503, + "TANGENT": 504, + "TEXCOORD_0": 505, + "TEXCOORD_1": 506, + "TEXCOORD_2": 507, + "TEXCOORD_3": 508, + "TEXCOORD_4": 509 + }, + "indices": 501, + "material": 1, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 40, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6, + "TEXCOORD_4": 7 + } + } + } + } + ] + }, + { + "name": "Drone_Turb_M_R_body_0", + "primitives": [ + { + "attributes": { + "NORMAL": 511, + "POSITION": 512, + "TANGENT": 513, + "TEXCOORD_0": 514, + "TEXCOORD_1": 515, + "TEXCOORD_2": 516, + "TEXCOORD_3": 517, + "TEXCOORD_4": 518 + }, + "indices": 510, + "material": 1, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 41, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6, + "TEXCOORD_4": 7 + } + } + } + } + ] + }, + { + "name": "Drone_Turb_Blade_R_body_0", + "primitives": [ + { + "attributes": { + "NORMAL": 520, + "POSITION": 521, + "TANGENT": 522, + "TEXCOORD_0": 523, + "TEXCOORD_1": 524, + "TEXCOORD_2": 525, + "TEXCOORD_3": 526, + "TEXCOORD_4": 527 + }, + "indices": 519, + "material": 1, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 42, + "attributes": { + "NORMAL": 0, + "POSITION": 1, + "TANGENT": 2, + "TEXCOORD_0": 3, + "TEXCOORD_1": 4, + "TEXCOORD_2": 5, + "TEXCOORD_3": 6, + "TEXCOORD_4": 7 + } + } + } + } + ] + } + ], + "nodes": [ + { + "children": [ + 1 + ], + "name": "RootNode (gltf orientation matrix)", + "rotation": [ + -0.7071067811865475, + 0, + 0, + 0.7071067811865476 + ], + "translation": [ + 0, + 0, + 0 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "children": [ + 2 + ], + "name": "RootNode (model correction matrix)" + }, + { + "children": [ + 3 + ], + "matrix": [ + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + -1, + 0, + 0, + 0, + 0, + 0, + 1 + ], + "name": "BusterDrone.fbx" + }, + { + "children": [ + 4 + ], + "name": "" + }, + { + "children": [ + 5, + 9 + ], + "name": "RootNode" + }, + { + "children": [ + 6, + 8 + ], + "name": "Env" + }, + { + "children": [ + 7 + ], + "name": "Scheibe", + "rotation": [ + 0.5, + 0.5, + -0.5, + 0.5 + ], + "translation": [ + 0, + -99, + 6.1232337864698064e-15 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 0, + "name": "Scheibe_Boden_0" + }, + { + "name": "Himmel" + }, + { + "children": [ + 10, + 13 + ], + "name": "Drone_Controller", + "translation": [ + 0, + 0, + 0 + ], + "rotation": [ + 0, + 0, + 0, + 1 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "children": [ + 11, + 12 + ], + "name": "Turbine_Controller", + "translation": [ + 0, + -100, + -5 + ], + "rotation": [ + 0, + 0, + 0, + 1 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "name": "Turbine_R", + "rotation": [ + 0.7071068286895752, + 0, + 0, + 0.7071068286895752 + ], + "translation": [ + 12.244799613952637, + 0, + -1.0070199966430664 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "name": "Turbine_L", + "rotation": [ + 0.7071068286895752, + 0, + 0, + 0.7071068286895752 + ], + "translation": [ + -12.244799613952637, + 0, + -1.0070199966430664 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "children": [ + 14, + 16 + ], + "name": "U_MassPoint", + "rotation": [ + 0.6133379936218262, + 0, + 0, + 0.789820671081543 + ], + "translation": [ + 0, + 0.11856790632009506, + -12.910046577453613 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "children": [ + 15 + ], + "name": "Eye_Controller", + "rotation": [ + 0.017331130802631378, + 0, + 0, + 0.9998499155044556 + ], + "translation": [ + 0, + 143.0728759765625, + 58.6367301940918 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "name": "Eye_Pupil", + "rotation": [ + -0.7071068286895752, + 0, + 0, + 0.7071068286895752 + ], + "translation": [ + 0, + 0, + 0 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "children": [ + 17 + ], + "name": "D_MassPoint", + "translation": [ + 0, + -1.7763568394002505e-15, + 77.5 + ], + "rotation": [ + 0, + 0, + 0, + 1 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "children": [ + 18, + 19, + 35, + 55, + 75, + 77, + 79, + 81, + 85, + 89 + ], + "name": "Drone_Body", + "rotation": [ + -0.7071068286895752, + 0, + 0, + 0.7071068286895752 + ], + "translation": [ + 0, + 0, + -37.5 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 1, + "name": "Drone_Body_body_0" + }, + { + "children": [ + 20, + 21 + ], + "name": "Drone_leg_F", + "translation": [ + 0, + -38.68080139160156, + 20.01194953918457 + ], + "rotation": [ + 0, + 0, + 0, + 1 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 2, + "name": "Drone_leg_F_leg_0" + }, + { + "children": [ + 22, + 23 + ], + "name": "F_P1_G", + "rotation": [ + -9.472242394202753e-15, + 0.7071065902709961, + 9.472236464972122e-15, + 0.7071070075035095 + ], + "translation": [ + 0.006630018353462219, + -5.510501384735107, + -0.09275053441524506 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 3, + "name": "F_P1_G_leg_0" + }, + { + "children": [ + 24, + 25 + ], + "name": "F_P2", + "rotation": [ + 0.4777144193649292, + 0.5213338136672974, + -0.4777144193649292, + 0.5213338136672974 + ], + "translation": [ + 0, + -0.00004080753933521919, + 0 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 4, + "name": "F_P2_leg_0" + }, + { + "children": [ + 26, + 27 + ], + "name": "F_P3_G", + "rotation": [ + -1.5265570559062843e-16, + -4.1108839354819793e-7, + -8.700754859676185e-17, + 1 + ], + "translation": [ + 0.00007688588812015951, + -22.9158935546875, + 0.02676698938012123 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 5, + "name": "F_P3_G_leg_0" + }, + { + "children": [ + 28, + 29 + ], + "name": "F_P4", + "rotation": [ + 4.003213763390976e-16, + -0.5735764503479004, + 0.8191520571708679, + -6.550924359051687e-17 + ], + "translation": [ + 0, + 0, + 0 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 6, + "name": "F_P4_leg_0" + }, + { + "children": [ + 30, + 31 + ], + "name": "F_P5_M", + "rotation": [ + 1.3877787807814457e-17, + -5.293955920339377e-23, + -9.926167350636332e-23, + 1 + ], + "translation": [ + -0.01574668288230896, + -11.282759666442871, + 0.081717349588871 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 7, + "name": "F_P5_M_leg_0" + }, + { + "children": [ + 32, + 33 + ], + "name": "F_P6_G", + "rotation": [ + -2.7755575615628914e-17, + -2.74964495616814e-10, + 7.940170894241536e-23, + 1 + ], + "translation": [ + 0.005422236397862434, + -9.326162338256836, + -0.08217942714691162 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 8, + "name": "F_P6_G_leg_0" + }, + { + "children": [ + 34 + ], + "name": "F_P7", + "rotation": [ + 0.5891698598861694, + -5.057324785588213e-23, + 2.0496480651152692e-23, + 0.8080092072486877 + ], + "translation": [ + 4.336808689942018e-19, + 0.00003388613185961731, + 0.000014036095308256336 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 9, + "name": "F_P7_leg_0" + }, + { + "children": [ + 36, + 37 + ], + "name": "Drone_Gen_R", + "rotation": [ + 0.1889660805463791, + -0.6813896298408508, + 0.1889660656452179, + 0.6813896298408508 + ], + "translation": [ + -26.78019905090332, + -31.10906982421875, + -0.748199999332428 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 10, + "name": "0" + }, + { + "children": [ + 38, + 39 + ], + "name": "Drone_Panel_R", + "rotation": [ + -2.0816678402999235e-17, + 2.62250594573743e-8, + -1.249000902703301e-16, + 1 + ], + "translation": [ + 0.6785002946853638, + -0.37506112456321716, + 4.957866191864014 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 11, + "name": "Drone_Panel_R_body_0" + }, + { + "children": [ + 40, + 41 + ], + "name": "Drone_leg_R", + "rotation": [ + -0.41061899065971375, + 0.575666606426239, + -0.41061899065971375, + 0.575666606426239 + ], + "translation": [ + -12.886950492858887, + -9.610799789428711, + -0.729290246963501 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 12, + "name": "Drone_leg_R_leg_0" + }, + { + "children": [ + 42, + 43 + ], + "name": "R_P1_G", + "rotation": [ + -2.6943811002715914e-16, + 0.7933533787727356, + 4.189390215534042e-17, + 0.6087614297866821 + ], + "translation": [ + 2.4154791831970215, + -4.842290878295898, + -0.006652138661593199 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 13, + "name": "R_P1_G_leg_0" + }, + { + "children": [ + 44, + 45 + ], + "name": "R_P2", + "rotation": [ + 0.6187881827354431, + 7.332807899516821e-17, + 4.8236011717070243e-17, + 0.7855579257011414 + ], + "translation": [ + 0, + 0, + 0 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 14, + "name": "R_P2_leg_0" + }, + { + "children": [ + 46, + 47 + ], + "name": "R_P3_G", + "rotation": [ + 3.5041414214731503e-16, + 2.7755575615628914e-17, + 1.3877787807814457e-16, + 1 + ], + "translation": [ + 0.002689761109650135, + -22.916383743286133, + 0.02693275175988674 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 15, + "name": "R_P3_G_leg_0" + }, + { + "children": [ + 48, + 49 + ], + "name": "R_P4", + "rotation": [ + 4.0562085120848394e-17, + -0.6030816435813904, + 0.7976794838905334, + 1.0742985044475295e-16 + ], + "translation": [ + 0.004904936067759991, + 0.0029711532406508923, + -0.0014322279021143913 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 16, + "name": "R_P4_leg_0" + }, + { + "children": [ + 50, + 51 + ], + "name": "R_P5_M", + "rotation": [ + 0.0000031865668006503256, + -1.3877721633365453e-17, + 2.0816726379474763e-17, + 1 + ], + "translation": [ + 0.010783359408378601, + -11.28590202331543, + 0.08223113417625427 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 17, + "name": "R_P5_M_leg_0" + }, + { + "children": [ + 52, + 53 + ], + "name": "R_P6_G", + "rotation": [ + -7.45931094670027e-17, + 0, + 0, + 1 + ], + "translation": [ + -0.005793713964521885, + -9.326017379760742, + -0.08199705928564072 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 18, + "name": "R_P6_G_leg_0" + }, + { + "children": [ + 54 + ], + "name": "R_P7", + "rotation": [ + 0.6483271718025208, + 3.472374165980341e-9, + -4.330498626359258e-8, + 0.7613618969917297 + ], + "translation": [ + -1.1546319456101628e-13, + -0.000002192010470025707, + -9.079604410544562e-7 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 19, + "name": "R_P7_leg_0" + }, + { + "children": [ + 56, + 57 + ], + "name": "Drone_Gen_L", + "rotation": [ + 0.1889660805463791, + 0.6813896298408508, + -0.1889660656452179, + 0.6813896298408508 + ], + "translation": [ + 26.78019905090332, + -31.10906982421875, + -0.748199999332428 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 20, + "name": "Drone_Gen_L_body_0" + }, + { + "children": [ + 58, + 59 + ], + "name": "Drone_Panel_L", + "rotation": [ + 0.0002122838923241943, + 2.747205307684908e-8, + -5.831957746588179e-12, + 1 + ], + "translation": [ + -0.6784976124763489, + -0.37716609239578247, + 4.957706451416016 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 21, + "name": "Drone_Panel_L_body_0" + }, + { + "children": [ + 60, + 61 + ], + "name": "Drone_leg_L", + "rotation": [ + -0.41061896085739136, + -0.575666606426239, + 0.41061902046203613, + 0.575666606426239 + ], + "translation": [ + 12.88695240020752, + -9.610796928405762, + -0.7292901277542114 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 22, + "name": "Drone_leg_L_leg_0" + }, + { + "children": [ + 62, + 63 + ], + "name": "L_P1_G", + "rotation": [ + 3.7478338591893966e-16, + -0.7613461017608643, + 5.642599445989575e-16, + 0.6483456492424011 + ], + "translation": [ + -2.415479898452759, + -4.84229040145874, + -0.006640426814556122 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 23, + "name": "L_P1_G_leg_0" + }, + { + "children": [ + 64, + 65 + ], + "name": "L_P2", + "rotation": [ + 0.5984764695167542, + -4.839795300149281e-18, + -1.0031977647616155e-16, + 0.8011403679847717 + ], + "translation": [ + 0, + 0, + 0 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 24, + "name": "L_P2_leg_0" + }, + { + "children": [ + 66, + 67 + ], + "name": "L_P3_G", + "rotation": [ + -1.708702623837155e-16, + 1.3877787807814457e-17, + -8.326672684688674e-17, + 1 + ], + "translation": [ + 0.0002644560590852052, + -22.915939331054688, + 0.026753252372145653 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 25, + "name": "L_P3_G_leg_0" + }, + { + "children": [ + 68, + 69 + ], + "name": "L_P4", + "rotation": [ + 3.0627252226114957e-17, + -0.6686968803405762, + 0.7435351014137268, + -5.480842821366953e-17 + ], + "translation": [ + -0.004460121039301157, + 0.000007818744961696211, + 0.00005186478665564209 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 26, + "name": "L_P4_leg_0" + }, + { + "children": [ + 70, + 71 + ], + "name": "L_P5_M", + "rotation": [ + 0.00001270258508156985, + 0.000003925847977370722, + -0.010920973494648933, + 0.9999404549598694 + ], + "translation": [ + 0.05688011273741722, + -11.236658096313477, + 0.08181961625814438 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 27, + "name": "L_P5_M_leg_0" + }, + { + "children": [ + 72, + 73 + ], + "name": "L_P6_G", + "rotation": [ + -7.632783294297951e-17, + -7.632783294297951e-17, + 5.551115123125783e-17, + 1 + ], + "translation": [ + 0.14344365894794464, + -9.371054649353027, + -0.08199699968099594 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 28, + "name": "L_P6_G_leg_0" + }, + { + "children": [ + 74 + ], + "name": "L_P7", + "rotation": [ + 0.7071068286895752, + 5.3971933547419153e-17, + 4.906539263005266e-18, + 0.7071068286895752 + ], + "translation": [ + 0.01226816326379776, + 0.00024394220963586122, + 0.0002562310837674886 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 29, + "name": "L_P7_leg_0" + }, + { + "children": [ + 76 + ], + "name": "Drone_UPanel_R", + "rotation": [ + 0.09390989691019058, + -0.7008430361747742, + 0.09390989691019058, + 0.700843095779419 + ], + "translation": [ + -28.345199584960938, + -10.20475959777832, + 0.14466851949691772 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 30, + "name": "Drone_UPanel_R_body_0" + }, + { + "children": [ + 78 + ], + "name": "Drone_UPanel_L", + "rotation": [ + 0.09390989691019058, + 0.7008430361747742, + -0.09390989691019058, + 0.700843095779419 + ], + "translation": [ + 28.230527877807617, + -11.228233337402344, + -0.07694999873638153 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 31, + "name": "Drone_UPanel_L_body_0" + }, + { + "children": [ + 80 + ], + "name": "Drone_UPart", + "translation": [ + 0, + 38.9765510559082, + -3.073050022125244 + ], + "rotation": [ + 0, + 0, + 0, + 1 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 32, + "name": "Drone_UPart_body_0" + }, + { + "children": [ + 82, + 83 + ], + "name": "Drone_ILens", + "translation": [ + 0, + -14.965813636779785, + 41.50516891479492 + ], + "rotation": [ + 0, + 0, + 0, + 1 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 33, + "name": "Drone_ILens_body_0" + }, + { + "children": [ + 84 + ], + "name": "Drone_IEye", + "rotation": [ + 0.0007836852455511689, + 0.9981203675270081, + -0.013049819506704807, + 0.0598752461373806 + ], + "translation": [ + 0, + -0.018226023763418198, + -8.908537864685059 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 34, + "name": "1" + }, + { + "children": [ + 86, + 87 + ], + "name": "Drone_Turb_M_L", + "rotation": [ + 0.5779998302459717, + -0.4667881727218628, + -0.5311384797096252, + -0.40732908248901367 + ], + "translation": [ + 19.973400115966797, + 24.55685043334961, + -6.007046699523926 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 35, + "name": "Drone_Turb_M_L_body_0" + }, + { + "children": [ + 88 + ], + "name": "Drone_Turb_Blade_L", + "rotation": [ + 0.40862560272216797, + 0.5128536224365234, + 0.6914904713630676, + 0.30306294560432434 + ], + "translation": [ + -0.000021889296476729214, + -36.155147552490234, + -10.966755867004395 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 36, + "name": "Drone_Turb_Blade_L_body_0" + }, + { + "children": [ + 90, + 91 + ], + "name": "Drone_Turb_M_R", + "rotation": [ + 0.5797019600868225, + 0.46958616375923157, + 0.5286666750907898, + -0.40490248799324036 + ], + "translation": [ + -19.973400115966797, + 24.556848526000977, + -6.007049560546875 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 37, + "name": "Drone_Turb_M_R_body_0" + }, + { + "children": [ + 92 + ], + "name": "Drone_Turb_Blade_R", + "rotation": [ + -0.4086257517337799, + 0.512853741645813, + 0.6914904713630676, + -0.30306291580200195 + ], + "translation": [ + 0.000051651491958182305, + -36.1551513671875, + -10.966744422912598 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 38, + "name": "Drone_Turb_Blade_R_body_0" + } + ], + "samplers": [ + { + "magFilter": 9729, + "minFilter": 9987, + "wrapS": 10497, + "wrapT": 10497 + } + ], + "scene": 0, + "scenes": [ + { + "name": "OSG_Scene", + "nodes": [ + 0 + ] + } + ], + "textures": [ + { + "sampler": 0, + "source": 0 + }, + { + "sampler": 0, + "source": 1 + }, + { + "sampler": 0, + "source": 2 + }, + { + "sampler": 0, + "source": 3 + }, + { + "sampler": 0, + "source": 4 + }, + { + "sampler": 0, + "source": 5 + }, + { + "sampler": 0, + "source": 6 + }, + { + "sampler": 0, + "source": 7 + }, + { + "sampler": 0, + "source": 8 + }, + { + "sampler": 0, + "source": 9 + } + ], + "extensionsRequired": [ + "KHR_draco_mesh_compression" + ], + "extensionsUsed": [ + "KHR_draco_mesh_compression" + ] +} diff --git a/example/assets/BusterDrone/textures/Boden_baseColor.jpg b/example/assets/BusterDrone/textures/Boden_baseColor.jpg new file mode 100644 index 00000000..a0b817b7 Binary files /dev/null and b/example/assets/BusterDrone/textures/Boden_baseColor.jpg differ diff --git a/example/assets/BusterDrone/textures/Boden_metallicRoughness.jpg b/example/assets/BusterDrone/textures/Boden_metallicRoughness.jpg new file mode 100644 index 00000000..16af5ced Binary files /dev/null and b/example/assets/BusterDrone/textures/Boden_metallicRoughness.jpg differ diff --git a/example/assets/BusterDrone/textures/Boden_normal.jpg b/example/assets/BusterDrone/textures/Boden_normal.jpg new file mode 100644 index 00000000..16ac42fb Binary files /dev/null and b/example/assets/BusterDrone/textures/Boden_normal.jpg differ diff --git a/example/assets/BusterDrone/textures/body_baseColor.jpg b/example/assets/BusterDrone/textures/body_baseColor.jpg new file mode 100644 index 00000000..338fa643 Binary files /dev/null and b/example/assets/BusterDrone/textures/body_baseColor.jpg differ diff --git a/example/assets/BusterDrone/textures/body_emissive.jpg b/example/assets/BusterDrone/textures/body_emissive.jpg new file mode 100644 index 00000000..db3e51b9 Binary files /dev/null and b/example/assets/BusterDrone/textures/body_emissive.jpg differ diff --git a/example/assets/BusterDrone/textures/body_metallicRoughness.jpg b/example/assets/BusterDrone/textures/body_metallicRoughness.jpg new file mode 100644 index 00000000..93b51013 Binary files /dev/null and b/example/assets/BusterDrone/textures/body_metallicRoughness.jpg differ diff --git a/example/assets/BusterDrone/textures/body_normal.jpg b/example/assets/BusterDrone/textures/body_normal.jpg new file mode 100644 index 00000000..28e0583b Binary files /dev/null and b/example/assets/BusterDrone/textures/body_normal.jpg differ diff --git a/example/assets/BusterDrone/textures/material_baseColor.jpg b/example/assets/BusterDrone/textures/material_baseColor.jpg new file mode 100644 index 00000000..154cb5b1 Binary files /dev/null and b/example/assets/BusterDrone/textures/material_baseColor.jpg differ diff --git a/example/assets/BusterDrone/textures/material_metallicRoughness.jpg b/example/assets/BusterDrone/textures/material_metallicRoughness.jpg new file mode 100644 index 00000000..5f539354 Binary files /dev/null and b/example/assets/BusterDrone/textures/material_metallicRoughness.jpg differ diff --git a/example/assets/BusterDrone/textures/material_normal.jpg b/example/assets/BusterDrone/textures/material_normal.jpg new file mode 100644 index 00000000..a1f52c14 Binary files /dev/null and b/example/assets/BusterDrone/textures/material_normal.jpg differ diff --git a/example/assets/FlightHelmet/FlightHelmet.bin b/example/assets/FlightHelmet/FlightHelmet.bin new file mode 100644 index 00000000..a8b48147 Binary files /dev/null and b/example/assets/FlightHelmet/FlightHelmet.bin differ diff --git a/example/assets/FlightHelmet/FlightHelmet.gltf b/example/assets/FlightHelmet/FlightHelmet.gltf new file mode 100644 index 00000000..b73b85b3 --- /dev/null +++ b/example/assets/FlightHelmet/FlightHelmet.gltf @@ -0,0 +1,755 @@ +{ + "accessors": [ + { + "componentType": 5123, + "count": 24408, + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 8468, + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 8468, + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 8468, + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 8468, + "type": "VEC3", + "max": [ + 0.131662, + 0.137638986, + 0.10078799 + ], + "min": [ + -0.131333, + -0.028128, + -0.137763992 + ] + }, + { + "componentType": 5123, + "count": 65688, + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 12552, + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 12552, + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 12552, + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 12552, + "type": "VEC3", + "max": [ + 0.11722149, + 0.196387976, + 0.132422984 + ], + "min": [ + -0.11722149, + -0.196387976, + -0.132422984 + ] + }, + { + "componentType": 5123, + "count": 2208, + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 436, + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 436, + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 436, + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 436, + "type": "VEC3", + "max": [ + 0.09527509, + 0.114654, + -0.08429489 + ], + "min": [ + -0.0952748954, + 0.0551489964, + -0.14295499 + ] + }, + { + "componentType": 5123, + "count": 60288, + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 17186, + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 17186, + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 17186, + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 17186, + "type": "VEC3", + "max": [ + 0.1572095, + 0.2716865, + 0.162181988 + ], + "min": [ + -0.1572095, + -0.2716865, + -0.162181988 + ] + }, + { + "componentType": 5123, + "count": 131574, + "type": "SCALAR" + }, + { + "componentType": 5126, + "count": 24148, + "type": "VEC2" + }, + { + "componentType": 5126, + "count": 24148, + "type": "VEC3" + }, + { + "componentType": 5126, + "count": 24148, + "type": "VEC4" + }, + { + "componentType": 5126, + "count": 24148, + "type": "VEC3", + "max": [ + 0.1504075, + 0.328366965, + 0.173673 + ], + "min": [ + -0.1504075, + -0.328366965, + -0.173673 + ] + } + ], + "asset": { + "generator": "glTF Tools for Unity", + "version": "2.0" + }, + "bufferViews": [ + { + "buffer": 0, + "byteOffset": 0, + "byteLength": 59806 + }, + { + "buffer": 0, + "byteOffset": 59808, + "byteLength": 99674 + }, + { + "buffer": 0, + "byteOffset": 159484, + "byteLength": 4875 + }, + { + "buffer": 0, + "byteOffset": 164360, + "byteLength": 133545 + }, + { + "buffer": 0, + "byteOffset": 297908, + "byteLength": 203914 + } + ], + "buffers": [ + { + "name": "FlightHelmet", + "byteLength": 501824, + "uri": "FlightHelmet.bin" + } + ], + "images": [ + { + "name": "FlightHelmet_baseColor", + "uri": "FlightHelmet_baseColor.png" + }, + { + "name": "FlightHelmet_occlusionRoughnessMetallic", + "uri": "FlightHelmet_occlusionRoughnessMetallic.png" + }, + { + "name": "FlightHelmet_normal", + "uri": "FlightHelmet_normal.png" + }, + { + "name": "FlightHelmet_baseColor1", + "uri": "FlightHelmet_baseColor1.png" + }, + { + "name": "FlightHelmet_occlusionRoughnessMetallic1", + "uri": "FlightHelmet_occlusionRoughnessMetallic1.png" + }, + { + "name": "FlightHelmet_normal1", + "uri": "FlightHelmet_normal1.png" + }, + { + "name": "FlightHelmet_baseColor2", + "uri": "FlightHelmet_baseColor2.png" + }, + { + "name": "FlightHelmet_occlusionRoughnessMetallic2", + "uri": "FlightHelmet_occlusionRoughnessMetallic2.png" + }, + { + "name": "FlightHelmet_normal2", + "uri": "FlightHelmet_normal2.png" + }, + { + "name": "FlightHelmet_baseColor3", + "uri": "FlightHelmet_baseColor3.png" + }, + { + "name": "FlightHelmet_occlusionRoughnessMetallic3", + "uri": "FlightHelmet_occlusionRoughnessMetallic3.png" + }, + { + "name": "FlightHelmet_normal3", + "uri": "FlightHelmet_normal3.png" + }, + { + "name": "FlightHelmet_baseColor4", + "uri": "FlightHelmet_baseColor4.png" + }, + { + "name": "FlightHelmet_occlusionRoughnessMetallic4", + "uri": "FlightHelmet_occlusionRoughnessMetallic4.png" + }, + { + "name": "FlightHelmet_normal4", + "uri": "FlightHelmet_normal4.png" + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "TEXCOORD_0": 1, + "NORMAL": 2, + "TANGENT": 3, + "POSITION": 4 + }, + "indices": 0, + "material": 0, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 0, + "attributes": { + "TEXCOORD_0": 0, + "NORMAL": 1, + "TANGENT": 2, + "POSITION": 3 + } + } + } + } + ], + "name": "GlassPlastic_low" + }, + { + "primitives": [ + { + "attributes": { + "TEXCOORD_0": 6, + "NORMAL": 7, + "TANGENT": 8, + "POSITION": 9 + }, + "indices": 5, + "material": 1, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 1, + "attributes": { + "TEXCOORD_0": 0, + "NORMAL": 1, + "TANGENT": 2, + "POSITION": 3 + } + } + } + } + ], + "name": "LeatherParts_low" + }, + { + "primitives": [ + { + "attributes": { + "TEXCOORD_0": 11, + "NORMAL": 12, + "TANGENT": 13, + "POSITION": 14 + }, + "indices": 10, + "material": 2, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 2, + "attributes": { + "TEXCOORD_0": 0, + "NORMAL": 1, + "TANGENT": 2, + "POSITION": 3 + } + } + } + } + ], + "name": "Lenses_low" + }, + { + "primitives": [ + { + "attributes": { + "TEXCOORD_0": 16, + "NORMAL": 17, + "TANGENT": 18, + "POSITION": 19 + }, + "indices": 15, + "material": 3, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 3, + "attributes": { + "TEXCOORD_0": 0, + "NORMAL": 1, + "TANGENT": 2, + "POSITION": 3 + } + } + } + } + ], + "name": "MetalParts_low" + }, + { + "primitives": [ + { + "attributes": { + "TEXCOORD_0": 21, + "NORMAL": 22, + "TANGENT": 23, + "POSITION": 24 + }, + "indices": 20, + "material": 4, + "mode": 4, + "extensions": { + "KHR_draco_mesh_compression": { + "bufferView": 4, + "attributes": { + "TEXCOORD_0": 0, + "NORMAL": 1, + "TANGENT": 2, + "POSITION": 3 + } + } + } + } + ], + "name": "RubberWood_low" + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 0, + "texCoord": 0 + }, + "metallicRoughnessTexture": { + "index": 1, + "texCoord": 0 + }, + "baseColorFactor": [ + 1, + 1, + 1, + 1 + ], + "metallicFactor": 1, + "roughnessFactor": 1 + }, + "normalTexture": { + "index": 2, + "texCoord": 0 + }, + "occlusionTexture": { + "index": 1, + "texCoord": 0 + }, + "name": "GlassPlasticMat", + "emissiveFactor": [ + 0, + 0, + 0 + ], + "alphaMode": "OPAQUE", + "doubleSided": false + }, + { + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 3, + "texCoord": 0 + }, + "metallicRoughnessTexture": { + "index": 4, + "texCoord": 0 + }, + "baseColorFactor": [ + 1, + 1, + 1, + 1 + ], + "metallicFactor": 1, + "roughnessFactor": 1 + }, + "normalTexture": { + "index": 5, + "texCoord": 0 + }, + "occlusionTexture": { + "index": 4, + "texCoord": 0 + }, + "name": "LeatherPartsMat", + "emissiveFactor": [ + 0, + 0, + 0 + ], + "alphaMode": "OPAQUE", + "doubleSided": false + }, + { + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 6, + "texCoord": 0 + }, + "metallicRoughnessTexture": { + "index": 7, + "texCoord": 0 + }, + "baseColorFactor": [ + 1, + 1, + 1, + 1 + ], + "metallicFactor": 1, + "roughnessFactor": 1 + }, + "normalTexture": { + "index": 8, + "texCoord": 0 + }, + "occlusionTexture": { + "index": 7, + "texCoord": 0 + }, + "alphaMode": "BLEND", + "name": "LensesMat", + "emissiveFactor": [ + 0, + 0, + 0 + ], + "doubleSided": false + }, + { + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 9, + "texCoord": 0 + }, + "metallicRoughnessTexture": { + "index": 10, + "texCoord": 0 + }, + "baseColorFactor": [ + 1, + 1, + 1, + 1 + ], + "metallicFactor": 1, + "roughnessFactor": 1 + }, + "normalTexture": { + "index": 11, + "texCoord": 0 + }, + "occlusionTexture": { + "index": 10, + "texCoord": 0 + }, + "name": "MetalPartsMat", + "emissiveFactor": [ + 0, + 0, + 0 + ], + "alphaMode": "OPAQUE", + "doubleSided": false + }, + { + "doubleSided": true, + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 12, + "texCoord": 0 + }, + "metallicRoughnessTexture": { + "index": 13, + "texCoord": 0 + }, + "baseColorFactor": [ + 1, + 1, + 1, + 1 + ], + "metallicFactor": 1, + "roughnessFactor": 1 + }, + "normalTexture": { + "index": 14, + "texCoord": 0 + }, + "occlusionTexture": { + "index": 13, + "texCoord": 0 + }, + "name": "RubberWoodMat", + "emissiveFactor": [ + 0, + 0, + 0 + ], + "alphaMode": "OPAQUE" + } + ], + "nodes": [ + { + "mesh": 0, + "name": "GlassPlastic_low" + }, + { + "mesh": 1, + "translation": [ + 0.000434499962, + 0.032592997, + 0.011676996 + ], + "name": "LeatherParts_low", + "rotation": [ + 0, + 0, + 0, + 1 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 2, + "name": "Lenses_low" + }, + { + "mesh": 3, + "translation": [ + 0.0331545, + -0.1488645, + -0.0242879968 + ], + "name": "MetalParts_low", + "rotation": [ + 0, + 0, + 0, + 1 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "mesh": 4, + "translation": [ + -0.00190849893, + -0.111985, + -0.013313001 + ], + "name": "RubberWood_low", + "rotation": [ + 0, + 0, + 0, + 1 + ], + "scale": [ + 1, + 1, + 1 + ] + }, + { + "children": [ + 0, + 1, + 2, + 3, + 4 + ], + "rotation": [ + 0, + 1, + 0, + 0 + ], + "name": "FlightHelmet", + "translation": [ + 0, + 0, + 0 + ], + "scale": [ + 1, + 1, + 1 + ] + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 5 + ] + } + ], + "textures": [ + { + "source": 0 + }, + { + "source": 1 + }, + { + "source": 2 + }, + { + "source": 3 + }, + { + "source": 4 + }, + { + "source": 5 + }, + { + "source": 6 + }, + { + "source": 7 + }, + { + "source": 8 + }, + { + "source": 9 + }, + { + "source": 10 + }, + { + "source": 11 + }, + { + "source": 12 + }, + { + "source": 13 + }, + { + "source": 14 + } + ], + "extensionsRequired": [ + "KHR_draco_mesh_compression" + ], + "extensionsUsed": [ + "KHR_draco_mesh_compression" + ] +} diff --git a/example/assets/FlightHelmet/FlightHelmet_baseColor.png b/example/assets/FlightHelmet/FlightHelmet_baseColor.png new file mode 100644 index 00000000..4116e198 Binary files /dev/null and b/example/assets/FlightHelmet/FlightHelmet_baseColor.png differ diff --git a/example/assets/FlightHelmet/FlightHelmet_baseColor1.png b/example/assets/FlightHelmet/FlightHelmet_baseColor1.png new file mode 100644 index 00000000..d47b33eb Binary files /dev/null and b/example/assets/FlightHelmet/FlightHelmet_baseColor1.png differ diff --git a/example/assets/FlightHelmet/FlightHelmet_baseColor2.png b/example/assets/FlightHelmet/FlightHelmet_baseColor2.png new file mode 100644 index 00000000..839c5019 Binary files /dev/null and b/example/assets/FlightHelmet/FlightHelmet_baseColor2.png differ diff --git a/example/assets/FlightHelmet/FlightHelmet_baseColor3.png b/example/assets/FlightHelmet/FlightHelmet_baseColor3.png new file mode 100644 index 00000000..6095f68d Binary files /dev/null and b/example/assets/FlightHelmet/FlightHelmet_baseColor3.png differ diff --git a/example/assets/FlightHelmet/FlightHelmet_baseColor4.png b/example/assets/FlightHelmet/FlightHelmet_baseColor4.png new file mode 100644 index 00000000..fddf46d1 Binary files /dev/null and b/example/assets/FlightHelmet/FlightHelmet_baseColor4.png differ diff --git a/example/assets/FlightHelmet/FlightHelmet_normal.png b/example/assets/FlightHelmet/FlightHelmet_normal.png new file mode 100644 index 00000000..058b4e9d Binary files /dev/null and b/example/assets/FlightHelmet/FlightHelmet_normal.png differ diff --git a/example/assets/FlightHelmet/FlightHelmet_normal1.png b/example/assets/FlightHelmet/FlightHelmet_normal1.png new file mode 100644 index 00000000..a00be5c1 Binary files /dev/null and b/example/assets/FlightHelmet/FlightHelmet_normal1.png differ diff --git a/example/assets/FlightHelmet/FlightHelmet_normal2.png b/example/assets/FlightHelmet/FlightHelmet_normal2.png new file mode 100644 index 00000000..a9ec5618 Binary files /dev/null and b/example/assets/FlightHelmet/FlightHelmet_normal2.png differ diff --git a/example/assets/FlightHelmet/FlightHelmet_normal3.png b/example/assets/FlightHelmet/FlightHelmet_normal3.png new file mode 100644 index 00000000..70f9022b Binary files /dev/null and b/example/assets/FlightHelmet/FlightHelmet_normal3.png differ diff --git a/example/assets/FlightHelmet/FlightHelmet_normal4.png b/example/assets/FlightHelmet/FlightHelmet_normal4.png new file mode 100644 index 00000000..3c973901 Binary files /dev/null and b/example/assets/FlightHelmet/FlightHelmet_normal4.png differ diff --git a/example/assets/FlightHelmet/FlightHelmet_occlusionRoughnessMetallic.png b/example/assets/FlightHelmet/FlightHelmet_occlusionRoughnessMetallic.png new file mode 100644 index 00000000..bbde038e Binary files /dev/null and b/example/assets/FlightHelmet/FlightHelmet_occlusionRoughnessMetallic.png differ diff --git a/example/assets/FlightHelmet/FlightHelmet_occlusionRoughnessMetallic1.png b/example/assets/FlightHelmet/FlightHelmet_occlusionRoughnessMetallic1.png new file mode 100644 index 00000000..067b4f81 Binary files /dev/null and b/example/assets/FlightHelmet/FlightHelmet_occlusionRoughnessMetallic1.png differ diff --git a/example/assets/FlightHelmet/FlightHelmet_occlusionRoughnessMetallic2.png b/example/assets/FlightHelmet/FlightHelmet_occlusionRoughnessMetallic2.png new file mode 100644 index 00000000..2466f5bc Binary files /dev/null and b/example/assets/FlightHelmet/FlightHelmet_occlusionRoughnessMetallic2.png differ diff --git a/example/assets/FlightHelmet/FlightHelmet_occlusionRoughnessMetallic3.png b/example/assets/FlightHelmet/FlightHelmet_occlusionRoughnessMetallic3.png new file mode 100644 index 00000000..dbe24290 Binary files /dev/null and b/example/assets/FlightHelmet/FlightHelmet_occlusionRoughnessMetallic3.png differ diff --git a/example/assets/FlightHelmet/FlightHelmet_occlusionRoughnessMetallic4.png b/example/assets/FlightHelmet/FlightHelmet_occlusionRoughnessMetallic4.png new file mode 100644 index 00000000..2cfc659e Binary files /dev/null and b/example/assets/FlightHelmet/FlightHelmet_occlusionRoughnessMetallic4.png differ diff --git a/example/assets/FlightHelmet/README.md b/example/assets/FlightHelmet/README.md new file mode 100644 index 00000000..2232005a --- /dev/null +++ b/example/assets/FlightHelmet/README.md @@ -0,0 +1,16 @@ +# Flight Helmet + +## Screenshot + +![screenshot](screenshot/screenshot.jpg) + +## License Information + +Donated by Microsoft for glTF testing + +[![CC0](http://i.creativecommons.org/p/zero/1.0/88x31.png)](http://creativecommons.org/publicdomain/zero/1.0/) +To the extent possible under law, Microsoft has waived all copyright and related or neighboring rights to this asset. + +Draco compression was done via Cesium tools on 27-03-2020 as follows. + + gltf-pipeline -i FlightHelmet.gltf -o FlightHelmet.gltf -d -s --keep-unused-elements diff --git a/example/assets/README b/example/assets/README new file mode 100644 index 00000000..183d8b95 --- /dev/null +++ b/example/assets/README @@ -0,0 +1,9 @@ +Model by LaVADraGoN at Sketchfab. + +From an joint University Project "Dune Express" Concept-Model was created by my Partner Evil Cloud. + +License: CC Attribution-NonCommercial + +Draco compression was done via Cesium tools on 27-03-2020 as follows. + + gltf-pipeline -i scene.gltf -o scene.gltf -d -s --keep-unused-elements diff --git a/example/assets/default_env/default_env_ibl.ktx b/example/assets/default_env/default_env_ibl.ktx new file mode 100644 index 00000000..6194dc3f Binary files /dev/null and b/example/assets/default_env/default_env_ibl.ktx differ diff --git a/example/assets/default_env/default_env_skybox.ktx b/example/assets/default_env/default_env_skybox.ktx new file mode 100644 index 00000000..e8de441a Binary files /dev/null and b/example/assets/default_env/default_env_skybox.ktx differ diff --git a/example/assets/default_env/default_env_skybox_tiny.ktx b/example/assets/default_env/default_env_skybox_tiny.ktx new file mode 100644 index 00000000..c34057b2 Binary files /dev/null and b/example/assets/default_env/default_env_skybox_tiny.ktx differ diff --git a/example/assets/monkey.obj b/example/assets/monkey.obj new file mode 100644 index 00000000..604bb2fb --- /dev/null +++ b/example/assets/monkey.obj @@ -0,0 +1,41572 @@ +# Blender v2.74 (sub 0) OBJ File: '' +# www.blender.org +o Suzanne +v 0.490583 0.186287 0.719791 +v 0.475731 0.192902 0.737549 +v 0.476952 0.160851 0.722916 +v 0.507355 0.178955 0.699112 +v 0.499054 0.213936 0.717773 +v 0.483154 0.217377 0.735779 +v 0.463715 0.170441 0.740295 +v 0.491943 0.150208 0.702637 +v 0.516968 0.210144 0.696838 +v -0.490583 0.186287 0.719791 +v -0.476952 0.160851 0.722916 +v -0.475731 0.192902 0.737549 +v -0.499054 0.213936 0.717773 +v -0.507355 0.178955 0.699112 +v -0.491943 0.150208 0.702637 +v -0.463715 0.170441 0.740295 +v -0.483154 0.217377 0.735779 +v -0.516968 0.210144 0.696838 +v 0.554451 0.160034 0.625362 +v 0.539948 0.165527 0.651382 +v 0.534363 0.122665 0.629669 +v 0.568237 0.155029 0.598480 +v 0.566986 0.200577 0.622635 +v 0.551636 0.203308 0.648743 +v 0.521240 0.130676 0.655518 +v 0.546875 0.115356 0.602905 +v 0.581543 0.198120 0.595703 +v -0.554451 0.160034 0.625362 +v -0.534363 0.122665 0.629669 +v -0.539948 0.165527 0.651382 +v -0.566986 0.200577 0.622635 +v -0.568237 0.155029 0.598480 +v -0.546875 0.115356 0.602905 +v -0.521240 0.130676 0.655518 +v -0.551636 0.203308 0.648743 +v -0.581543 0.198120 0.595703 +v 0.435793 0.043385 0.650026 +v 0.429695 0.056686 0.674721 +v 0.394478 0.030914 0.658119 +v 0.441650 0.031281 0.624237 +v 0.473938 0.063278 0.642303 +v 0.465088 0.075256 0.667480 +v 0.391388 0.045044 0.682281 +v 0.397461 0.018066 0.632812 +v 0.482422 0.052368 0.616089 +v -0.435793 0.043385 0.650026 +v -0.394478 0.030914 0.658119 +v -0.429695 0.056686 0.674721 +v -0.473938 0.063278 0.642303 +v -0.441650 0.031281 0.624237 +v -0.397461 0.018066 0.632812 +v -0.391388 0.045044 0.682281 +v -0.465088 0.075256 0.667480 +v -0.482422 0.052368 0.616089 +v 0.409536 0.107073 0.737062 +v 0.403610 0.123070 0.752808 +v 0.381317 0.098648 0.742607 +v 0.416298 0.089279 0.718636 +v 0.435661 0.120537 0.731766 +v 0.426971 0.134918 0.748108 +v 0.378387 0.115662 0.757751 +v 0.384674 0.079712 0.724884 +v 0.445557 0.104553 0.712646 +v -0.409536 0.107073 0.737062 +v -0.381317 0.098648 0.742607 +v -0.403610 0.123070 0.752808 +v -0.435661 0.120537 0.731766 +v -0.416298 0.089279 0.718636 +v -0.384674 0.079712 0.724884 +v -0.378387 0.115662 0.757751 +v -0.426971 0.134918 0.748108 +v -0.445557 0.104553 0.712646 +v 0.295601 0.107073 0.758238 +v 0.302277 0.123070 0.771889 +v 0.270142 0.120537 0.762299 +v 0.288086 0.089279 0.742264 +v 0.323273 0.098648 0.753441 +v 0.326752 0.115662 0.767517 +v 0.279816 0.134918 0.775604 +v 0.259247 0.104553 0.746735 +v 0.319366 0.079712 0.736969 +v -0.295601 0.107073 0.758238 +v -0.270142 0.120537 0.762299 +v -0.302277 0.123070 0.771889 +v -0.323273 0.098648 0.753441 +v -0.288086 0.089279 0.742264 +v -0.259247 0.104553 0.746735 +v -0.279816 0.134918 0.775604 +v -0.326752 0.115662 0.767517 +v -0.319366 0.079712 0.736969 +v 0.267395 0.043385 0.680546 +v 0.273682 0.056686 0.703140 +v 0.229271 0.063278 0.686409 +v 0.261475 0.031281 0.656738 +v 0.308693 0.030914 0.673714 +v 0.311920 0.045044 0.696808 +v 0.238373 0.075256 0.708527 +v 0.220703 0.052368 0.663086 +v 0.305664 0.018066 0.649414 +v -0.267395 0.043385 0.680546 +v -0.229271 0.063278 0.686409 +v -0.273682 0.056686 0.703140 +v -0.308693 0.030914 0.673714 +v -0.261475 0.031281 0.656738 +v -0.220703 0.052368 0.663086 +v -0.238373 0.075256 0.708527 +v -0.311920 0.045044 0.696808 +v -0.305664 0.018066 0.649414 +v 0.148794 0.160034 0.696226 +v 0.163658 0.165527 0.717094 +v 0.136261 0.200577 0.697220 +v 0.134888 0.155029 0.674255 +v 0.168877 0.122665 0.694252 +v 0.182343 0.130676 0.715485 +v 0.151978 0.203308 0.717834 +v 0.121582 0.198120 0.675537 +v 0.156250 0.115356 0.671875 +v -0.148794 0.160034 0.696226 +v -0.136261 0.200577 0.697220 +v -0.163658 0.165527 0.717094 +v -0.168877 0.122665 0.694252 +v -0.134888 0.155029 0.674255 +v -0.121582 0.198120 0.675537 +v -0.151978 0.203308 0.717834 +v -0.182343 0.130676 0.715485 +v -0.156250 0.115356 0.671875 +v 0.216328 0.186287 0.768185 +v 0.232445 0.192902 0.780968 +v 0.207901 0.213936 0.768509 +v 0.198174 0.178955 0.753319 +v 0.229797 0.160851 0.767242 +v 0.244293 0.170441 0.780121 +v 0.225037 0.217377 0.781250 +v 0.188599 0.210144 0.753723 +v 0.213470 0.150208 0.752228 +v -0.216328 0.186287 0.768185 +v -0.207901 0.213936 0.768509 +v -0.232445 0.192902 0.780968 +v -0.229797 0.160851 0.767242 +v -0.198174 0.178955 0.753319 +v -0.188599 0.210144 0.753723 +v -0.225037 0.217377 0.781250 +v -0.244293 0.170441 0.780121 +v -0.213470 0.150208 0.752228 +v 0.216328 0.300165 0.768185 +v 0.232445 0.294243 0.780968 +v 0.229797 0.326294 0.767242 +v 0.198174 0.306931 0.753319 +v 0.207901 0.271950 0.768509 +v 0.225037 0.269012 0.781250 +v 0.244293 0.317627 0.780121 +v 0.213470 0.336182 0.752228 +v 0.188599 0.275330 0.753723 +v -0.216328 0.300165 0.768185 +v -0.229797 0.326294 0.767242 +v -0.232445 0.294243 0.780968 +v -0.207901 0.271950 0.768509 +v -0.198174 0.306931 0.753319 +v -0.213470 0.336182 0.752228 +v -0.244293 0.317627 0.780121 +v -0.225037 0.269012 0.781250 +v -0.188599 0.275330 0.753723 +v 0.148794 0.326481 0.696226 +v 0.163658 0.320358 0.717094 +v 0.168877 0.364563 0.694252 +v 0.134888 0.332367 0.674255 +v 0.136261 0.285355 0.697220 +v 0.151978 0.282166 0.717834 +v 0.182343 0.355713 0.715485 +v 0.156250 0.373047 0.671875 +v 0.121582 0.288452 0.675537 +v -0.148794 0.326481 0.696226 +v -0.168877 0.364563 0.694252 +v -0.163658 0.320358 0.717094 +v -0.136261 0.285355 0.697220 +v -0.134888 0.332367 0.674255 +v -0.156250 0.373047 0.671875 +v -0.182343 0.355713 0.715485 +v -0.151978 0.282166 0.717834 +v -0.121582 0.288452 0.675537 +v 0.267395 0.445076 0.680546 +v 0.273682 0.430573 0.703140 +v 0.308693 0.457611 0.673714 +v 0.261475 0.458862 0.656738 +v 0.229271 0.424988 0.686409 +v 0.238373 0.411865 0.708527 +v 0.311920 0.442261 0.696808 +v 0.305664 0.472168 0.649414 +v 0.220703 0.437500 0.663086 +v -0.267395 0.445076 0.680546 +v -0.308693 0.457611 0.673714 +v -0.273682 0.430573 0.703140 +v -0.229271 0.424988 0.686409 +v -0.261475 0.458862 0.656738 +v -0.305664 0.472168 0.649414 +v -0.311920 0.442261 0.696808 +v -0.238373 0.411865 0.708527 +v -0.220703 0.437500 0.663086 +v 0.295601 0.381268 0.758238 +v 0.302277 0.366592 0.771889 +v 0.323273 0.389755 0.753441 +v 0.288086 0.397980 0.742264 +v 0.270142 0.367615 0.762299 +v 0.279816 0.354492 0.775604 +v 0.326752 0.374084 0.767517 +v 0.319366 0.407593 0.736969 +v 0.259247 0.382568 0.746735 +v -0.295601 0.381268 0.758238 +v -0.323273 0.389755 0.753441 +v -0.302277 0.366592 0.771889 +v -0.270142 0.367615 0.762299 +v -0.288086 0.397980 0.742264 +v -0.319366 0.407593 0.736969 +v -0.326752 0.374084 0.767517 +v -0.279816 0.354492 0.775604 +v -0.259247 0.382568 0.746735 +v 0.409536 0.381268 0.737062 +v 0.403610 0.366592 0.752808 +v 0.435661 0.367615 0.731766 +v 0.416298 0.397980 0.718636 +v 0.381317 0.389755 0.742607 +v 0.378387 0.374084 0.757751 +v 0.426971 0.354492 0.748108 +v 0.445557 0.382568 0.712646 +v 0.384674 0.407593 0.724884 +v -0.409536 0.381268 0.737062 +v -0.435661 0.367615 0.731766 +v -0.403610 0.366592 0.752808 +v -0.381317 0.389755 0.742607 +v -0.416298 0.397980 0.718636 +v -0.445557 0.382568 0.712646 +v -0.426971 0.354492 0.748108 +v -0.378387 0.374084 0.757751 +v -0.384674 0.407593 0.724884 +v 0.435793 0.445076 0.650026 +v 0.429695 0.430573 0.674721 +v 0.473938 0.424988 0.642303 +v 0.441650 0.458862 0.624237 +v 0.394478 0.457611 0.658119 +v 0.391388 0.442261 0.682281 +v 0.465088 0.411865 0.667480 +v 0.482422 0.437500 0.616089 +v 0.397461 0.472168 0.632812 +v -0.435793 0.445076 0.650026 +v -0.473938 0.424988 0.642303 +v -0.429695 0.430573 0.674721 +v -0.394478 0.457611 0.658119 +v -0.441650 0.458862 0.624237 +v -0.482422 0.437500 0.616089 +v -0.465088 0.411865 0.667480 +v -0.391388 0.442261 0.682281 +v -0.397461 0.472168 0.632812 +v 0.554451 0.326481 0.625362 +v 0.539948 0.320358 0.651382 +v 0.566986 0.285355 0.622635 +v 0.568237 0.332367 0.598480 +v 0.534363 0.364563 0.629669 +v 0.521240 0.355713 0.655518 +v 0.551636 0.282166 0.648743 +v 0.581543 0.288452 0.595703 +v 0.546875 0.373047 0.602905 +v -0.554451 0.326481 0.625362 +v -0.566986 0.285355 0.622635 +v -0.539948 0.320358 0.651382 +v -0.534363 0.364563 0.629669 +v -0.568237 0.332367 0.598480 +v -0.581543 0.288452 0.595703 +v -0.551636 0.282166 0.648743 +v -0.521240 0.355713 0.655518 +v -0.546875 0.373047 0.602905 +v 0.490583 0.300165 0.719791 +v 0.475731 0.294243 0.737549 +v 0.499054 0.271950 0.717773 +v 0.507355 0.306931 0.699112 +v 0.476952 0.326294 0.722916 +v 0.463715 0.317627 0.740295 +v 0.483154 0.269012 0.735779 +v 0.516968 0.275330 0.696838 +v 0.491943 0.336182 0.702637 +v -0.490583 0.300165 0.719791 +v -0.499054 0.271950 0.717773 +v -0.475731 0.294243 0.737549 +v -0.476952 0.326294 0.722916 +v -0.507355 0.306931 0.699112 +v -0.516968 0.275330 0.696838 +v -0.483154 0.269012 0.735779 +v -0.463715 0.317627 0.740295 +v -0.491943 0.336182 0.702637 +v 0.455147 0.286345 0.769239 +v 0.458488 0.287560 0.761902 +v 0.445485 0.306035 0.771533 +v 0.451820 0.285065 0.775139 +v 0.460876 0.265124 0.767868 +v 0.464600 0.265717 0.760437 +v 0.448456 0.307861 0.764221 +v 0.442373 0.303975 0.777476 +v 0.457031 0.264506 0.773926 +v -0.455147 0.286345 0.769239 +v -0.445485 0.306035 0.771533 +v -0.458488 0.287560 0.761902 +v -0.460876 0.265124 0.767868 +v -0.451820 0.285065 0.775139 +v -0.442373 0.303975 0.777476 +v -0.448456 0.307861 0.764221 +v -0.464600 0.265717 0.760437 +v -0.457031 0.264506 0.773926 +v 0.395660 0.347622 0.782614 +v 0.396896 0.350296 0.775269 +v 0.374499 0.353882 0.787137 +v 0.394361 0.344879 0.788539 +v 0.415171 0.337306 0.778379 +v 0.417084 0.339844 0.771057 +v 0.375092 0.356750 0.779724 +v 0.373881 0.350769 0.793142 +v 0.413035 0.334574 0.784353 +v -0.395660 0.347622 0.782614 +v -0.374499 0.353882 0.787137 +v -0.396896 0.350296 0.775269 +v -0.415171 0.337306 0.778379 +v -0.394361 0.344879 0.788539 +v -0.373881 0.350769 0.793142 +v -0.375092 0.356750 0.779724 +v -0.417084 0.339844 0.771057 +v -0.413035 0.334574 0.784353 +v 0.311326 0.347622 0.799734 +v 0.309967 0.350296 0.792427 +v 0.292936 0.337306 0.802961 +v 0.312536 0.344879 0.805555 +v 0.331479 0.353882 0.795916 +v 0.330780 0.356750 0.788513 +v 0.290924 0.339844 0.795746 +v 0.294881 0.334574 0.808675 +v 0.332052 0.350769 0.801890 +v -0.311326 0.347622 0.799734 +v -0.292936 0.337306 0.802961 +v -0.309967 0.350296 0.792427 +v -0.331479 0.353882 0.795916 +v -0.312536 0.344879 0.805555 +v -0.294881 0.334574 0.808675 +v -0.290924 0.339844 0.795746 +v -0.330780 0.356750 0.788513 +v -0.332052 0.350769 0.801890 +v 0.253613 0.286345 0.807582 +v 0.250664 0.287560 0.800529 +v 0.247467 0.265124 0.807831 +v 0.256221 0.285065 0.813100 +v 0.263568 0.306035 0.806857 +v 0.260773 0.307861 0.799774 +v 0.244324 0.265717 0.800781 +v 0.250427 0.264506 0.813354 +v 0.266154 0.303975 0.812419 +v -0.253613 0.286345 0.807582 +v -0.247467 0.265124 0.807831 +v -0.250664 0.287560 0.800529 +v -0.263568 0.306035 0.806857 +v -0.256221 0.285065 0.813100 +v -0.250427 0.264506 0.813355 +v -0.244324 0.265717 0.800781 +v -0.260773 0.307861 0.799774 +v -0.266154 0.303975 0.812419 +v 0.253613 0.201951 0.807582 +v 0.250664 0.200592 0.800529 +v 0.263568 0.183561 0.806857 +v 0.256221 0.203161 0.813100 +v 0.247467 0.222104 0.807831 +v 0.244324 0.221405 0.800781 +v 0.260773 0.181549 0.799774 +v 0.266154 0.185506 0.812419 +v 0.250427 0.222677 0.813355 +v -0.253613 0.201951 0.807582 +v -0.263568 0.183561 0.806857 +v -0.250664 0.200592 0.800529 +v -0.247467 0.222104 0.807831 +v -0.256221 0.203161 0.813100 +v -0.266154 0.185506 0.812419 +v -0.260773 0.181549 0.799774 +v -0.244324 0.221405 0.800781 +v -0.250427 0.222677 0.813354 +v 0.311326 0.144238 0.799734 +v 0.309967 0.141289 0.792427 +v 0.331479 0.138092 0.795916 +v 0.312536 0.146846 0.805555 +v 0.292936 0.154193 0.802961 +v 0.290924 0.151398 0.795746 +v 0.330780 0.134949 0.788513 +v 0.332052 0.141052 0.801890 +v 0.294881 0.156779 0.808675 +v -0.311326 0.144238 0.799734 +v -0.331479 0.138092 0.795916 +v -0.309967 0.141289 0.792427 +v -0.292936 0.154193 0.802961 +v -0.312536 0.146846 0.805555 +v -0.332052 0.141052 0.801890 +v -0.330780 0.134949 0.788513 +v -0.290924 0.151398 0.795746 +v -0.294881 0.156779 0.808675 +v 0.395660 0.144238 0.782614 +v 0.396896 0.141289 0.775269 +v 0.415171 0.154193 0.778379 +v 0.394361 0.146846 0.788539 +v 0.374499 0.138092 0.787137 +v 0.375092 0.134949 0.779724 +v 0.417084 0.151398 0.771057 +v 0.413035 0.156779 0.784353 +v 0.373881 0.141052 0.793142 +v -0.395660 0.144238 0.782614 +v -0.415171 0.154193 0.778379 +v -0.396896 0.141289 0.775269 +v -0.374499 0.138092 0.787137 +v -0.394361 0.146846 0.788539 +v -0.413035 0.156779 0.784353 +v -0.417084 0.151398 0.771057 +v -0.375092 0.134949 0.779724 +v -0.373881 0.141052 0.793142 +v 0.455147 0.201951 0.769239 +v 0.458488 0.200592 0.761902 +v 0.460876 0.222104 0.767868 +v 0.451820 0.203161 0.775139 +v 0.445485 0.183561 0.771533 +v 0.448456 0.181549 0.764221 +v 0.464600 0.221405 0.760437 +v 0.457031 0.222677 0.773926 +v 0.442373 0.185506 0.777476 +v -0.455147 0.201951 0.769239 +v -0.460876 0.222104 0.767868 +v -0.458488 0.200592 0.761902 +v -0.445485 0.183561 0.771533 +v -0.451820 0.203161 0.775139 +v -0.457031 0.222677 0.773926 +v -0.464600 0.221405 0.760437 +v -0.448456 0.181549 0.764221 +v -0.442373 0.185506 0.777476 +v 0.423029 0.214253 0.793797 +v 0.412671 0.207344 0.797722 +v 0.434700 0.209651 0.787987 +v 0.420110 0.226463 0.795868 +v 0.399190 0.223785 0.804077 +v 0.425975 0.196086 0.790456 +v 0.437500 0.225688 0.787598 +v -0.423029 0.214253 0.793797 +v -0.434700 0.209651 0.787987 +v -0.412671 0.207344 0.797722 +v -0.420110 0.226463 0.795868 +v -0.437500 0.225688 0.787598 +v -0.425975 0.196086 0.790456 +v -0.399190 0.223785 0.804077 +v 0.382165 0.173490 0.803835 +v 0.387149 0.162328 0.799627 +v 0.389305 0.184008 0.803510 +v 0.369354 0.175784 0.808271 +v 0.370382 0.158875 0.803843 +v 0.401357 0.171590 0.796519 +v 0.372009 0.196604 0.810750 +v -0.382165 0.173490 0.803835 +v -0.389305 0.184008 0.803510 +v -0.387149 0.162328 0.799627 +v -0.369354 0.175784 0.808271 +v -0.372009 0.196604 0.810750 +v -0.401357 0.171590 0.796519 +v -0.370382 0.158875 0.803843 +v 0.323622 0.173490 0.816376 +v 0.316706 0.184008 0.818960 +v 0.319026 0.162328 0.814212 +v 0.335826 0.175784 0.815503 +v 0.333110 0.196604 0.819051 +v 0.305461 0.171590 0.816813 +v 0.335063 0.158875 0.811452 +v -0.323622 0.173490 0.816376 +v -0.319026 0.162328 0.814212 +v -0.316706 0.184008 0.818960 +v -0.335826 0.175784 0.815503 +v -0.335063 0.158875 0.811452 +v -0.305461 0.171590 0.816813 +v -0.333110 0.196604 0.819051 +v 0.282860 0.214253 0.821777 +v 0.285146 0.226463 0.822489 +v 0.271703 0.209651 0.820384 +v 0.293371 0.207344 0.821971 +v 0.305929 0.223785 0.822795 +v 0.268250 0.225688 0.820679 +v 0.280965 0.196086 0.819906 +v -0.282860 0.214253 0.821777 +v -0.271703 0.209651 0.820384 +v -0.285146 0.226463 0.822489 +v -0.293371 0.207344 0.821971 +v -0.280965 0.196086 0.819906 +v -0.268250 0.225688 0.820679 +v -0.305929 0.223785 0.822795 +v 0.282860 0.272925 0.821777 +v 0.293371 0.280235 0.821971 +v 0.271703 0.277863 0.820384 +v 0.285146 0.260033 0.822489 +v 0.305929 0.262848 0.822795 +v 0.280965 0.292338 0.819906 +v 0.268250 0.261007 0.820679 +v -0.282860 0.272925 0.821777 +v -0.271703 0.277863 0.820384 +v -0.293371 0.280235 0.821971 +v -0.285146 0.260033 0.822489 +v -0.268250 0.261007 0.820679 +v -0.280965 0.292338 0.819906 +v -0.305929 0.262848 0.822795 +v 0.323622 0.316057 0.816376 +v 0.335826 0.313459 0.815503 +v 0.319026 0.328033 0.814212 +v 0.316706 0.304975 0.818960 +v 0.333110 0.291494 0.819051 +v 0.335063 0.331482 0.811452 +v 0.305461 0.318380 0.816813 +v -0.323622 0.316057 0.816376 +v -0.319026 0.328033 0.814212 +v -0.335826 0.313459 0.815503 +v -0.316706 0.304975 0.818960 +v -0.305461 0.318380 0.816813 +v -0.335063 0.331482 0.811452 +v -0.333110 0.291494 0.819051 +v 0.382165 0.316057 0.803835 +v 0.389305 0.304975 0.803510 +v 0.387149 0.328033 0.799627 +v 0.369354 0.313459 0.808271 +v 0.372009 0.291494 0.810750 +v 0.401357 0.318380 0.796519 +v 0.370382 0.331482 0.803843 +v -0.382165 0.316057 0.803835 +v -0.387149 0.328033 0.799627 +v -0.389305 0.304975 0.803510 +v -0.369354 0.313459 0.808271 +v -0.370382 0.331482 0.803843 +v -0.401357 0.318380 0.796519 +v -0.372009 0.291494 0.810750 +v 0.423029 0.272925 0.793797 +v 0.420110 0.260033 0.795868 +v 0.434700 0.277863 0.787987 +v 0.412671 0.280235 0.797722 +v 0.399190 0.262848 0.804077 +v 0.437500 0.261007 0.787598 +v 0.425975 0.292338 0.790456 +v -0.423029 0.272925 0.793797 +v -0.434700 0.277863 0.787987 +v -0.420110 0.260033 0.795868 +v -0.412671 0.280235 0.797722 +v -0.425975 0.292338 0.790456 +v -0.437500 0.261007 0.787598 +v -0.399190 0.262848 0.804077 +v 0.084074 -0.955433 0.600103 +v 0.123741 -0.951782 0.596733 +v 0.080223 -0.945984 0.617844 +v 0.042511 -0.957962 0.602509 +v 0.086151 -0.962784 0.580421 +v 0.127319 -0.958923 0.576202 +v 0.117645 -0.942444 0.615417 +v 0.040649 -0.948425 0.619629 +v 0.043457 -0.965454 0.583374 +v -0.084074 -0.955433 0.600103 +v -0.080223 -0.945984 0.617844 +v -0.123741 -0.951782 0.596733 +v -0.086151 -0.962784 0.580421 +v -0.042511 -0.957962 0.602509 +v -0.040649 -0.948425 0.619629 +v -0.117645 -0.942444 0.615417 +v -0.127319 -0.958923 0.576202 +v -0.043457 -0.965454 0.583374 +v 0.222595 -0.937094 0.585682 +v 0.247322 -0.929466 0.583427 +v 0.207420 -0.927895 0.608459 +v 0.193596 -0.942810 0.589012 +v 0.234108 -0.943405 0.561295 +v 0.261749 -0.935547 0.557526 +v 0.229004 -0.920013 0.607727 +v 0.181732 -0.933655 0.610291 +v 0.202026 -0.949402 0.566071 +v -0.222595 -0.937094 0.585682 +v -0.207420 -0.927895 0.608459 +v -0.247322 -0.929466 0.583427 +v -0.234108 -0.943405 0.561295 +v -0.193596 -0.942810 0.589012 +v -0.181732 -0.933655 0.610291 +v -0.229004 -0.920013 0.607727 +v -0.261749 -0.935547 0.557526 +v -0.202026 -0.949402 0.566071 +v 0.294128 -0.887417 0.587631 +v 0.301353 -0.865410 0.592384 +v 0.270287 -0.875626 0.615791 +v 0.282997 -0.905205 0.584312 +v 0.312477 -0.893158 0.557487 +v 0.319580 -0.870483 0.561218 +v 0.276947 -0.853363 0.621307 +v 0.260254 -0.894257 0.611389 +v 0.300934 -0.911133 0.555450 +v -0.294128 -0.887417 0.587631 +v -0.270287 -0.875626 0.615791 +v -0.301353 -0.865410 0.592384 +v -0.312477 -0.893158 0.557487 +v -0.282997 -0.905205 0.584312 +v -0.260254 -0.894257 0.611389 +v -0.276947 -0.853363 0.621307 +v -0.319580 -0.870483 0.561218 +v -0.300934 -0.911133 0.555450 +v 0.303806 -0.770702 0.611521 +v 0.299583 -0.728416 0.618073 +v 0.279419 -0.765106 0.640259 +v 0.305779 -0.807396 0.604744 +v 0.320938 -0.768753 0.579506 +v 0.316467 -0.723572 0.586151 +v 0.275421 -0.726227 0.646393 +v 0.281219 -0.798553 0.633881 +v 0.323242 -0.808472 0.572693 +v -0.303806 -0.770702 0.611521 +v -0.279419 -0.765106 0.640259 +v -0.299583 -0.728416 0.618073 +v -0.320938 -0.768753 0.579506 +v -0.305779 -0.807396 0.604744 +v -0.281219 -0.798553 0.633881 +v -0.275421 -0.726227 0.646393 +v -0.316467 -0.723572 0.586151 +v -0.323242 -0.808472 0.572693 +v 0.277363 -0.568411 0.632162 +v 0.267906 -0.511993 0.634514 +v 0.253563 -0.571358 0.661118 +v 0.286003 -0.625664 0.628632 +v 0.294243 -0.560806 0.599220 +v 0.284821 -0.505249 0.600891 +v 0.244324 -0.514893 0.664215 +v 0.262115 -0.627960 0.657013 +v 0.302795 -0.617859 0.596283 +v -0.277363 -0.568411 0.632162 +v -0.253563 -0.571358 0.661118 +v -0.267906 -0.511993 0.634514 +v -0.294243 -0.560806 0.599220 +v -0.286003 -0.625664 0.628632 +v -0.262115 -0.627960 0.657013 +v -0.244324 -0.514893 0.664215 +v -0.284821 -0.505249 0.600891 +v -0.302795 -0.617859 0.596283 +v 0.294582 -0.116600 0.642537 +v 0.305641 -0.133804 0.603195 +v 0.351562 -0.101128 0.626167 +v 0.283086 -0.094314 0.680211 +v 0.243879 -0.131995 0.657483 +v 0.257782 -0.149048 0.613861 +v 0.360779 -0.118103 0.590912 +v 0.342346 -0.079865 0.659363 +v 0.226522 -0.108914 0.701620 +v -0.294582 -0.116600 0.642537 +v -0.351562 -0.101128 0.626167 +v -0.305641 -0.133804 0.603195 +v -0.243879 -0.131995 0.657483 +v -0.283086 -0.094314 0.680211 +v -0.342346 -0.079865 0.659363 +v -0.360779 -0.118103 0.590912 +v -0.257782 -0.149048 0.613861 +v -0.226522 -0.108914 0.701620 +v 0.518518 -0.039904 0.591358 +v 0.524391 -0.055946 0.564651 +v 0.568031 -0.012436 0.583534 +v 0.509888 -0.021393 0.616333 +v 0.465881 -0.063683 0.600014 +v 0.472595 -0.080017 0.571259 +v 0.573700 -0.028046 0.557953 +v 0.558777 0.005157 0.607239 +v 0.457336 -0.044342 0.627136 +v -0.518518 -0.039904 0.591358 +v -0.568031 -0.012436 0.583534 +v -0.524391 -0.055946 0.564651 +v -0.465881 -0.063683 0.600014 +v -0.509888 -0.021393 0.616333 +v -0.558777 0.005157 0.607239 +v -0.573700 -0.028046 0.557953 +v -0.472595 -0.080017 0.571259 +v -0.457336 -0.044342 0.627136 +v 0.695892 0.094107 0.553816 +v 0.706741 0.081833 0.525932 +v 0.728752 0.137581 0.547348 +v 0.680222 0.107094 0.580078 +v 0.657356 0.054520 0.564034 +v 0.665619 0.040680 0.537567 +v 0.742401 0.127472 0.518311 +v 0.710205 0.147949 0.574890 +v 0.644470 0.069489 0.588684 +v -0.695892 0.094107 0.553816 +v -0.728752 0.137581 0.547348 +v -0.706741 0.081833 0.525932 +v -0.657356 0.054520 0.564034 +v -0.680222 0.107094 0.580078 +v -0.710205 0.147949 0.574890 +v -0.742401 0.127472 0.518311 +v -0.665619 0.040680 0.537567 +v -0.644470 0.069489 0.588684 +v 0.782604 0.286396 0.574619 +v 0.801468 0.286873 0.549110 +v 0.784630 0.333412 0.594757 +v 0.758759 0.284370 0.597511 +v 0.772667 0.235634 0.557846 +v 0.790619 0.232086 0.530029 +v 0.803436 0.337769 0.571686 +v 0.760864 0.326935 0.614471 +v 0.749756 0.238281 0.583679 +v -0.782604 0.286396 0.574619 +v -0.784630 0.333412 0.594757 +v -0.801468 0.286873 0.549110 +v -0.772667 0.235634 0.557846 +v -0.758759 0.284370 0.597511 +v -0.760864 0.326935 0.614471 +v -0.803436 0.337769 0.571686 +v -0.790619 0.232086 0.530029 +v -0.749756 0.238281 0.583679 +v 0.743801 0.422855 0.647242 +v 0.756302 0.433861 0.625923 +v 0.714348 0.441048 0.661263 +v 0.726959 0.409172 0.663361 +v 0.765251 0.401497 0.632172 +v 0.780609 0.411255 0.611359 +v 0.724091 0.452606 0.638916 +v 0.700684 0.426819 0.678009 +v 0.745239 0.389069 0.648285 +v -0.743801 0.422855 0.647242 +v -0.714348 0.441048 0.661263 +v -0.756302 0.433861 0.625923 +v -0.765251 0.401497 0.632172 +v -0.726959 0.409172 0.663361 +v -0.700684 0.426819 0.678009 +v -0.724091 0.452606 0.638916 +v -0.780609 0.411255 0.611359 +v -0.745239 0.389069 0.648285 +v 0.580261 0.511143 0.705696 +v 0.587532 0.522339 0.680916 +v 0.528114 0.540871 0.720879 +v 0.570915 0.496353 0.724144 +v 0.630943 0.483803 0.690285 +v 0.638031 0.495209 0.666016 +v 0.535828 0.552155 0.695831 +v 0.519012 0.525391 0.739441 +v 0.621094 0.469299 0.708405 +v -0.580261 0.511143 0.705696 +v -0.528114 0.540871 0.720879 +v -0.587532 0.522339 0.680916 +v -0.630943 0.483803 0.690285 +v -0.570915 0.496353 0.724144 +v -0.519012 0.525391 0.739441 +v -0.535828 0.552155 0.695831 +v -0.638031 0.495209 0.666016 +v -0.621094 0.469299 0.708405 +v 0.392262 0.631254 0.759464 +v 0.398003 0.646759 0.734665 +v 0.354561 0.654953 0.769402 +v 0.386742 0.609703 0.777313 +v 0.432991 0.602730 0.748070 +v 0.440125 0.616241 0.723053 +v 0.358429 0.672455 0.744904 +v 0.351318 0.630951 0.786804 +v 0.425629 0.583740 0.766296 +v -0.392262 0.631254 0.759464 +v -0.354561 0.654953 0.769402 +v -0.398003 0.646759 0.734665 +v -0.432991 0.602730 0.748070 +v -0.386742 0.609703 0.777313 +v -0.351318 0.630951 0.786804 +v -0.358429 0.672455 0.744904 +v -0.440125 0.616241 0.723053 +v -0.425629 0.583740 0.766296 +v 0.249750 0.675045 0.790478 +v 0.247032 0.694756 0.767014 +v 0.217407 0.661621 0.794456 +v 0.254288 0.648613 0.806641 +v 0.283669 0.678207 0.784935 +v 0.283112 0.697968 0.761139 +v 0.212830 0.680664 0.771271 +v 0.224182 0.636230 0.810364 +v 0.285645 0.651581 0.801453 +v -0.249750 0.675045 0.790478 +v -0.217407 0.661621 0.794456 +v -0.247032 0.694756 0.767014 +v -0.283669 0.678207 0.784935 +v -0.254288 0.648613 0.806641 +v -0.224182 0.636230 0.810364 +v -0.212830 0.680664 0.771271 +v -0.283112 0.697968 0.761139 +v -0.285645 0.651581 0.801453 +v 0.133230 0.561876 0.796757 +v 0.126549 0.576622 0.773888 +v 0.109032 0.519722 0.794945 +v 0.142944 0.543198 0.812675 +v 0.159119 0.602783 0.797478 +v 0.152527 0.619141 0.774567 +v 0.102692 0.533051 0.772156 +v 0.118591 0.503357 0.810944 +v 0.168518 0.581543 0.813293 +v -0.133230 0.561876 0.796757 +v -0.109032 0.519722 0.794945 +v -0.126549 0.576622 0.773888 +v -0.159119 0.602783 0.797478 +v -0.142944 0.543198 0.812675 +v -0.118591 0.503357 0.810944 +v -0.102692 0.533051 0.772156 +v -0.152527 0.619141 0.774567 +v -0.168518 0.581543 0.813293 +v 0.042845 0.428807 0.786262 +v 0.039101 0.441628 0.764481 +v 0.021423 0.414590 0.784034 +v 0.049759 0.413369 0.801711 +v 0.064262 0.450905 0.789238 +v 0.059357 0.463104 0.767029 +v 0.019409 0.428894 0.762573 +v 0.025269 0.395481 0.799344 +v 0.072693 0.436707 0.804962 +v -0.042845 0.428807 0.786262 +v -0.021423 0.414590 0.784034 +v -0.039101 0.441628 0.764481 +v -0.064262 0.450905 0.789238 +v -0.049759 0.413369 0.801711 +v -0.025269 0.395481 0.799344 +v -0.019409 0.428894 0.762573 +v -0.059357 0.463104 0.767029 +v -0.072693 0.436707 0.804962 +v 0.179953 0.481827 0.804874 +v 0.192589 0.463028 0.791557 +v 0.202950 0.506821 0.803665 +v 0.167114 0.501877 0.815636 +v 0.157730 0.454422 0.805115 +v 0.171753 0.440308 0.792694 +v 0.214325 0.483093 0.789551 +v 0.191223 0.531982 0.815247 +v 0.143738 0.469666 0.814972 +v -0.179953 0.481827 0.804874 +v -0.202950 0.506821 0.803665 +v -0.192589 0.463028 0.791557 +v -0.157730 0.454422 0.805115 +v -0.167114 0.501877 0.815636 +v -0.191223 0.531982 0.815247 +v -0.214325 0.483093 0.789551 +v -0.171753 0.440308 0.792694 +v -0.143738 0.469666 0.814972 +v 0.273697 0.548807 0.795172 +v 0.280563 0.516190 0.779991 +v 0.298180 0.550529 0.790718 +v 0.266739 0.583336 0.807800 +v 0.249855 0.541183 0.798782 +v 0.258392 0.510193 0.783691 +v 0.303314 0.517456 0.775726 +v 0.293213 0.585541 0.803162 +v 0.241028 0.573975 0.811340 +v -0.273697 0.548807 0.795172 +v -0.298180 0.550529 0.790718 +v -0.280563 0.516190 0.779991 +v -0.249855 0.541183 0.798782 +v -0.266739 0.583336 0.807800 +v -0.293213 0.585541 0.803162 +v -0.303314 0.517456 0.775726 +v -0.258392 0.510193 0.783691 +v -0.241028 0.573975 0.811340 +v 0.379248 0.524296 0.771605 +v 0.411896 0.507378 0.762650 +v 0.380058 0.554375 0.782196 +v 0.350212 0.537682 0.779091 +v 0.379364 0.495567 0.758812 +v 0.410065 0.481415 0.750854 +v 0.414764 0.534424 0.772400 +v 0.349121 0.570282 0.790466 +v 0.352020 0.506714 0.765350 +v -0.379248 0.524296 0.771605 +v -0.380058 0.554375 0.782196 +v -0.411896 0.507378 0.762650 +v -0.379364 0.495567 0.758812 +v -0.350212 0.537682 0.779091 +v -0.349121 0.570282 0.790466 +v -0.414764 0.534424 0.772400 +v -0.410065 0.481415 0.750854 +v -0.352020 0.506714 0.765350 +v 0.538280 0.443983 0.725407 +v 0.581680 0.421967 0.711182 +v 0.549278 0.461624 0.732903 +v 0.492828 0.466270 0.739273 +v 0.527794 0.427345 0.715965 +v 0.567932 0.407776 0.702606 +v 0.595703 0.437317 0.717804 +v 0.500824 0.486816 0.747498 +v 0.485626 0.446625 0.729126 +v -0.538280 0.443983 0.725407 +v -0.549278 0.461624 0.732903 +v -0.581680 0.421967 0.711182 +v -0.527794 0.427345 0.715965 +v -0.492828 0.466270 0.739273 +v -0.500824 0.486816 0.747498 +v -0.595703 0.437317 0.717804 +v -0.567932 0.407776 0.702606 +v -0.485626 0.446625 0.729126 +v 0.664808 0.365265 0.673624 +v 0.676369 0.346100 0.663368 +v 0.686188 0.379356 0.675629 +v 0.646194 0.383026 0.684937 +v 0.644188 0.352577 0.670113 +v 0.654572 0.333099 0.662292 +v 0.699341 0.360382 0.663055 +v 0.665527 0.396790 0.688873 +v 0.627258 0.370667 0.679291 +v -0.664808 0.365265 0.673624 +v -0.686188 0.379356 0.675629 +v -0.676369 0.346100 0.663368 +v -0.644188 0.352577 0.670113 +v -0.646194 0.383026 0.684937 +v -0.665527 0.396790 0.688873 +v -0.699341 0.360382 0.663055 +v -0.654572 0.333099 0.662292 +v -0.627258 0.370667 0.679291 +v 0.681852 0.264402 0.638559 +v 0.673935 0.230431 0.633636 +v 0.706696 0.272987 0.629402 +v 0.684456 0.295929 0.645576 +v 0.658775 0.255814 0.645416 +v 0.650818 0.224701 0.642761 +v 0.698730 0.235596 0.621765 +v 0.709106 0.307526 0.639496 +v 0.661530 0.284882 0.649841 +v -0.681852 0.264402 0.638559 +v -0.706696 0.272987 0.629402 +v -0.673935 0.230431 0.633636 +v -0.658775 0.255814 0.645416 +v -0.684456 0.295929 0.645576 +v -0.709106 0.307526 0.639496 +v -0.698730 0.235596 0.621765 +v -0.650818 0.224701 0.642761 +v -0.661530 0.284882 0.649841 +v 0.613293 0.127222 0.636484 +v 0.582336 0.095871 0.642464 +v 0.637497 0.124214 0.621643 +v 0.639511 0.160698 0.632599 +v 0.589966 0.128693 0.648781 +v 0.559631 0.099274 0.654755 +v 0.605652 0.090851 0.627991 +v 0.664062 0.159912 0.617859 +v 0.616150 0.160126 0.644470 +v -0.613293 0.127222 0.636484 +v -0.637497 0.124214 0.621643 +v -0.582336 0.095871 0.642464 +v -0.589966 0.128693 0.648781 +v -0.639511 0.160698 0.632599 +v -0.664062 0.159912 0.617859 +v -0.605652 0.090851 0.627991 +v -0.559631 0.099274 0.654755 +v -0.616150 0.160126 0.644470 +v 0.469450 0.023006 0.667860 +v 0.425774 0.007507 0.679520 +v 0.484497 0.011078 0.654724 +v 0.510010 0.043137 0.658180 +v 0.455070 0.033340 0.678703 +v 0.415924 0.021027 0.689087 +v 0.436584 -0.007599 0.667175 +v 0.528748 0.033844 0.644592 +v 0.491760 0.050812 0.669769 +v -0.469450 0.023006 0.667860 +v -0.484497 0.011078 0.654724 +v -0.425774 0.007507 0.679520 +v -0.455070 0.033340 0.678703 +v -0.510010 0.043137 0.658180 +v -0.528748 0.033844 0.644592 +v -0.436584 -0.007599 0.667175 +v -0.415924 0.021027 0.689087 +v -0.491760 0.050812 0.669769 +v 0.277513 -0.008467 0.729205 +v 0.228559 -0.004325 0.745786 +v 0.274053 -0.038283 0.724126 +v 0.328514 -0.008179 0.711075 +v 0.282532 0.018578 0.730293 +v 0.239227 0.027771 0.743073 +v 0.217977 -0.042141 0.746419 +v 0.330383 -0.031891 0.702332 +v 0.328400 0.013824 0.715698 +v -0.277513 -0.008467 0.729205 +v -0.274053 -0.038283 0.724126 +v -0.228559 -0.004325 0.745786 +v -0.282532 0.018578 0.730293 +v -0.328514 -0.008179 0.711075 +v -0.330383 -0.031891 0.702332 +v -0.217977 -0.042141 0.746419 +v -0.239227 0.027771 0.743073 +v -0.328400 0.013824 0.715698 +v 0.125806 0.044509 0.756848 +v 0.150055 0.074005 0.754539 +v 0.104836 0.068683 0.751911 +v 0.100606 0.010891 0.759301 +v 0.151929 0.022683 0.759733 +v 0.172211 0.056091 0.755157 +v 0.133575 0.093933 0.752258 +v 0.072388 0.039686 0.751478 +v 0.130819 -0.017117 0.764730 +v -0.125806 0.044509 0.756848 +v -0.104836 0.068683 0.751911 +v -0.150055 0.074005 0.754539 +v -0.151929 0.022683 0.759733 +v -0.100606 0.010891 0.759301 +v -0.072388 0.039686 0.751478 +v -0.133575 0.093933 0.752258 +v -0.172211 0.056091 0.755157 +v -0.130819 -0.017117 0.764730 +v 0.099239 0.375864 0.799227 +v 0.117020 0.400772 0.802246 +v 0.078720 0.385842 0.807113 +v 0.083649 0.351220 0.795417 +v 0.119873 0.367493 0.789330 +v 0.134888 0.391968 0.791595 +v 0.099792 0.410828 0.810455 +v 0.057373 0.362033 0.803738 +v 0.107788 0.342651 0.786133 +v -0.099239 0.375864 0.799227 +v -0.078720 0.385842 0.807113 +v -0.117020 0.400772 0.802246 +v -0.119873 0.367493 0.789330 +v -0.083649 0.351220 0.795417 +v -0.057373 0.362033 0.803738 +v -0.099792 0.410828 0.810455 +v -0.134888 0.391968 0.791595 +v -0.107788 0.342651 0.786133 +v 0.064318 0.269104 0.777134 +v 0.066162 0.297936 0.784186 +v 0.032898 0.272973 0.781470 +v 0.064766 0.240150 0.770027 +v 0.092781 0.265114 0.772621 +v 0.094360 0.291382 0.777588 +v 0.034058 0.305637 0.790799 +v 0.033081 0.241333 0.772583 +v 0.093658 0.238800 0.767487 +v -0.064318 0.269104 0.777134 +v -0.032898 0.272973 0.781470 +v -0.066162 0.297936 0.784186 +v -0.092781 0.265114 0.772621 +v -0.064766 0.240150 0.770027 +v -0.033081 0.241333 0.772583 +v -0.034058 0.305637 0.790799 +v -0.094360 0.291382 0.777588 +v -0.093658 0.238800 0.767487 +v 0.073391 0.153090 0.751471 +v 0.079414 0.123645 0.748218 +v 0.104965 0.162315 0.753624 +v 0.069374 0.182198 0.756691 +v 0.037720 0.144748 0.750064 +v 0.041138 0.107680 0.745741 +v 0.111969 0.138367 0.751038 +v 0.099884 0.187164 0.757599 +v 0.035522 0.178345 0.756470 +v -0.073391 0.153090 0.751471 +v -0.104965 0.162315 0.753624 +v -0.079414 0.123645 0.748218 +v -0.037720 0.144748 0.750064 +v -0.069374 0.182198 0.756691 +v -0.099884 0.187164 0.757599 +v -0.111969 0.138367 0.751038 +v -0.041138 0.107680 0.745741 +v -0.035522 0.178345 0.756470 +v 0.055864 -0.911802 0.663013 +v 0.046097 -0.900871 0.674286 +v 0.028320 -0.913940 0.662949 +v 0.065666 -0.923462 0.649399 +v 0.081856 -0.908318 0.663300 +v 0.067810 -0.897369 0.675232 +v 0.023315 -0.902893 0.673767 +v 0.033325 -0.925720 0.649902 +v 0.096039 -0.919983 0.648865 +v -0.055864 -0.911802 0.663013 +v -0.028320 -0.913940 0.662949 +v -0.046097 -0.900871 0.674286 +v -0.081856 -0.908318 0.663300 +v -0.065666 -0.923462 0.649399 +v -0.033325 -0.925720 0.649902 +v -0.023315 -0.902893 0.673767 +v -0.067810 -0.897369 0.675232 +v -0.096039 -0.919983 0.648865 +v 0.143497 -0.889484 0.666868 +v 0.121422 -0.876411 0.681267 +v 0.126076 -0.897545 0.665070 +v 0.166679 -0.903206 0.649414 +v 0.157753 -0.878532 0.669518 +v 0.134186 -0.864258 0.684601 +v 0.105896 -0.885529 0.678650 +v 0.146942 -0.910217 0.648621 +v 0.182861 -0.893524 0.651184 +v -0.143497 -0.889484 0.666868 +v -0.126076 -0.897545 0.665070 +v -0.121422 -0.876411 0.681267 +v -0.157753 -0.878532 0.669518 +v -0.166679 -0.903206 0.649414 +v -0.146942 -0.910217 0.648621 +v -0.105896 -0.885529 0.678650 +v -0.134186 -0.864258 0.684601 +v -0.182861 -0.893524 0.651184 +v 0.181567 -0.822344 0.683732 +v 0.152913 -0.804985 0.699430 +v 0.176643 -0.844627 0.678093 +v 0.212151 -0.841202 0.664284 +v 0.183904 -0.798508 0.689649 +v 0.151904 -0.780488 0.705527 +v 0.150177 -0.828125 0.693756 +v 0.205322 -0.862396 0.658752 +v 0.216522 -0.817841 0.670258 +v -0.181567 -0.822344 0.683732 +v -0.176643 -0.844627 0.678093 +v -0.152913 -0.804985 0.699430 +v -0.183904 -0.798508 0.689649 +v -0.212151 -0.841202 0.664284 +v -0.205322 -0.862396 0.658752 +v -0.150177 -0.828125 0.693756 +v -0.151904 -0.780488 0.705527 +v -0.216522 -0.817841 0.670258 +v 0.163996 -0.569075 0.718437 +v 0.156151 -0.514290 0.722023 +v 0.132980 -0.567276 0.728561 +v 0.171585 -0.621750 0.714348 +v 0.195122 -0.570595 0.704269 +v 0.186707 -0.515137 0.708038 +v 0.125885 -0.513214 0.731812 +v 0.139923 -0.618683 0.724915 +v 0.203156 -0.624664 0.699860 +v -0.163996 -0.569075 0.718437 +v -0.132980 -0.567276 0.728561 +v -0.156151 -0.514290 0.722023 +v -0.195122 -0.570595 0.704269 +v -0.171585 -0.621750 0.714348 +v -0.139923 -0.618683 0.724915 +v -0.125885 -0.513214 0.731812 +v -0.186707 -0.515137 0.708038 +v -0.203156 -0.624664 0.699860 +v 0.184522 -0.730820 0.703058 +v 0.185155 -0.753556 0.699415 +v 0.218079 -0.742950 0.686157 +v 0.182205 -0.703293 0.706566 +v 0.150502 -0.720002 0.716161 +v 0.150195 -0.738984 0.713950 +v 0.219330 -0.769379 0.681366 +v 0.214996 -0.711700 0.690704 +v 0.149078 -0.695709 0.718567 +v -0.184522 -0.730820 0.703058 +v -0.218079 -0.742950 0.686157 +v -0.185155 -0.753556 0.699415 +v -0.150502 -0.720002 0.716161 +v -0.182205 -0.703293 0.706566 +v -0.214996 -0.711700 0.690704 +v -0.219330 -0.769379 0.681366 +v -0.150195 -0.738984 0.713950 +v -0.149078 -0.695709 0.718567 +v 0.041890 -0.380160 0.745908 +v 0.042461 -0.349551 0.746542 +v 0.020691 -0.382935 0.746460 +v 0.042091 -0.417427 0.746002 +v 0.064106 -0.376719 0.744253 +v 0.064863 -0.343027 0.744956 +v 0.020996 -0.354004 0.747070 +v 0.020752 -0.418945 0.746582 +v 0.064606 -0.415802 0.744263 +v -0.041890 -0.380160 0.745908 +v -0.020691 -0.382935 0.746460 +v -0.042461 -0.349551 0.746542 +v -0.064106 -0.376719 0.744253 +v -0.042091 -0.417427 0.746002 +v -0.020752 -0.418945 0.746582 +v -0.020996 -0.354004 0.747070 +v -0.064863 -0.343027 0.744956 +v -0.064606 -0.415802 0.744263 +v 0.049374 -0.563419 0.741390 +v 0.075424 -0.564285 0.739365 +v 0.046028 -0.511330 0.743988 +v 0.024414 -0.562927 0.742065 +v 0.052711 -0.613075 0.738541 +v 0.080231 -0.614166 0.736389 +v 0.070587 -0.511627 0.742065 +v 0.022705 -0.511230 0.744629 +v 0.026123 -0.612549 0.739258 +v -0.049374 -0.563419 0.741390 +v -0.046028 -0.511330 0.743988 +v -0.075424 -0.564285 0.739365 +v -0.052711 -0.613075 0.738541 +v -0.024414 -0.562927 0.742065 +v -0.022705 -0.511230 0.744629 +v -0.070587 -0.511627 0.742065 +v -0.080231 -0.614166 0.736389 +v -0.026123 -0.612549 0.739258 +v 0.056691 -0.710072 0.733549 +v 0.055920 -0.725777 0.732763 +v 0.086232 -0.709626 0.730901 +v 0.056587 -0.687508 0.734512 +v 0.028107 -0.711838 0.734421 +v 0.027710 -0.728455 0.733582 +v 0.085132 -0.724946 0.730186 +v 0.085968 -0.687775 0.731995 +v 0.028076 -0.688232 0.735352 +v -0.056691 -0.710072 0.733549 +v -0.086232 -0.709626 0.730901 +v -0.055920 -0.725777 0.732763 +v -0.028107 -0.711838 0.734421 +v -0.056587 -0.687508 0.734512 +v -0.085968 -0.687775 0.731995 +v -0.085132 -0.724946 0.730186 +v -0.027710 -0.728455 0.733582 +v -0.028076 -0.688232 0.735352 +v 0.108304 -0.253541 0.767297 +v 0.115776 -0.238411 0.769028 +v 0.107849 -0.251030 0.777138 +v 0.097225 -0.267695 0.765249 +v 0.110157 -0.256663 0.757261 +v 0.117218 -0.240265 0.758942 +v 0.115417 -0.236816 0.779083 +v 0.096741 -0.263885 0.774780 +v 0.099287 -0.273203 0.755454 +v -0.108304 -0.253541 0.767297 +v -0.107849 -0.251030 0.777138 +v -0.115776 -0.238411 0.769028 +v -0.110157 -0.256663 0.757261 +v -0.097225 -0.267695 0.765249 +v -0.096741 -0.263885 0.774780 +v -0.115417 -0.236816 0.779083 +v -0.117218 -0.240265 0.758942 +v -0.099287 -0.273203 0.755454 +v 0.115433 -0.184227 0.768106 +v 0.107582 -0.166679 0.766350 +v 0.115601 -0.183525 0.777374 +v 0.119408 -0.203163 0.769455 +v 0.115662 -0.184731 0.760262 +v 0.107269 -0.166748 0.759521 +v 0.108215 -0.165985 0.775116 +v 0.119324 -0.202393 0.779205 +v 0.120026 -0.204010 0.760529 +v -0.115433 -0.184227 0.768106 +v -0.115601 -0.183525 0.777374 +v -0.107582 -0.166679 0.766350 +v -0.115662 -0.184731 0.760262 +v -0.119408 -0.203163 0.769455 +v -0.119324 -0.202393 0.779205 +v -0.108215 -0.165985 0.775116 +v -0.107269 -0.166748 0.759521 +v -0.120026 -0.204010 0.760529 +v 0.062420 -0.137662 0.762971 +v 0.044228 -0.137599 0.763105 +v 0.066582 -0.137550 0.771408 +v 0.080208 -0.142540 0.763512 +v 0.057976 -0.135142 0.756307 +v 0.036377 -0.135050 0.755697 +v 0.050201 -0.137512 0.771820 +v 0.082825 -0.142181 0.771820 +v 0.077850 -0.140869 0.757324 +v -0.062420 -0.137662 0.762971 +v -0.066582 -0.137550 0.771408 +v -0.044228 -0.137599 0.763105 +v -0.057976 -0.135142 0.756307 +v -0.080208 -0.142540 0.763512 +v -0.082825 -0.142181 0.771820 +v -0.050201 -0.137512 0.771820 +v -0.036377 -0.135050 0.755697 +v -0.077850 -0.140869 0.757324 +v 0.010370 -0.161025 0.768607 +v 0.004761 -0.168549 0.770798 +v 0.014473 -0.160324 0.777382 +v 0.017677 -0.151087 0.766157 +v 0.005402 -0.161143 0.762258 +v 0.002441 -0.166748 0.765869 +v 0.006836 -0.168579 0.779236 +v 0.023712 -0.150452 0.775116 +v 0.009399 -0.150675 0.758748 +v -0.010370 -0.161025 0.768607 +v -0.014473 -0.160324 0.777382 +v -0.004761 -0.168549 0.770798 +v -0.005402 -0.161143 0.762258 +v -0.017677 -0.151087 0.766157 +v -0.023712 -0.150452 0.775116 +v -0.006836 -0.168579 0.779236 +v -0.002441 -0.166748 0.765869 +v -0.009399 -0.150675 0.758748 +v 0.044097 -0.302878 0.762311 +v 0.043773 -0.311740 0.754720 +v 0.064540 -0.292903 0.762625 +v 0.044357 -0.297356 0.770721 +v 0.022369 -0.309723 0.762207 +v 0.021973 -0.318359 0.754883 +v 0.065229 -0.301646 0.754233 +v 0.064514 -0.287445 0.771362 +v 0.022583 -0.304321 0.770508 +v -0.044097 -0.302878 0.762311 +v -0.064540 -0.292903 0.762625 +v -0.043773 -0.311740 0.754720 +v -0.022369 -0.309723 0.762207 +v -0.044357 -0.297356 0.770721 +v -0.064514 -0.287445 0.771362 +v -0.065229 -0.301646 0.754233 +v -0.021973 -0.318359 0.754883 +v -0.022583 -0.304321 0.770508 +v 0.042237 -0.281415 0.792981 +v 0.043381 -0.287743 0.786407 +v 0.061067 -0.272830 0.793749 +v 0.041216 -0.273951 0.798967 +v 0.021576 -0.287766 0.792725 +v 0.022095 -0.294556 0.786133 +v 0.063049 -0.278290 0.787231 +v 0.058662 -0.267131 0.799384 +v 0.021240 -0.279358 0.798828 +v -0.042237 -0.281415 0.792981 +v -0.061067 -0.272830 0.793749 +v -0.043381 -0.287743 0.786407 +v -0.021576 -0.287766 0.792725 +v -0.041216 -0.273951 0.798967 +v -0.058662 -0.267131 0.799384 +v -0.063049 -0.278290 0.787231 +v -0.022095 -0.294556 0.786133 +v -0.021240 -0.279358 0.798828 +v 0.022377 -0.165894 0.803798 +v 0.011200 -0.174574 0.806144 +v 0.026116 -0.170854 0.810398 +v 0.033509 -0.156116 0.801014 +v 0.019936 -0.162277 0.795906 +v 0.009766 -0.170532 0.798035 +v 0.013550 -0.181085 0.813260 +v 0.036716 -0.161024 0.807237 +v 0.030914 -0.152405 0.793304 +v -0.022377 -0.165894 0.803798 +v -0.026116 -0.170854 0.810398 +v -0.011200 -0.174574 0.806144 +v -0.019936 -0.162277 0.795906 +v -0.033509 -0.156116 0.801014 +v -0.036716 -0.161024 0.807237 +v -0.013550 -0.181085 0.813260 +v -0.009766 -0.170532 0.798035 +v -0.030914 -0.152405 0.793304 +v 0.071327 -0.143918 0.796885 +v 0.058137 -0.143817 0.797261 +v 0.070258 -0.150553 0.803765 +v 0.083861 -0.148162 0.797261 +v 0.071251 -0.139473 0.789047 +v 0.056915 -0.139465 0.789520 +v 0.058445 -0.150160 0.803819 +v 0.081475 -0.154202 0.803819 +v 0.085022 -0.144012 0.789520 +v -0.071327 -0.143918 0.796885 +v -0.070258 -0.150553 0.803765 +v -0.058137 -0.143817 0.797261 +v -0.071251 -0.139473 0.789047 +v -0.083861 -0.148162 0.797261 +v -0.081475 -0.154202 0.803819 +v -0.058445 -0.150160 0.803819 +v -0.056915 -0.139465 0.789520 +v -0.085022 -0.144012 0.789520 +v 0.108573 -0.185947 0.803689 +v 0.103026 -0.169952 0.801014 +v 0.101652 -0.187492 0.810124 +v 0.111298 -0.202988 0.805710 +v 0.112915 -0.184349 0.795868 +v 0.106506 -0.167328 0.793304 +v 0.097588 -0.173123 0.807237 +v 0.103455 -0.203308 0.812164 +v 0.116150 -0.202393 0.797882 +v -0.108573 -0.185947 0.803689 +v -0.101652 -0.187492 0.810124 +v -0.103026 -0.169952 0.801014 +v -0.112915 -0.184349 0.795868 +v -0.111298 -0.202988 0.805710 +v -0.103455 -0.203308 0.812164 +v -0.097588 -0.173123 0.807237 +v -0.106506 -0.167328 0.793304 +v -0.116150 -0.202393 0.797882 +v 0.100396 -0.243748 0.801819 +v 0.107269 -0.232468 0.804733 +v 0.092867 -0.241755 0.807611 +v 0.090456 -0.253756 0.798418 +v 0.104980 -0.246178 0.794685 +v 0.112244 -0.233887 0.797272 +v 0.099060 -0.231140 0.810822 +v 0.084418 -0.251017 0.803901 +v 0.094299 -0.257172 0.791626 +v -0.100396 -0.243748 0.801819 +v -0.092867 -0.241755 0.807611 +v -0.107269 -0.232468 0.804733 +v -0.104980 -0.246178 0.794685 +v -0.090456 -0.253756 0.798418 +v -0.084418 -0.251017 0.803901 +v -0.099060 -0.231140 0.810822 +v -0.112244 -0.233887 0.797272 +v -0.094299 -0.257172 0.791626 +v 0.062231 -0.183748 0.818621 +v 0.057755 -0.198088 0.821059 +v 0.077879 -0.186545 0.817662 +v 0.065681 -0.170695 0.814752 +v 0.046410 -0.180284 0.817905 +v 0.037476 -0.193780 0.820828 +v 0.076355 -0.201111 0.819855 +v 0.078423 -0.173489 0.814073 +v 0.052951 -0.168471 0.814073 +v -0.062231 -0.183748 0.818621 +v -0.077879 -0.186545 0.817662 +v -0.057755 -0.198088 0.821059 +v -0.046410 -0.180284 0.817905 +v -0.065681 -0.170695 0.814752 +v -0.078423 -0.173489 0.814073 +v -0.076355 -0.201111 0.819855 +v -0.037476 -0.193780 0.820828 +v -0.052951 -0.168471 0.814073 +v 0.045914 -0.241560 0.815415 +v 0.023621 -0.242155 0.815803 +v 0.042986 -0.254084 0.810320 +v 0.065553 -0.240809 0.814264 +v 0.049240 -0.227659 0.819381 +v 0.025391 -0.225640 0.819851 +v 0.022217 -0.256653 0.810547 +v 0.060859 -0.251383 0.809638 +v 0.070007 -0.228943 0.818024 +v -0.045914 -0.241560 0.815415 +v -0.042986 -0.254084 0.810320 +v -0.023621 -0.242155 0.815803 +v -0.049240 -0.227659 0.819381 +v -0.065553 -0.240809 0.814264 +v -0.060859 -0.251383 0.809638 +v -0.022217 -0.256653 0.810547 +v -0.025391 -0.225640 0.819851 +v -0.070007 -0.228943 0.818024 +v 0.057559 -0.086162 0.755893 +v 0.054375 -0.110087 0.753164 +v 0.082990 -0.105353 0.761007 +v 0.065145 -0.056827 0.758813 +v 0.029449 -0.072425 0.750359 +v 0.027954 -0.103678 0.749715 +v 0.077728 -0.122803 0.756592 +v 0.094564 -0.083767 0.765462 +v 0.033203 -0.033678 0.750746 +v -0.057559 -0.086162 0.755893 +v -0.082990 -0.105353 0.761007 +v -0.054375 -0.110087 0.753164 +v -0.029449 -0.072425 0.750359 +v -0.065145 -0.056827 0.758813 +v -0.094564 -0.083767 0.765462 +v -0.077728 -0.122803 0.756592 +v -0.027954 -0.103678 0.749715 +v -0.033203 -0.033678 0.750746 +v 0.128578 -0.179783 0.751031 +v 0.121521 -0.182320 0.753151 +v 0.135300 -0.204750 0.742661 +v 0.138883 -0.177802 0.744626 +v 0.118635 -0.153785 0.758657 +v 0.111542 -0.160400 0.756836 +v 0.127472 -0.204620 0.748444 +v 0.145660 -0.205688 0.732452 +v 0.131063 -0.146145 0.757772 +v -0.128578 -0.179783 0.751031 +v -0.135300 -0.204750 0.742661 +v -0.121521 -0.182320 0.753151 +v -0.118635 -0.153785 0.758657 +v -0.138883 -0.177802 0.744626 +v -0.145660 -0.205688 0.732452 +v -0.127472 -0.204620 0.748444 +v -0.111542 -0.160400 0.756836 +v -0.131063 -0.146145 0.757772 +v 0.139457 -0.268054 0.727502 +v 0.125537 -0.264109 0.738218 +v 0.137737 -0.288821 0.726187 +v 0.155556 -0.271851 0.713173 +v 0.140182 -0.248360 0.730331 +v 0.129059 -0.245270 0.740509 +v 0.119673 -0.284312 0.736777 +v 0.156860 -0.292847 0.711914 +v 0.153839 -0.251587 0.716461 +v -0.139457 -0.268054 0.727502 +v -0.137737 -0.288821 0.726187 +v -0.125537 -0.264109 0.738218 +v -0.140182 -0.248360 0.730331 +v -0.155556 -0.271851 0.713173 +v -0.156860 -0.292847 0.711914 +v -0.119673 -0.284312 0.736777 +v -0.129059 -0.245270 0.740509 +v -0.153839 -0.251587 0.716461 +v 0.139558 -0.376040 0.726020 +v 0.113147 -0.374430 0.735159 +v 0.143486 -0.415871 0.725777 +v 0.166153 -0.377899 0.712379 +v 0.137157 -0.341708 0.725912 +v 0.112471 -0.338633 0.735435 +v 0.115509 -0.414948 0.734985 +v 0.171814 -0.416992 0.712067 +v 0.161743 -0.344604 0.712158 +v -0.139558 -0.376040 0.726020 +v -0.143486 -0.415871 0.725777 +v -0.113147 -0.374430 0.735159 +v -0.137157 -0.341708 0.725912 +v -0.166153 -0.377899 0.712379 +v -0.171814 -0.416992 0.712067 +v -0.115509 -0.414948 0.734985 +v -0.112471 -0.338633 0.735435 +v -0.161743 -0.344604 0.712158 +v 0.237865 -0.378819 0.634836 +v 0.253693 -0.375473 0.599762 +v 0.228569 -0.347641 0.633682 +v 0.216690 -0.379913 0.666512 +v 0.247795 -0.415924 0.635643 +v 0.264191 -0.411499 0.600891 +v 0.243744 -0.345276 0.598450 +v 0.208618 -0.348022 0.665771 +v 0.225525 -0.417725 0.666779 +v -0.237865 -0.378819 0.634836 +v -0.228569 -0.347641 0.633682 +v -0.253693 -0.375473 0.599762 +v -0.247795 -0.415924 0.635643 +v -0.216690 -0.379913 0.666512 +v -0.208618 -0.348022 0.665771 +v -0.243744 -0.345276 0.598450 +v -0.264191 -0.411499 0.600891 +v -0.225525 -0.417725 0.666779 +v 0.207571 -0.279001 0.632662 +v 0.221146 -0.278908 0.597656 +v 0.201843 -0.259880 0.635185 +v 0.190926 -0.277649 0.665413 +v 0.213524 -0.298996 0.632034 +v 0.227509 -0.298157 0.596985 +v 0.215179 -0.260834 0.599792 +v 0.185944 -0.257690 0.668488 +v 0.195923 -0.298218 0.664551 +v -0.207571 -0.279001 0.632662 +v -0.201843 -0.259880 0.635185 +v -0.221146 -0.278908 0.597656 +v -0.213524 -0.298996 0.632034 +v -0.190926 -0.277649 0.665413 +v -0.185944 -0.257690 0.668488 +v -0.215179 -0.260834 0.599792 +v -0.227509 -0.298157 0.596985 +v -0.195923 -0.298218 0.664551 +v 0.184795 -0.195007 0.657861 +v 0.188642 -0.171302 0.665235 +v 0.168363 -0.185828 0.697446 +v 0.188599 -0.218224 0.648705 +v 0.198990 -0.203087 0.616371 +v 0.204193 -0.183472 0.620575 +v 0.168538 -0.155545 0.710286 +v 0.173370 -0.212769 0.684967 +v 0.202118 -0.223114 0.610046 +v -0.184795 -0.195007 0.657861 +v -0.168363 -0.185828 0.697446 +v -0.188642 -0.171302 0.665235 +v -0.198990 -0.203087 0.616371 +v -0.188599 -0.218224 0.648705 +v -0.173370 -0.212769 0.684967 +v -0.168538 -0.155545 0.710286 +v -0.204193 -0.183472 0.620575 +v -0.202118 -0.223114 0.610046 +v 0.049577 -0.750141 0.727230 +v 0.051984 -0.744484 0.730169 +v 0.025421 -0.753983 0.727051 +v 0.047813 -0.754250 0.722206 +v 0.071202 -0.748169 0.726789 +v 0.076709 -0.742891 0.728843 +v 0.026245 -0.748230 0.730408 +v 0.024658 -0.757935 0.721741 +v 0.067963 -0.752228 0.722260 +v -0.049577 -0.750141 0.727230 +v -0.025421 -0.753983 0.727051 +v -0.051984 -0.744484 0.730169 +v -0.071202 -0.748169 0.726789 +v -0.047813 -0.754250 0.722206 +v -0.024658 -0.757935 0.721741 +v -0.026245 -0.748230 0.730408 +v -0.076709 -0.742891 0.728843 +v -0.067963 -0.752228 0.722260 +v 0.103248 -0.781776 0.718223 +v 0.113179 -0.784508 0.716795 +v 0.098607 -0.763916 0.722273 +v 0.097542 -0.781319 0.715424 +v 0.103081 -0.801788 0.713608 +v 0.112946 -0.806396 0.711700 +v 0.108325 -0.763276 0.721885 +v 0.093231 -0.765533 0.718964 +v 0.097229 -0.799683 0.711090 +v -0.103248 -0.781776 0.718223 +v -0.098607 -0.763916 0.722273 +v -0.113179 -0.784508 0.716795 +v -0.103081 -0.801788 0.713608 +v -0.097542 -0.781319 0.715424 +v -0.093231 -0.765533 0.718964 +v -0.108325 -0.763276 0.721885 +v -0.112946 -0.806396 0.711700 +v -0.097229 -0.799683 0.711090 +v 0.080856 -0.850964 0.699348 +v 0.089531 -0.856911 0.697777 +v 0.091148 -0.837616 0.703903 +v 0.075569 -0.846626 0.696785 +v 0.068703 -0.861526 0.695297 +v 0.076599 -0.867340 0.694031 +v 0.100372 -0.843506 0.702057 +v 0.085510 -0.833618 0.701447 +v 0.063934 -0.857086 0.692627 +v -0.080856 -0.850964 0.699348 +v -0.091148 -0.837616 0.703903 +v -0.089531 -0.856911 0.697777 +v -0.068703 -0.861526 0.695297 +v -0.075569 -0.846626 0.696785 +v -0.085510 -0.833618 0.701447 +v -0.100372 -0.843506 0.702057 +v -0.076599 -0.867340 0.694031 +v -0.063934 -0.857086 0.692627 +v 0.028055 -0.878609 0.688099 +v 0.031815 -0.884178 0.687103 +v 0.041878 -0.875076 0.689621 +v 0.025841 -0.874153 0.685654 +v 0.014069 -0.880447 0.687286 +v 0.015991 -0.886047 0.686218 +v 0.047302 -0.880646 0.688660 +v 0.038666 -0.870636 0.687012 +v 0.012939 -0.875977 0.684998 +v -0.028055 -0.878609 0.688099 +v -0.041878 -0.875076 0.689621 +v -0.031815 -0.884178 0.687103 +v -0.014069 -0.880447 0.687286 +v -0.025841 -0.874153 0.685654 +v -0.038666 -0.870636 0.687012 +v -0.047302 -0.880646 0.688660 +v -0.015991 -0.886047 0.686218 +v -0.012939 -0.875977 0.684998 +v 0.023897 -0.861345 0.660884 +v 0.023918 -0.866371 0.670944 +v 0.035774 -0.857994 0.661634 +v 0.024796 -0.854034 0.651792 +v 0.011963 -0.863144 0.660614 +v 0.011963 -0.868164 0.670593 +v 0.035858 -0.862946 0.671875 +v 0.036926 -0.850769 0.652395 +v 0.012451 -0.855896 0.651550 +v -0.023897 -0.861345 0.660884 +v -0.035774 -0.857994 0.661634 +v -0.023918 -0.866371 0.670944 +v -0.011963 -0.863144 0.660614 +v -0.024796 -0.854034 0.651792 +v -0.036926 -0.850769 0.652395 +v -0.035858 -0.862946 0.671875 +v -0.011963 -0.868164 0.670593 +v -0.012451 -0.855896 0.651550 +v 0.070070 -0.836320 0.668723 +v 0.070778 -0.840004 0.680214 +v 0.079313 -0.825144 0.672794 +v 0.070341 -0.831446 0.658130 +v 0.059242 -0.845573 0.665469 +v 0.059662 -0.849884 0.676514 +v 0.080383 -0.828003 0.684479 +v 0.078668 -0.821696 0.662458 +v 0.060120 -0.839539 0.655284 +v -0.070070 -0.836320 0.668723 +v -0.079313 -0.825144 0.672794 +v -0.070778 -0.840004 0.680214 +v -0.059242 -0.845573 0.665469 +v -0.070341 -0.831446 0.658130 +v -0.078668 -0.821696 0.662458 +v -0.080383 -0.828003 0.684479 +v -0.059662 -0.849884 0.676514 +v -0.060120 -0.839539 0.655284 +v 0.091356 -0.783049 0.685164 +v 0.092751 -0.782204 0.697845 +v 0.087624 -0.770889 0.688510 +v 0.089567 -0.785174 0.672450 +v 0.090666 -0.797465 0.681339 +v 0.092102 -0.797974 0.693634 +v 0.088959 -0.768829 0.701263 +v 0.085754 -0.774689 0.675334 +v 0.088921 -0.797648 0.669904 +v -0.091356 -0.783049 0.685164 +v -0.087624 -0.770889 0.688510 +v -0.092751 -0.782204 0.697845 +v -0.090666 -0.797465 0.681339 +v -0.089567 -0.785174 0.672450 +v -0.085754 -0.774689 0.675334 +v -0.088959 -0.768829 0.701263 +v -0.092102 -0.797974 0.693634 +v -0.088921 -0.797648 0.669904 +v 0.045183 -0.763474 0.690982 +v 0.045891 -0.759956 0.703682 +v 0.023315 -0.766769 0.690216 +v 0.044022 -0.769279 0.677801 +v 0.064156 -0.761368 0.691388 +v 0.065155 -0.757965 0.704071 +v 0.023682 -0.763306 0.702942 +v 0.022705 -0.772522 0.677063 +v 0.062561 -0.766998 0.678182 +v -0.045183 -0.763474 0.690982 +v -0.023315 -0.766769 0.690216 +v -0.045891 -0.759956 0.703682 +v -0.064156 -0.761368 0.691388 +v -0.044022 -0.769279 0.677801 +v -0.022705 -0.772522 0.677063 +v -0.023682 -0.763306 0.702942 +v -0.065155 -0.757965 0.704071 +v -0.062561 -0.766998 0.678182 +v 0.034325 -0.810499 0.648293 +v 0.017578 -0.813110 0.647827 +v 0.030228 -0.827972 0.644824 +v 0.049408 -0.807549 0.648715 +v 0.038467 -0.793297 0.655421 +v 0.019775 -0.796204 0.654846 +v 0.015381 -0.830261 0.644470 +v 0.044006 -0.824890 0.645274 +v 0.054993 -0.790558 0.655802 +v -0.034325 -0.810499 0.648293 +v -0.030228 -0.827972 0.644824 +v -0.017578 -0.813110 0.647827 +v -0.038467 -0.793297 0.655421 +v -0.049408 -0.807549 0.648715 +v -0.044006 -0.824890 0.645274 +v -0.015381 -0.830261 0.644470 +v -0.019775 -0.796204 0.654846 +v -0.054993 -0.790558 0.655802 +v 0.076056 -0.805489 0.649282 +v 0.071259 -0.804832 0.648359 +v 0.074431 -0.814204 0.647825 +v 0.081572 -0.797656 0.652929 +v 0.077209 -0.791901 0.654256 +v 0.066223 -0.818542 0.646047 +v 0.081109 -0.806925 0.652611 +v -0.076056 -0.805489 0.649282 +v -0.074431 -0.814204 0.647825 +v -0.071259 -0.804832 0.648359 +v -0.081572 -0.797656 0.652929 +v -0.081109 -0.806925 0.652611 +v -0.066223 -0.818542 0.646047 +v -0.077209 -0.791901 0.654256 +v 0.162680 0.181686 0.765173 +v 0.149582 0.177116 0.760918 +v 0.168854 0.165359 0.763634 +v 0.171791 0.185127 0.768402 +v 0.157768 0.199203 0.766907 +v 0.144196 0.196075 0.763092 +v 0.156281 0.159363 0.759094 +v 0.177338 0.169556 0.767120 +v 0.167542 0.201813 0.769867 +v -0.162680 0.181686 0.765173 +v -0.168854 0.165359 0.763634 +v -0.149582 0.177116 0.760918 +v -0.157768 0.199203 0.766907 +v -0.171791 0.185127 0.768402 +v -0.177338 0.169556 0.767120 +v -0.156281 0.159363 0.759094 +v -0.144196 0.196075 0.763092 +v -0.167542 0.201813 0.769867 +v 0.151558 0.258635 0.771482 +v 0.136513 0.259651 0.769722 +v 0.152061 0.237778 0.770142 +v 0.163200 0.258408 0.773361 +v 0.152901 0.280281 0.772697 +v 0.137573 0.282593 0.771729 +v 0.137482 0.237457 0.767609 +v 0.163147 0.238556 0.772430 +v 0.164948 0.278961 0.774139 +v -0.151558 0.258635 0.771482 +v -0.152061 0.237778 0.770142 +v -0.136513 0.259651 0.769722 +v -0.152901 0.280281 0.772697 +v -0.163200 0.258408 0.773361 +v -0.163147 0.238556 0.772430 +v -0.137482 0.237457 0.767609 +v -0.137573 0.282593 0.771729 +v -0.164948 0.278961 0.774139 +v 0.169922 0.347494 0.775753 +v 0.155823 0.353516 0.776787 +v 0.161964 0.325172 0.774925 +v 0.181702 0.342247 0.775932 +v 0.180199 0.368759 0.776085 +v 0.167114 0.376099 0.777557 +v 0.147095 0.329956 0.775391 +v 0.174103 0.321320 0.775482 +v 0.191376 0.362122 0.776001 +v -0.169922 0.347494 0.775753 +v -0.161964 0.325172 0.774925 +v -0.155823 0.353516 0.776787 +v -0.180199 0.368759 0.776085 +v -0.181702 0.342247 0.775932 +v -0.174103 0.321320 0.775482 +v -0.147095 0.329956 0.775391 +v -0.167114 0.376099 0.777557 +v -0.191376 0.362122 0.776001 +v 0.195831 0.123102 0.761213 +v 0.185638 0.113251 0.756767 +v 0.211334 0.110130 0.759460 +v 0.202789 0.129036 0.765625 +v 0.184662 0.136307 0.761955 +v 0.173492 0.127625 0.757385 +v 0.202362 0.099304 0.755035 +v 0.217682 0.116608 0.764252 +v 0.192108 0.141724 0.766022 +v -0.195831 0.123102 0.761213 +v -0.211334 0.110130 0.759460 +v -0.185638 0.113251 0.756767 +v -0.184662 0.136307 0.761955 +v -0.202789 0.129036 0.765625 +v -0.217682 0.116608 0.764252 +v -0.202362 0.099304 0.755035 +v -0.173492 0.127625 0.757385 +v -0.192108 0.141724 0.766022 +v 0.295715 0.070044 0.741734 +v 0.292175 0.057976 0.735573 +v 0.331818 0.060028 0.732864 +v 0.298645 0.078766 0.747841 +v 0.261810 0.082939 0.749695 +v 0.256195 0.070984 0.744415 +v 0.330475 0.048126 0.725708 +v 0.333221 0.069427 0.739441 +v 0.266144 0.090851 0.755341 +v -0.295715 0.070044 0.741734 +v -0.331818 0.060028 0.732864 +v -0.292175 0.057976 0.735573 +v -0.261810 0.082939 0.749695 +v -0.298645 0.078766 0.747841 +v -0.333221 0.069427 0.739441 +v -0.330475 0.048126 0.725708 +v -0.256195 0.070984 0.744415 +v -0.266144 0.090851 0.755341 +v 0.430023 0.062960 0.708277 +v 0.435020 0.053055 0.698753 +v 0.458771 0.075356 0.701012 +v 0.427040 0.072319 0.716141 +v 0.399872 0.055878 0.715836 +v 0.402863 0.045074 0.706909 +v 0.465637 0.066559 0.691132 +v 0.454407 0.083984 0.709106 +v 0.398285 0.065643 0.723328 +v -0.430023 0.062960 0.708277 +v -0.458771 0.075356 0.701012 +v -0.435020 0.053055 0.698753 +v -0.399872 0.055878 0.715836 +v -0.427040 0.072319 0.716141 +v -0.454407 0.083984 0.709106 +v -0.465637 0.066559 0.691132 +v -0.402863 0.045074 0.706909 +v -0.398285 0.065643 0.723328 +v 0.542826 0.137938 0.679276 +v 0.554260 0.133514 0.669991 +v 0.566963 0.163986 0.673225 +v 0.534157 0.142967 0.686760 +v 0.515594 0.113716 0.686333 +v 0.525696 0.107697 0.676605 +v 0.579529 0.161224 0.664490 +v 0.556976 0.167572 0.680267 +v 0.508362 0.120117 0.694214 +v -0.542826 0.137938 0.679276 +v -0.566963 0.163986 0.673225 +v -0.554260 0.133514 0.669991 +v -0.515594 0.113716 0.686333 +v -0.534157 0.142967 0.686760 +v -0.556976 0.167572 0.680267 +v -0.579529 0.161224 0.664490 +v -0.525696 0.107697 0.676605 +v -0.508362 0.120117 0.694214 +v 0.606207 0.242353 0.666210 +v 0.620842 0.244400 0.658905 +v 0.608749 0.266487 0.667183 +v 0.593781 0.241859 0.672562 +v 0.599037 0.216965 0.666603 +v 0.613220 0.217499 0.658875 +v 0.623566 0.269867 0.660339 +v 0.596100 0.264832 0.673431 +v 0.587128 0.217743 0.673065 +v -0.606207 0.242353 0.666210 +v -0.608749 0.266487 0.667183 +v -0.620842 0.244400 0.658905 +v -0.599037 0.216965 0.666603 +v -0.593781 0.241859 0.672562 +v -0.596100 0.264832 0.673431 +v -0.623566 0.269867 0.660339 +v -0.613220 0.217499 0.658875 +v -0.587128 0.217743 0.673065 +v 0.595995 0.328135 0.675100 +v 0.609489 0.333992 0.670326 +v 0.582909 0.345398 0.680649 +v 0.584427 0.323761 0.680466 +v 0.603806 0.309425 0.671486 +v 0.618073 0.314667 0.665955 +v 0.595276 0.351868 0.676483 +v 0.572296 0.340271 0.685760 +v 0.591583 0.305847 0.677216 +v -0.595995 0.328135 0.675100 +v -0.582909 0.345398 0.680649 +v -0.609489 0.333992 0.670326 +v -0.603806 0.309425 0.671486 +v -0.584427 0.323761 0.680466 +v -0.572296 0.340271 0.685760 +v -0.595276 0.351868 0.676483 +v -0.618073 0.314667 0.665955 +v -0.591583 0.305847 0.677216 +v 0.503357 0.391073 0.713556 +v 0.510246 0.400642 0.709160 +v 0.469002 0.404320 0.726433 +v 0.497322 0.383499 0.719528 +v 0.535759 0.376770 0.700424 +v 0.544739 0.385071 0.696381 +v 0.473785 0.415253 0.721802 +v 0.464722 0.395874 0.732758 +v 0.527985 0.370056 0.705902 +v -0.503357 0.391073 0.713556 +v -0.469002 0.404320 0.726433 +v -0.510246 0.400642 0.709160 +v -0.535759 0.376770 0.700424 +v -0.497322 0.383499 0.719528 +v -0.464722 0.395874 0.732758 +v -0.473785 0.415253 0.721802 +v -0.544739 0.385071 0.696381 +v -0.527985 0.370056 0.705902 +v 0.380135 0.436440 0.748350 +v 0.380096 0.451042 0.746086 +v 0.356743 0.443642 0.750946 +v 0.379944 0.425575 0.752808 +v 0.406227 0.427238 0.744164 +v 0.407501 0.440765 0.740601 +v 0.355560 0.459106 0.750092 +v 0.357544 0.432159 0.754181 +v 0.404907 0.417114 0.749725 +v -0.380135 0.436440 0.748350 +v -0.356743 0.443642 0.750946 +v -0.380096 0.451042 0.746086 +v -0.406227 0.427238 0.744164 +v -0.379944 0.425575 0.752808 +v -0.357544 0.432159 0.754181 +v -0.355560 0.459106 0.750092 +v -0.407501 0.440765 0.740601 +v -0.404907 0.417114 0.749725 +v 0.296757 0.449911 0.759260 +v 0.292282 0.466202 0.760735 +v 0.278557 0.446487 0.762779 +v 0.300385 0.437691 0.760437 +v 0.315575 0.450569 0.755951 +v 0.312225 0.466919 0.757050 +v 0.272919 0.462341 0.764404 +v 0.283203 0.434448 0.763763 +v 0.318237 0.438385 0.757477 +v -0.296757 0.449911 0.759260 +v -0.278557 0.446487 0.762779 +v -0.292282 0.466202 0.760735 +v -0.315575 0.450569 0.755951 +v -0.300385 0.437691 0.760437 +v -0.283203 0.434448 0.763763 +v -0.272919 0.462341 0.764404 +v -0.312225 0.466919 0.757050 +v -0.318237 0.438385 0.757477 +v 0.224537 0.419571 0.772213 +v 0.215141 0.431717 0.774071 +v 0.207817 0.405197 0.774345 +v 0.232689 0.409409 0.772423 +v 0.242332 0.431290 0.769463 +v 0.234222 0.445007 0.771240 +v 0.197144 0.415649 0.776215 +v 0.217133 0.396057 0.774292 +v 0.249268 0.420288 0.769989 +v -0.224537 0.419571 0.772213 +v -0.207817 0.405197 0.774345 +v -0.215141 0.431717 0.774071 +v -0.242332 0.431290 0.769463 +v -0.232689 0.409409 0.772423 +v -0.217133 0.396057 0.774292 +v -0.197144 0.415649 0.776215 +v -0.234222 0.445007 0.771240 +v -0.249268 0.420288 0.769989 +v 0.250059 0.388058 0.768858 +v 0.245293 0.393967 0.771477 +v 0.237206 0.376572 0.769073 +v 0.254211 0.382874 0.765381 +v 0.263641 0.397583 0.768150 +v 0.259766 0.403931 0.770111 +v 0.231659 0.381897 0.772339 +v 0.242065 0.371948 0.764893 +v 0.266968 0.391968 0.765381 +v -0.250059 0.388058 0.768858 +v -0.237206 0.376572 0.769073 +v -0.245293 0.393967 0.771477 +v -0.263641 0.397583 0.768150 +v -0.254211 0.382874 0.765381 +v -0.242065 0.371948 0.764893 +v -0.231659 0.381897 0.772339 +v -0.259766 0.403931 0.770111 +v -0.266968 0.391968 0.765381 +v 0.306885 0.413393 0.761459 +v 0.305328 0.420265 0.762390 +v 0.292023 0.410278 0.764275 +v 0.308075 0.407257 0.759766 +v 0.322601 0.414436 0.758858 +v 0.321655 0.421173 0.759796 +v 0.289795 0.417114 0.765350 +v 0.293823 0.404175 0.762451 +v 0.323242 0.408447 0.757080 +v -0.306885 0.413393 0.761459 +v -0.292023 0.410278 0.764275 +v -0.305328 0.420265 0.762390 +v -0.322601 0.414436 0.758858 +v -0.308075 0.407257 0.759766 +v -0.293823 0.404175 0.762451 +v -0.289795 0.417114 0.765350 +v -0.321655 0.421173 0.759796 +v -0.323242 0.408447 0.757080 +v 0.377991 0.405756 0.756111 +v 0.378845 0.410957 0.757629 +v 0.357819 0.410561 0.756691 +v 0.377014 0.401398 0.753021 +v 0.400360 0.399109 0.753716 +v 0.401978 0.403687 0.755463 +v 0.358032 0.416412 0.757965 +v 0.357422 0.405518 0.754150 +v 0.398682 0.395386 0.750122 +v -0.377991 0.405756 0.756111 +v -0.357819 0.410561 0.756691 +v -0.378845 0.410957 0.757629 +v -0.400360 0.399109 0.753716 +v -0.377014 0.401398 0.753021 +v -0.357422 0.405518 0.754150 +v -0.358032 0.416412 0.757965 +v -0.401978 0.403687 0.755463 +v -0.398682 0.395386 0.750122 +v 0.481569 0.368893 0.723400 +v 0.486610 0.372757 0.725327 +v 0.453156 0.380493 0.737022 +v 0.476654 0.365631 0.719543 +v 0.508156 0.356186 0.709312 +v 0.514435 0.360046 0.711151 +v 0.456909 0.384399 0.738983 +v 0.449463 0.377319 0.733032 +v 0.502075 0.352783 0.705688 +v -0.481569 0.368893 0.723400 +v -0.453156 0.380493 0.737022 +v -0.486610 0.372757 0.725327 +v -0.508156 0.356186 0.709312 +v -0.476654 0.365631 0.719543 +v -0.449463 0.377319 0.733032 +v -0.456909 0.384399 0.738983 +v -0.514435 0.360046 0.711151 +v -0.502075 0.352783 0.705688 +v 0.556698 0.314034 0.683895 +v 0.565140 0.317047 0.685410 +v 0.546333 0.328720 0.688896 +v 0.548737 0.311157 0.680786 +v 0.562782 0.298386 0.681084 +v 0.571442 0.300720 0.682465 +v 0.554352 0.332214 0.690521 +v 0.538696 0.325439 0.685669 +v 0.554688 0.296143 0.678101 +v -0.556698 0.314034 0.683895 +v -0.546333 0.328720 0.688896 +v -0.565140 0.317047 0.685410 +v -0.562782 0.298386 0.681084 +v -0.548737 0.311157 0.680786 +v -0.538696 0.325439 0.685669 +v -0.554352 0.332214 0.690521 +v -0.571442 0.300720 0.682465 +v -0.554688 0.296143 0.678101 +v 0.564873 0.243647 0.677670 +v 0.573517 0.242867 0.678421 +v 0.566719 0.263229 0.678185 +v 0.556824 0.244537 0.675446 +v 0.559349 0.222954 0.678490 +v 0.567719 0.221039 0.679047 +v 0.575470 0.263611 0.679169 +v 0.558594 0.262939 0.675659 +v 0.551514 0.224976 0.676514 +v -0.564873 0.243647 0.677670 +v -0.566719 0.263229 0.678185 +v -0.573517 0.242867 0.678421 +v -0.559349 0.222954 0.678490 +v -0.556824 0.244537 0.675446 +v -0.558594 0.262939 0.675659 +v -0.575470 0.263611 0.679169 +v -0.567719 0.221039 0.679047 +v -0.551514 0.224976 0.676514 +v 0.514406 0.156858 0.692991 +v 0.520576 0.152641 0.693565 +v 0.533867 0.178917 0.686272 +v 0.508484 0.160828 0.690826 +v 0.492264 0.136261 0.700562 +v 0.497375 0.131592 0.701294 +v 0.540985 0.175262 0.686737 +v 0.527100 0.182495 0.684326 +v 0.487305 0.140503 0.698120 +v -0.514406 0.156858 0.692991 +v -0.533867 0.178917 0.686272 +v -0.520576 0.152641 0.693565 +v -0.492264 0.136261 0.700562 +v -0.508484 0.160828 0.690826 +v -0.527100 0.182495 0.684326 +v -0.540985 0.175262 0.686737 +v -0.497375 0.131592 0.701294 +v -0.487305 0.140503 0.698120 +v 0.421144 0.093580 0.721745 +v 0.423103 0.087669 0.722916 +v 0.445328 0.103912 0.715149 +v 0.419189 0.098785 0.718750 +v 0.395500 0.087547 0.728348 +v 0.396454 0.081390 0.729675 +v 0.448303 0.098389 0.716187 +v 0.442383 0.108765 0.712280 +v 0.394531 0.093018 0.725220 +v -0.421144 0.093580 0.721745 +v -0.445328 0.103912 0.715149 +v -0.423103 0.087669 0.722916 +v -0.395500 0.087547 0.728348 +v -0.419189 0.098785 0.718750 +v -0.442383 0.108765 0.712280 +v -0.448303 0.098389 0.716187 +v -0.396454 0.081390 0.729675 +v -0.394531 0.093018 0.725220 +v 0.305481 0.096802 0.750551 +v 0.303406 0.091400 0.752693 +v 0.336876 0.089592 0.742996 +v 0.307434 0.101837 0.746582 +v 0.275795 0.106575 0.757133 +v 0.272858 0.101715 0.759613 +v 0.335785 0.083710 0.744812 +v 0.337891 0.094971 0.739380 +v 0.278564 0.111206 0.752808 +v -0.305481 0.096802 0.750551 +v -0.336876 0.089592 0.742996 +v -0.303406 0.091400 0.752693 +v -0.275795 0.106575 0.757133 +v -0.307434 0.101837 0.746582 +v -0.337891 0.094971 0.739380 +v -0.335785 0.083710 0.744812 +v -0.272858 0.101715 0.759613 +v -0.278564 0.111206 0.752808 +v 0.216736 0.140863 0.764282 +v 0.212494 0.136940 0.767578 +v 0.230843 0.129128 0.764000 +v 0.220734 0.144775 0.759277 +v 0.206657 0.153076 0.763588 +v 0.202240 0.149292 0.767120 +v 0.226837 0.125031 0.767059 +v 0.234619 0.133179 0.759155 +v 0.210815 0.156860 0.758423 +v -0.216736 0.140863 0.764282 +v -0.230843 0.129128 0.764000 +v -0.212494 0.136940 0.767578 +v -0.206657 0.153076 0.763588 +v -0.220734 0.144775 0.759277 +v -0.234619 0.133179 0.759155 +v -0.226837 0.125031 0.767059 +v -0.202240 0.149292 0.767120 +v -0.210815 0.156860 0.758423 +v 0.205391 0.331785 0.768919 +v 0.199097 0.334465 0.773094 +v 0.197731 0.314400 0.768700 +v 0.210724 0.329590 0.763641 +v 0.214653 0.348190 0.769012 +v 0.208344 0.351868 0.773071 +v 0.191559 0.316071 0.772919 +v 0.202881 0.313110 0.763306 +v 0.220093 0.345093 0.763916 +v -0.205391 0.331785 0.768919 +v -0.197731 0.314400 0.768700 +v -0.199097 0.334465 0.773094 +v -0.214653 0.348190 0.769012 +v -0.210724 0.329590 0.763641 +v -0.202881 0.313110 0.763306 +v -0.191559 0.316071 0.772919 +v -0.208344 0.351868 0.773071 +v -0.220093 0.345093 0.763916 +v 0.185858 0.260014 0.766788 +v 0.179710 0.259262 0.771408 +v 0.185196 0.242226 0.765831 +v 0.191162 0.260895 0.760712 +v 0.187996 0.278175 0.767632 +v 0.181915 0.278107 0.772064 +v 0.179016 0.240875 0.770599 +v 0.190674 0.243652 0.759521 +v 0.193115 0.278442 0.761841 +v -0.185858 0.260014 0.766788 +v -0.185196 0.242226 0.765831 +v -0.179710 0.259262 0.771408 +v -0.187996 0.278175 0.767632 +v -0.191162 0.260895 0.760712 +v -0.190674 0.243652 0.759521 +v -0.179016 0.240875 0.770599 +v -0.181915 0.278107 0.772064 +v -0.193115 0.278442 0.761841 +v 0.189936 0.193789 0.763062 +v 0.184517 0.190895 0.767487 +v 0.193932 0.179443 0.762642 +v 0.194977 0.196686 0.757050 +v 0.187271 0.208992 0.763847 +v 0.181458 0.206573 0.768524 +v 0.188934 0.176147 0.766754 +v 0.198608 0.182739 0.756958 +v 0.192627 0.211426 0.757568 +v -0.189936 0.193789 0.763062 +v -0.193932 0.179443 0.762642 +v -0.184517 0.190895 0.767487 +v -0.187271 0.208992 0.763847 +v -0.194977 0.196686 0.757050 +v -0.198608 0.182739 0.756958 +v -0.188934 0.176147 0.766754 +v -0.181458 0.206573 0.768524 +v -0.192627 0.211426 0.757568 +v 0.043276 0.451839 0.676824 +v 0.039986 0.452339 0.707779 +v 0.066345 0.471651 0.678061 +v 0.047060 0.451939 0.647467 +v 0.021352 0.440521 0.675720 +v 0.019897 0.440369 0.706421 +v 0.060455 0.472992 0.709656 +v 0.075529 0.470252 0.647119 +v 0.022542 0.441833 0.646851 +v -0.043276 0.451839 0.676824 +v -0.066345 0.471651 0.678061 +v -0.039986 0.452339 0.707779 +v -0.021352 0.440521 0.675720 +v -0.047060 0.451939 0.647467 +v -0.075529 0.470252 0.647119 +v -0.060455 0.472992 0.709656 +v -0.019897 0.440369 0.706421 +v -0.022542 0.441833 0.646851 +v 0.136071 0.582881 0.684044 +v 0.126640 0.586479 0.715324 +v 0.160395 0.624624 0.685371 +v 0.153851 0.577931 0.654639 +v 0.112772 0.539664 0.681906 +v 0.103302 0.542450 0.713806 +v 0.152283 0.629639 0.715851 +v 0.174845 0.616414 0.658447 +v 0.130501 0.536130 0.650537 +v -0.136071 0.582881 0.684044 +v -0.160395 0.624624 0.685371 +v -0.126640 0.586479 0.715324 +v -0.112772 0.539664 0.681906 +v -0.153851 0.577931 0.654639 +v -0.174845 0.616414 0.658447 +v -0.152283 0.629639 0.715851 +v -0.103302 0.542450 0.713806 +v -0.130501 0.536130 0.650537 +v 0.253725 0.697434 0.676595 +v 0.249138 0.706261 0.707443 +v 0.290416 0.700178 0.670882 +v 0.259657 0.680644 0.648963 +v 0.218989 0.684163 0.681618 +v 0.213562 0.692139 0.712067 +v 0.286896 0.709076 0.701202 +v 0.293715 0.683919 0.644925 +v 0.226847 0.669027 0.655029 +v -0.253725 0.697434 0.676595 +v -0.290416 0.700178 0.670882 +v -0.249138 0.706261 0.707443 +v -0.218989 0.684163 0.681618 +v -0.259657 0.680644 0.648963 +v -0.293715 0.683919 0.644925 +v -0.286896 0.709076 0.701202 +v -0.213562 0.692139 0.712067 +v -0.226847 0.669027 0.655029 +v 0.404939 0.645821 0.644101 +v 0.406456 0.653717 0.674179 +v 0.446495 0.614126 0.631902 +v 0.395183 0.633182 0.618290 +v 0.365886 0.672895 0.654830 +v 0.365631 0.681122 0.684479 +v 0.449158 0.621735 0.662628 +v 0.434478 0.601860 0.603750 +v 0.360609 0.659383 0.630642 +v -0.404939 0.645821 0.644101 +v -0.446495 0.614126 0.631902 +v -0.406456 0.653717 0.674179 +v -0.365886 0.672895 0.654830 +v -0.395183 0.633182 0.618290 +v -0.434478 0.601860 0.603750 +v -0.449158 0.621735 0.662628 +v -0.365631 0.681122 0.684479 +v -0.360609 0.659383 0.630642 +v 0.589727 0.523550 0.589155 +v 0.592567 0.528320 0.621498 +v 0.637665 0.498596 0.574966 +v 0.583603 0.516073 0.555067 +v 0.539970 0.551169 0.603918 +v 0.542908 0.557159 0.635895 +v 0.641327 0.502411 0.607422 +v 0.631500 0.492462 0.540100 +v 0.531035 0.541558 0.571279 +v -0.589727 0.523550 0.589155 +v -0.637665 0.498596 0.574966 +v -0.592567 0.528320 0.621498 +v -0.539970 0.551169 0.603918 +v -0.583603 0.516073 0.555067 +v -0.631500 0.492462 0.540100 +v -0.641327 0.502411 0.607422 +v -0.542908 0.557159 0.635895 +v -0.531035 0.541558 0.571279 +v 0.756081 0.440042 0.540770 +v 0.762344 0.442253 0.572060 +v 0.782715 0.416725 0.528481 +v 0.745865 0.435959 0.506790 +v 0.722198 0.458679 0.551346 +v 0.727875 0.461273 0.583252 +v 0.789276 0.418579 0.558990 +v 0.771759 0.413361 0.495361 +v 0.713165 0.454010 0.516663 +v -0.756081 0.440042 0.540770 +v -0.782715 0.416725 0.528481 +v -0.762344 0.442253 0.572060 +v -0.722198 0.458679 0.551346 +v -0.745865 0.435959 0.506790 +v -0.771759 0.413361 0.495361 +v -0.789276 0.418579 0.558990 +v -0.727875 0.461273 0.583252 +v -0.713165 0.454010 0.516663 +v 0.807924 0.285683 0.468472 +v 0.813675 0.285866 0.496284 +v 0.796822 0.228050 0.448769 +v 0.799004 0.286552 0.437050 +v 0.809265 0.339363 0.491158 +v 0.815521 0.340210 0.519806 +v 0.801971 0.227570 0.476074 +v 0.789612 0.230530 0.417145 +v 0.798981 0.338531 0.459473 +v -0.807924 0.285683 0.468472 +v -0.796822 0.228050 0.448769 +v -0.813675 0.285866 0.496284 +v -0.809265 0.339363 0.491158 +v -0.799004 0.286552 0.437050 +v -0.789612 0.230530 0.417145 +v -0.801971 0.227570 0.476074 +v -0.815521 0.340210 0.519806 +v -0.798981 0.338531 0.459473 +v 0.707251 0.072214 0.442305 +v 0.710892 0.070419 0.471397 +v 0.664291 0.030380 0.453781 +v 0.704903 0.076591 0.406708 +v 0.745277 0.119072 0.435310 +v 0.749359 0.117584 0.463379 +v 0.667450 0.028351 0.483734 +v 0.663055 0.034576 0.416840 +v 0.741516 0.123352 0.401398 +v -0.707251 0.072214 0.442305 +v -0.664291 0.030380 0.453781 +v -0.710892 0.070419 0.471397 +v -0.745277 0.119072 0.435310 +v -0.704903 0.076591 0.406708 +v -0.663055 0.034576 0.416840 +v -0.667450 0.028351 0.483734 +v -0.749359 0.117584 0.463379 +v -0.741516 0.123352 0.401398 +v 0.526698 -0.066154 0.484599 +v 0.527718 -0.068672 0.512405 +v 0.478083 -0.090466 0.492487 +v 0.525973 -0.062763 0.450525 +v 0.573288 -0.038406 0.476181 +v 0.575043 -0.040863 0.505585 +v 0.478699 -0.092712 0.518158 +v 0.475627 -0.087998 0.462531 +v 0.573090 -0.034637 0.439667 +v -0.526698 -0.066154 0.484599 +v -0.478083 -0.090466 0.492487 +v -0.527718 -0.068672 0.512405 +v -0.573288 -0.038406 0.476181 +v -0.525973 -0.062763 0.450525 +v -0.475627 -0.087998 0.462531 +v -0.478699 -0.092712 0.518158 +v -0.575043 -0.040863 0.505585 +v -0.573090 -0.034637 0.439667 +v 0.075544 -0.503523 0.305014 +v 0.038269 -0.511505 0.302521 +v 0.080856 -0.478897 0.287445 +v 0.110832 -0.492332 0.310600 +v 0.070930 -0.534462 0.320183 +v 0.035767 -0.541443 0.316528 +v 0.041138 -0.487976 0.286133 +v 0.117737 -0.466187 0.292358 +v 0.104889 -0.524628 0.326508 +v -0.075544 -0.503523 0.305014 +v -0.080856 -0.478897 0.287445 +v -0.038269 -0.511505 0.302521 +v -0.070930 -0.534462 0.320183 +v -0.110832 -0.492332 0.310600 +v -0.117737 -0.466187 0.292358 +v -0.041138 -0.487976 0.286133 +v -0.035767 -0.541443 0.316528 +v -0.104889 -0.524628 0.326508 +v 0.067045 -0.667839 0.348866 +v 0.033508 -0.674057 0.344330 +v 0.066566 -0.617622 0.341331 +v 0.100639 -0.659042 0.355087 +v 0.068680 -0.720360 0.357109 +v 0.034302 -0.726807 0.353271 +v 0.033325 -0.623718 0.336548 +v 0.099640 -0.608978 0.348114 +v 0.103210 -0.711273 0.362274 +v -0.067045 -0.667839 0.348866 +v -0.066566 -0.617622 0.341331 +v -0.033508 -0.674057 0.344330 +v -0.068680 -0.720360 0.357109 +v -0.100639 -0.659042 0.355087 +v -0.099640 -0.608978 0.348114 +v -0.033325 -0.623718 0.336548 +v -0.034302 -0.726807 0.353271 +v -0.103210 -0.711273 0.362274 +v 0.076223 -0.866589 0.403757 +v 0.038086 -0.872620 0.403900 +v 0.073563 -0.821953 0.383934 +v 0.114464 -0.858139 0.403267 +v 0.078865 -0.904655 0.426773 +v 0.039429 -0.910156 0.428345 +v 0.036743 -0.828369 0.382568 +v 0.110535 -0.812958 0.385590 +v 0.118317 -0.896942 0.424255 +v -0.076223 -0.866589 0.403757 +v -0.073563 -0.821953 0.383934 +v -0.038086 -0.872620 0.403900 +v -0.078865 -0.904655 0.426773 +v -0.114464 -0.858139 0.403267 +v -0.110535 -0.812958 0.385590 +v -0.036743 -0.828369 0.382568 +v -0.039429 -0.910156 0.428345 +v -0.118317 -0.896942 0.424255 +v 0.085375 -0.964533 0.506567 +v 0.086517 -0.968521 0.533333 +v 0.042816 -0.968292 0.510254 +v 0.083595 -0.953697 0.479141 +v 0.127419 -0.959175 0.501122 +v 0.128784 -0.963806 0.527985 +v 0.043457 -0.971802 0.536987 +v 0.041870 -0.958008 0.482544 +v 0.125031 -0.947601 0.474060 +v -0.085375 -0.964533 0.506567 +v -0.042816 -0.968292 0.510254 +v -0.086517 -0.968521 0.533333 +v -0.127419 -0.959175 0.501122 +v -0.083595 -0.953697 0.479141 +v -0.041870 -0.958008 0.482544 +v -0.043457 -0.971802 0.536987 +v -0.128784 -0.963806 0.527985 +v -0.125031 -0.947601 0.474060 +v 0.245768 -0.938317 0.479853 +v 0.245552 -0.944992 0.507462 +v 0.208931 -0.946388 0.487053 +v 0.242890 -0.924855 0.454310 +v 0.276826 -0.927365 0.473742 +v 0.276764 -0.935059 0.501251 +v 0.209351 -0.952332 0.514435 +v 0.206451 -0.933319 0.460632 +v 0.271946 -0.914049 0.450358 +v -0.245768 -0.938317 0.479853 +v -0.208931 -0.946388 0.487053 +v -0.245552 -0.944992 0.507462 +v -0.276826 -0.927365 0.473742 +v -0.242890 -0.924855 0.454310 +v -0.206451 -0.933319 0.460632 +v -0.209351 -0.952332 0.514435 +v -0.276764 -0.935059 0.501251 +v -0.271946 -0.914049 0.450358 +v 0.321346 -0.862075 0.462102 +v 0.326668 -0.879181 0.493126 +v 0.314576 -0.890011 0.464617 +v 0.308205 -0.844143 0.435481 +v 0.322495 -0.828186 0.461670 +v 0.330566 -0.850220 0.494324 +v 0.317413 -0.902832 0.493805 +v 0.304172 -0.874986 0.440837 +v 0.306580 -0.805786 0.432922 +v -0.321346 -0.862075 0.462102 +v -0.314576 -0.890011 0.464617 +v -0.326668 -0.879181 0.493126 +v -0.322495 -0.828186 0.461670 +v -0.308205 -0.844143 0.435481 +v -0.304172 -0.874986 0.440837 +v -0.317413 -0.902832 0.493805 +v -0.330566 -0.850220 0.494324 +v -0.306580 -0.805786 0.432922 +v 0.310318 -0.698254 0.470505 +v 0.324234 -0.730545 0.507393 +v 0.315781 -0.745361 0.466309 +v 0.288986 -0.666351 0.436760 +v 0.304031 -0.648849 0.475037 +v 0.318665 -0.681824 0.513031 +v 0.328369 -0.775513 0.501892 +v 0.295593 -0.715210 0.434143 +v 0.282227 -0.616699 0.439850 +v -0.310318 -0.698254 0.470505 +v -0.315781 -0.745361 0.466309 +v -0.324234 -0.730545 0.507393 +v -0.304031 -0.648849 0.475037 +v -0.288986 -0.666351 0.436760 +v -0.295593 -0.715210 0.434143 +v -0.328369 -0.775513 0.501892 +v -0.318665 -0.681824 0.513031 +v -0.282227 -0.616699 0.439850 +v 0.201250 -0.631351 0.377092 +v 0.207367 -0.682823 0.379738 +v 0.168114 -0.638412 0.368576 +v 0.196632 -0.581596 0.374481 +v 0.232956 -0.631256 0.389793 +v 0.239929 -0.682251 0.390686 +v 0.173035 -0.690155 0.372894 +v 0.164948 -0.588593 0.364105 +v 0.227051 -0.581543 0.389191 +v -0.201250 -0.631351 0.377092 +v -0.168114 -0.638412 0.368576 +v -0.207367 -0.682823 0.379738 +v -0.232956 -0.631256 0.389793 +v -0.196632 -0.581596 0.374481 +v -0.164948 -0.588593 0.364105 +v -0.173035 -0.690155 0.372894 +v -0.239929 -0.682251 0.390686 +v -0.227051 -0.581543 0.389191 +v 0.228111 -0.831111 0.400490 +v 0.260659 -0.827450 0.404719 +v 0.233582 -0.871144 0.414210 +v 0.191460 -0.838791 0.400490 +v 0.221466 -0.784721 0.390511 +v 0.254822 -0.782593 0.397278 +v 0.263401 -0.864610 0.415934 +v 0.197296 -0.879242 0.416687 +v 0.185242 -0.792328 0.387909 +v -0.228111 -0.831111 0.400490 +v -0.233582 -0.871144 0.414210 +v -0.260659 -0.827450 0.404719 +v -0.221466 -0.784721 0.390511 +v -0.191460 -0.838791 0.400490 +v -0.197296 -0.879242 0.416687 +v -0.263401 -0.864610 0.415934 +v -0.254822 -0.782593 0.397278 +v -0.185242 -0.792328 0.387909 +v 0.196812 -0.456774 0.355516 +v 0.220146 -0.452835 0.380783 +v 0.194527 -0.493431 0.363983 +v 0.171471 -0.466667 0.335350 +v 0.200142 -0.425339 0.345924 +v 0.220428 -0.419006 0.376007 +v 0.220703 -0.491455 0.384918 +v 0.166779 -0.501801 0.347870 +v 0.177307 -0.437134 0.321167 +v -0.196812 -0.456774 0.355516 +v -0.194527 -0.493431 0.363983 +v -0.220146 -0.452835 0.380783 +v -0.200142 -0.425339 0.345924 +v -0.171471 -0.466667 0.335350 +v -0.166779 -0.501801 0.347870 +v -0.220703 -0.491455 0.384918 +v -0.220428 -0.419006 0.376007 +v -0.177307 -0.437134 0.321167 +v 0.284008 -0.498650 0.484663 +v 0.297325 -0.524307 0.524178 +v 0.290726 -0.547653 0.482361 +v 0.264732 -0.474808 0.446335 +v 0.277069 -0.452927 0.486320 +v 0.289093 -0.474243 0.525696 +v 0.304993 -0.577087 0.521698 +v 0.270020 -0.519775 0.444855 +v 0.259613 -0.433655 0.447418 +v -0.284008 -0.498650 0.484663 +v -0.290726 -0.547653 0.482361 +v -0.297325 -0.524307 0.524178 +v -0.277069 -0.452927 0.486320 +v -0.264732 -0.474808 0.446335 +v -0.270020 -0.519775 0.444855 +v -0.304993 -0.577087 0.521698 +v -0.289093 -0.474243 0.525696 +v -0.259613 -0.433655 0.447418 +v 0.232539 -0.266420 0.502273 +v 0.236686 -0.283499 0.496721 +v 0.236780 -0.256163 0.472836 +v 0.228381 -0.250931 0.507975 +v 0.231949 -0.273140 0.531860 +v 0.237640 -0.290588 0.529358 +v 0.237913 -0.274072 0.461434 +v 0.234111 -0.240814 0.483134 +v 0.226776 -0.257050 0.535095 +v -0.232539 -0.266420 0.502273 +v -0.236780 -0.256163 0.472836 +v -0.236686 -0.283499 0.496721 +v -0.231949 -0.273140 0.531860 +v -0.228381 -0.250931 0.507975 +v -0.234111 -0.240814 0.483134 +v -0.237913 -0.274072 0.461434 +v -0.237640 -0.290588 0.529358 +v -0.226776 -0.257050 0.535095 +v 0.254331 -0.349107 0.489352 +v 0.261993 -0.377975 0.488487 +v 0.243797 -0.341025 0.449869 +v 0.247398 -0.324240 0.490282 +v 0.261444 -0.359482 0.526703 +v 0.270905 -0.391235 0.526672 +v 0.248749 -0.367249 0.449249 +v 0.240599 -0.316675 0.449837 +v 0.252411 -0.332825 0.526917 +v -0.254331 -0.349107 0.489352 +v -0.243797 -0.341025 0.449869 +v -0.261993 -0.377975 0.488487 +v -0.261444 -0.359482 0.526703 +v -0.247398 -0.324240 0.490282 +v -0.240599 -0.316675 0.449837 +v -0.248749 -0.367249 0.449249 +v -0.270905 -0.391235 0.526672 +v -0.252411 -0.332825 0.526917 +v 0.218145 -0.207146 0.523665 +v 0.214432 -0.209770 0.547310 +v 0.219175 -0.221695 0.519124 +v 0.224555 -0.200968 0.505967 +v 0.225142 -0.192583 0.526544 +v 0.220673 -0.193970 0.549652 +v 0.216156 -0.225677 0.543396 +v 0.225484 -0.213837 0.499898 +v 0.232142 -0.188178 0.510136 +v -0.218145 -0.207146 0.523665 +v -0.219175 -0.221695 0.519124 +v -0.214432 -0.209770 0.547310 +v -0.225142 -0.192583 0.526544 +v -0.224555 -0.200968 0.505967 +v -0.225484 -0.213837 0.499898 +v -0.216156 -0.225677 0.543396 +v -0.220673 -0.193970 0.549652 +v -0.232142 -0.188178 0.510136 +v 0.321769 -0.147392 0.517473 +v 0.277205 -0.163011 0.523553 +v 0.320273 -0.145765 0.501000 +v 0.373357 -0.130535 0.509312 +v 0.319954 -0.148148 0.538986 +v 0.273773 -0.163452 0.545380 +v 0.279871 -0.160957 0.508305 +v 0.369710 -0.129136 0.489183 +v 0.372742 -0.131775 0.531464 +v -0.321769 -0.147392 0.517473 +v -0.320273 -0.145765 0.501000 +v -0.277205 -0.163011 0.523553 +v -0.319954 -0.148148 0.538986 +v -0.373357 -0.130535 0.509312 +v -0.369710 -0.129136 0.489183 +v -0.279871 -0.160957 0.508305 +v -0.273773 -0.163452 0.545380 +v -0.372742 -0.131775 0.531464 +v 0.166850 -0.049606 -0.668689 +v 0.169505 0.016754 -0.700663 +v 0.241069 -0.044846 -0.641429 +v 0.165660 -0.107758 -0.628306 +v 0.085266 -0.052383 -0.690063 +v 0.086548 0.016968 -0.723267 +v 0.245280 0.016846 -0.671183 +v 0.239665 -0.098572 -0.603190 +v 0.084595 -0.113220 -0.648315 +v -0.166850 -0.049606 -0.668689 +v -0.241069 -0.044846 -0.641429 +v -0.169505 0.016754 -0.700663 +v -0.085266 -0.052383 -0.690063 +v -0.165660 -0.107758 -0.628306 +v -0.239665 -0.098572 -0.603190 +v -0.245280 0.016846 -0.671183 +v -0.086548 0.016968 -0.723267 +v -0.084595 -0.113220 -0.648315 +v 0.156572 -0.254202 -0.449600 +v 0.161245 -0.209198 -0.519338 +v 0.227727 -0.237633 -0.429899 +v 0.150467 -0.294426 -0.369148 +v 0.079712 -0.264526 -0.465179 +v 0.082153 -0.218445 -0.536499 +v 0.234212 -0.194031 -0.497884 +v 0.219177 -0.277008 -0.351593 +v 0.076538 -0.305481 -0.382812 +v -0.156572 -0.254202 -0.449600 +v -0.227727 -0.237633 -0.429899 +v -0.161245 -0.209198 -0.519338 +v -0.079712 -0.264526 -0.465179 +v -0.150467 -0.294426 -0.369148 +v -0.219177 -0.277008 -0.351593 +v -0.234212 -0.194031 -0.497884 +v -0.082153 -0.218445 -0.536499 +v -0.076538 -0.305481 -0.382812 +v 0.125648 -0.380646 -0.068269 +v 0.134598 -0.357628 -0.174477 +v 0.184905 -0.360827 -0.060336 +v 0.116897 -0.398966 0.032628 +v 0.063538 -0.393082 -0.073578 +v 0.068237 -0.369690 -0.182617 +v 0.197205 -0.338654 -0.163727 +v 0.172786 -0.378579 0.041034 +v 0.058960 -0.411621 0.028931 +v -0.125648 -0.380646 -0.068269 +v -0.184905 -0.360827 -0.060336 +v -0.134598 -0.357628 -0.174477 +v -0.063538 -0.393082 -0.073578 +v -0.116897 -0.398966 0.032628 +v -0.172786 -0.378579 0.041034 +v -0.197205 -0.338654 -0.163727 +v -0.068237 -0.369690 -0.182617 +v -0.058960 -0.411621 0.028931 +v 0.095665 -0.435340 0.219087 +v 0.101668 -0.424998 0.178197 +v 0.137908 -0.418864 0.225194 +v 0.090805 -0.446243 0.247101 +v 0.048950 -0.446640 0.218781 +v 0.051636 -0.437012 0.176880 +v 0.148494 -0.406533 0.186297 +v 0.130676 -0.431030 0.252075 +v 0.046509 -0.456970 0.247070 +v -0.095665 -0.435340 0.219087 +v -0.137908 -0.418864 0.225194 +v -0.101668 -0.424998 0.178197 +v -0.048950 -0.446640 0.218781 +v -0.090805 -0.446243 0.247101 +v -0.130676 -0.431030 0.252075 +v -0.148494 -0.406533 0.186297 +v -0.051636 -0.437012 0.176880 +v -0.046509 -0.456970 0.247070 +v 0.210653 -0.361588 0.315624 +v 0.206398 -0.378952 0.326424 +v 0.195271 -0.379801 0.273890 +v 0.218611 -0.344620 0.301917 +v 0.222089 -0.346945 0.361450 +v 0.220795 -0.367249 0.366608 +v 0.188293 -0.395142 0.291626 +v 0.207048 -0.364297 0.249733 +v 0.226642 -0.326685 0.354582 +v -0.210653 -0.361588 0.315624 +v -0.195271 -0.379801 0.273890 +v -0.206398 -0.378952 0.326424 +v -0.222089 -0.346945 0.361450 +v -0.218611 -0.344620 0.301917 +v -0.207048 -0.364297 0.249733 +v -0.188293 -0.395142 0.291626 +v -0.220795 -0.367249 0.366608 +v -0.226642 -0.326685 0.354582 +v 0.736374 0.089799 0.231551 +v 0.719398 0.085838 0.300140 +v 0.698669 0.038717 0.235500 +v 0.754905 0.094526 0.161220 +v 0.765015 0.143478 0.234406 +v 0.751038 0.135803 0.299713 +v 0.680267 0.039581 0.307098 +v 0.718901 0.036675 0.159316 +v 0.780701 0.152954 0.168182 +v -0.736374 0.089799 0.231551 +v -0.698669 0.038717 0.235500 +v -0.719398 0.085838 0.300140 +v -0.765015 0.143478 0.234406 +v -0.754905 0.094526 0.161220 +v -0.718901 0.036675 0.159316 +v -0.680267 0.039581 0.307098 +v -0.751038 0.135803 0.299713 +v -0.780701 0.152954 0.168182 +v 0.195356 0.281429 -0.750453 +v 0.206543 0.384476 -0.743980 +v 0.283376 0.266525 -0.718397 +v 0.184133 0.183136 -0.742940 +v 0.099609 0.292053 -0.774139 +v 0.105347 0.397827 -0.767334 +v 0.299438 0.365662 -0.712494 +v 0.267171 0.173096 -0.710897 +v 0.093872 0.190308 -0.766724 +v -0.195356 0.281429 -0.750453 +v -0.283376 0.266525 -0.718397 +v -0.206543 0.384476 -0.743980 +v -0.099609 0.292053 -0.774139 +v -0.184133 0.183136 -0.742940 +v -0.267171 0.173096 -0.710897 +v -0.299438 0.365662 -0.712494 +v -0.105347 0.397827 -0.767334 +v -0.093872 0.190308 -0.766724 +v 0.222467 0.906395 0.088663 +v 0.222633 0.876961 0.168488 +v 0.323311 0.890091 0.077751 +v 0.222412 0.923302 0.004158 +v 0.113312 0.916031 0.094162 +v 0.113403 0.886292 0.176697 +v 0.323517 0.861298 0.153992 +v 0.323242 0.906586 -0.002655 +v 0.113281 0.933105 0.006592 +v -0.222467 0.906395 0.088663 +v -0.323311 0.890091 0.077751 +v -0.222633 0.876961 0.168488 +v -0.113312 0.916031 0.094162 +v -0.222412 0.923302 0.004158 +v -0.323242 0.906586 -0.002655 +v -0.323517 0.861298 0.153992 +v -0.113403 0.886292 0.176697 +v -0.113281 0.933105 0.006592 +v 0.222464 0.909124 -0.269276 +v 0.222412 0.925163 -0.176170 +v 0.323296 0.892982 -0.259018 +v 0.222618 0.878571 -0.361626 +v 0.113312 0.918198 -0.279755 +v 0.113281 0.934570 -0.181885 +v 0.323242 0.908661 -0.172211 +v 0.323456 0.862488 -0.345306 +v 0.113403 0.887634 -0.376587 +v -0.222464 0.909124 -0.269276 +v -0.323296 0.892982 -0.259018 +v -0.222412 0.925163 -0.176170 +v -0.113312 0.918198 -0.279755 +v -0.222618 0.878571 -0.361626 +v -0.323456 0.862488 -0.345306 +v -0.323242 0.908661 -0.172211 +v -0.113281 0.934570 -0.181885 +v -0.113403 0.887634 -0.376587 +v 0.223112 0.682232 -0.611683 +v 0.223442 0.764313 -0.535912 +v 0.323601 0.662140 -0.583969 +v 0.220886 0.588882 -0.674370 +v 0.113770 0.695328 -0.633759 +v 0.113892 0.775574 -0.556763 +v 0.324310 0.746033 -0.510712 +v 0.320190 0.567566 -0.644989 +v 0.112671 0.603394 -0.697021 +v -0.223112 0.682232 -0.611683 +v -0.323601 0.662140 -0.583969 +v -0.223442 0.764313 -0.535912 +v -0.113770 0.695328 -0.633759 +v -0.220886 0.588882 -0.674370 +v -0.320190 0.567566 -0.644989 +v -0.324310 0.746033 -0.510712 +v -0.113892 0.775574 -0.556763 +v -0.112671 0.603394 -0.697021 +v 0.708225 0.431393 0.390034 +v 0.720291 0.429367 0.431412 +v 0.733482 0.411156 0.382050 +v 0.698792 0.440613 0.346008 +v 0.678764 0.447838 0.395615 +v 0.690094 0.446075 0.438782 +v 0.745270 0.408600 0.422119 +v 0.724792 0.420105 0.339478 +v 0.669495 0.457581 0.349731 +v -0.708225 0.431393 0.390034 +v -0.733482 0.411156 0.382050 +v -0.720291 0.429367 0.431412 +v -0.678764 0.447838 0.395615 +v -0.698792 0.440613 0.346008 +v -0.724792 0.420105 0.339478 +v -0.745270 0.408600 0.422119 +v -0.690094 0.446075 0.438782 +v -0.669495 0.457581 0.349731 +v 0.698740 0.530504 0.197056 +v 0.694275 0.491577 0.249451 +v 0.729317 0.501434 0.195602 +v 0.704933 0.570992 0.142288 +v 0.665375 0.556496 0.196625 +v 0.662903 0.513489 0.250122 +v 0.723083 0.466492 0.246216 +v 0.737152 0.537781 0.142639 +v 0.669495 0.601166 0.140869 +v -0.698740 0.530504 0.197056 +v -0.729317 0.501434 0.195602 +v -0.694275 0.491577 0.249451 +v -0.665375 0.556496 0.196625 +v -0.704933 0.570992 0.142288 +v -0.737152 0.537781 0.142639 +v -0.723083 0.466492 0.246216 +v -0.662903 0.513489 0.250122 +v -0.669495 0.601166 0.140869 +v 0.715889 0.652287 -0.033312 +v 0.714241 0.634438 0.026749 +v 0.749954 0.611969 -0.027893 +v 0.716331 0.661476 -0.094322 +v 0.677322 0.689438 -0.038094 +v 0.676086 0.670380 0.023193 +v 0.748260 0.595398 0.030579 +v 0.750092 0.620544 -0.087463 +v 0.677734 0.699188 -0.100372 +v -0.715889 0.652287 -0.033312 +v -0.749954 0.611969 -0.027893 +v -0.714241 0.634438 0.026749 +v -0.677322 0.689438 -0.038094 +v -0.716331 0.661476 -0.094322 +v -0.750092 0.620544 -0.087463 +v -0.748260 0.595398 0.030579 +v -0.676086 0.670380 0.023193 +v -0.677734 0.699188 -0.100372 +v 0.715253 0.639699 -0.276770 +v 0.715996 0.655647 -0.217461 +v 0.746846 0.599574 -0.265793 +v 0.713376 0.613230 -0.331390 +v 0.677399 0.677605 -0.286964 +v 0.677734 0.693939 -0.225983 +v 0.748749 0.614197 -0.208313 +v 0.742540 0.577555 -0.316772 +v 0.676392 0.649292 -0.344238 +v -0.715253 0.639699 -0.276770 +v -0.746846 0.599574 -0.265793 +v -0.715996 0.655647 -0.217461 +v -0.677399 0.677605 -0.286964 +v -0.713376 0.613230 -0.331390 +v -0.742540 0.577555 -0.316772 +v -0.748749 0.614197 -0.208313 +v -0.677734 0.693939 -0.225983 +v -0.676392 0.649292 -0.344238 +v 0.702647 0.452920 -0.449942 +v 0.693344 0.379573 -0.473598 +v 0.664925 0.477890 -0.476326 +v 0.707821 0.519023 -0.418701 +v 0.735752 0.434256 -0.417804 +v 0.730010 0.364912 -0.438550 +v 0.654694 0.400452 -0.503693 +v 0.671021 0.548218 -0.440186 +v 0.737413 0.495158 -0.392334 +v -0.702647 0.452920 -0.449942 +v -0.664925 0.477890 -0.476326 +v -0.693344 0.379573 -0.473598 +v -0.735752 0.434256 -0.417804 +v -0.707821 0.519023 -0.418701 +v -0.671021 0.548218 -0.440186 +v -0.654694 0.400452 -0.503693 +v -0.730010 0.364912 -0.438550 +v -0.737413 0.495158 -0.392334 +v 0.536285 0.573414 -0.524878 +v 0.480309 0.606171 -0.538864 +v 0.539757 0.654678 -0.469200 +v 0.582924 0.539902 -0.512062 +v 0.528847 0.483459 -0.570496 +v 0.474243 0.513123 -0.591156 +v 0.482635 0.689880 -0.475922 +v 0.587524 0.617798 -0.463867 +v 0.574249 0.453918 -0.550690 +v -0.536285 0.573414 -0.524878 +v -0.539757 0.654678 -0.469200 +v -0.480309 0.606171 -0.538864 +v -0.528847 0.483459 -0.570496 +v -0.582924 0.539902 -0.512062 +v -0.587524 0.617798 -0.463867 +v -0.482635 0.689880 -0.475922 +v -0.474243 0.513123 -0.591156 +v -0.574249 0.453918 -0.550690 +v 0.542112 0.798134 -0.272734 +v 0.483360 0.836433 -0.260788 +v 0.542236 0.813797 -0.202942 +v 0.591583 0.756981 -0.284737 +v 0.541740 0.768387 -0.340996 +v 0.483246 0.806335 -0.334442 +v 0.483398 0.851898 -0.186249 +v 0.591797 0.772919 -0.218781 +v 0.590942 0.727661 -0.348389 +v -0.542112 0.798134 -0.272734 +v -0.542236 0.813797 -0.202942 +v -0.483360 0.836433 -0.260788 +v -0.541740 0.768387 -0.340996 +v -0.591583 0.756981 -0.284737 +v -0.591797 0.772919 -0.218781 +v -0.483398 0.851898 -0.186249 +v -0.483246 0.806335 -0.334442 +v -0.590942 0.727661 -0.348389 +v 0.542219 0.801712 0.006268 +v 0.483437 0.836746 0.034447 +v 0.542168 0.777512 0.072800 +v 0.591690 0.764023 -0.018898 +v 0.542236 0.814835 -0.062500 +v 0.483398 0.851288 -0.037689 +v 0.483551 0.810638 0.103699 +v 0.591370 0.741791 0.045410 +v 0.591797 0.775726 -0.084869 +v -0.542219 0.801712 0.006268 +v -0.542168 0.777512 0.072800 +v -0.483437 0.836746 0.034447 +v -0.542236 0.814835 -0.062500 +v -0.591690 0.764023 -0.018898 +v -0.591370 0.741791 0.045410 +v -0.483551 0.810638 0.103699 +v -0.483398 0.851288 -0.037689 +v -0.591797 0.775726 -0.084869 +v 0.542263 0.633270 0.250738 +v 0.485184 0.657524 0.284599 +v 0.543785 0.578995 0.302589 +v 0.589020 0.607491 0.221771 +v 0.541893 0.689926 0.195358 +v 0.484161 0.717499 0.229187 +v 0.487335 0.600098 0.335480 +v 0.589661 0.556702 0.274780 +v 0.589661 0.660370 0.166016 +v -0.542263 0.633270 0.250738 +v -0.543785 0.578995 0.302589 +v -0.485184 0.657524 0.284599 +v -0.541893 0.689926 0.195358 +v -0.589020 0.607491 0.221771 +v -0.589661 0.556702 0.274780 +v -0.487335 0.600098 0.335480 +v -0.484161 0.717499 0.229187 +v -0.589661 0.660370 0.166016 +v 0.559893 0.500391 0.439969 +v 0.567703 0.501120 0.480634 +v 0.605980 0.481804 0.419113 +v 0.552727 0.510452 0.396919 +v 0.504074 0.517317 0.464300 +v 0.511504 0.520439 0.501821 +v 0.614777 0.481110 0.462219 +v 0.598206 0.492004 0.373413 +v 0.496979 0.527344 0.424713 +v -0.559893 0.500391 0.439969 +v -0.605980 0.481804 0.419113 +v -0.567703 0.501120 0.480634 +v -0.504074 0.517317 0.464300 +v -0.552727 0.510452 0.396919 +v -0.598206 0.492004 0.373413 +v -0.614777 0.481110 0.462219 +v -0.511504 0.520439 0.501821 +v -0.496979 0.527344 0.424713 +v 0.225033 0.708813 0.373446 +v 0.228249 0.643381 0.428871 +v 0.326248 0.696312 0.348900 +v 0.223518 0.774879 0.311371 +v 0.114777 0.715723 0.391098 +v 0.116699 0.645788 0.453369 +v 0.329498 0.633789 0.400787 +v 0.324615 0.760834 0.290222 +v 0.113892 0.783508 0.324890 +v -0.225033 0.708813 0.373446 +v -0.326248 0.696312 0.348900 +v -0.228249 0.643381 0.428871 +v -0.114777 0.715723 0.391098 +v -0.223518 0.774879 0.311371 +v -0.324615 0.760834 0.290222 +v -0.329498 0.633789 0.400787 +v -0.116699 0.645788 0.453369 +v -0.113892 0.783508 0.324890 +v 0.279478 0.593847 0.598597 +v 0.227795 0.582844 0.610419 +v 0.273390 0.626628 0.610511 +v 0.330547 0.605777 0.594639 +v 0.283487 0.561994 0.587711 +v 0.217660 0.547482 0.601831 +v 0.233073 0.616903 0.620117 +v 0.313124 0.635213 0.608059 +v 0.350371 0.575005 0.580557 +v -0.279478 0.593847 0.598597 +v -0.273390 0.626628 0.610511 +v -0.227795 0.582844 0.610419 +v -0.283487 0.561994 0.587711 +v -0.330547 0.605777 0.594639 +v -0.313124 0.635213 0.608059 +v -0.233073 0.616903 0.620117 +v -0.217660 0.547482 0.601831 +v -0.350371 0.575005 0.580557 +v 0.264325 0.532255 0.536906 +v 0.276214 0.527733 0.558414 +v 0.353256 0.534254 0.514013 +v 0.249143 0.551563 0.510231 +v 0.177797 0.524575 0.558295 +v 0.194059 0.514930 0.576807 +v 0.362456 0.534966 0.541128 +v 0.343536 0.549316 0.482697 +v 0.151082 0.546586 0.536743 +v -0.264325 0.532255 0.536906 +v -0.353256 0.534254 0.514013 +v -0.276214 0.527733 0.558414 +v -0.177797 0.524575 0.558295 +v -0.249143 0.551563 0.510231 +v -0.343536 0.549316 0.482697 +v -0.362456 0.534966 0.541128 +v -0.194059 0.514930 0.576807 +v -0.151082 0.546586 0.536743 +v 0.043607 0.481438 0.594865 +v 0.045616 0.467930 0.605139 +v 0.062858 0.494973 0.587744 +v 0.023020 0.483799 0.591553 +v 0.020915 0.462585 0.605347 +v 0.077889 0.481564 0.601343 +v 0.034709 0.509720 0.577271 +v -0.043607 0.481438 0.594865 +v -0.062858 0.494973 0.587744 +v -0.045616 0.467930 0.605139 +v -0.023020 0.483799 0.591553 +v -0.034709 0.509720 0.577271 +v -0.077889 0.481564 0.601343 +v -0.020915 0.462585 0.605347 +v 0.784697 0.301950 0.306030 +v 0.785851 0.293968 0.355354 +v 0.788879 0.251572 0.273224 +v 0.786530 0.313858 0.254997 +v 0.772636 0.346855 0.339325 +v 0.778839 0.341095 0.383301 +v 0.784485 0.242004 0.328644 +v 0.795837 0.264282 0.216400 +v 0.769714 0.357361 0.293335 +v -0.784697 0.301950 0.306030 +v -0.788879 0.251572 0.273224 +v -0.785851 0.293968 0.355354 +v -0.772636 0.346855 0.339325 +v -0.786530 0.313858 0.254997 +v -0.795837 0.264282 0.216400 +v -0.784485 0.242004 0.328644 +v -0.778839 0.341095 0.383301 +v -0.769714 0.357361 0.293335 +v 0.804417 0.377589 0.116296 +v 0.817850 0.322947 0.074697 +v 0.811021 0.402430 0.073255 +v 0.783363 0.425812 0.155380 +v 0.797272 0.352676 0.159630 +v 0.811218 0.300903 0.116669 +v 0.823462 0.344399 0.033193 +v 0.791229 0.454285 0.108948 +v 0.775818 0.397888 0.201050 +v -0.804417 0.377589 0.116296 +v -0.811021 0.402430 0.073255 +v -0.817850 0.322947 0.074697 +v -0.797272 0.352676 0.159630 +v -0.783363 0.425812 0.155380 +v -0.791229 0.454285 0.108948 +v -0.823462 0.344399 0.033193 +v -0.811218 0.300903 0.116669 +v -0.775818 0.397888 0.201050 +v 0.814716 0.454516 -0.064343 +v 0.824522 0.397928 -0.091722 +v 0.811153 0.459579 -0.114929 +v 0.799576 0.512054 -0.041992 +v 0.816484 0.442591 -0.016345 +v 0.826880 0.383950 -0.049082 +v 0.820815 0.403076 -0.137632 +v 0.797333 0.518250 -0.096497 +v 0.799896 0.499207 0.010559 +v -0.814716 0.454516 -0.064343 +v -0.811153 0.459579 -0.114929 +v -0.824522 0.397928 -0.091722 +v -0.816484 0.442591 -0.016345 +v -0.799576 0.512054 -0.041992 +v -0.797333 0.518250 -0.096497 +v -0.820815 0.403076 -0.137632 +v -0.826880 0.383950 -0.049082 +v -0.799896 0.499207 0.010559 +v 0.793396 0.440866 -0.278996 +v 0.781101 0.429953 -0.331592 +v 0.785023 0.498469 -0.266220 +v 0.801174 0.451187 -0.223602 +v 0.799083 0.387798 -0.293734 +v 0.783721 0.372114 -0.347485 +v 0.775255 0.486369 -0.316284 +v 0.790619 0.509949 -0.210510 +v 0.809097 0.397339 -0.239561 +v -0.793396 0.440866 -0.278996 +v -0.785023 0.498469 -0.266220 +v -0.781101 0.429953 -0.331592 +v -0.799083 0.387798 -0.293734 +v -0.801174 0.451187 -0.223602 +v -0.790619 0.509949 -0.210510 +v -0.775255 0.486369 -0.316284 +v -0.783721 0.372114 -0.347485 +v -0.809097 0.397339 -0.239561 +v 0.335531 -0.264199 -0.042689 +v 0.289265 -0.300951 -0.047010 +v 0.348457 -0.250298 -0.134697 +v 0.381268 -0.226669 -0.037891 +v 0.322035 -0.272196 0.047436 +v 0.274797 -0.313516 0.048073 +v 0.303650 -0.283356 -0.142853 +v 0.391449 -0.216858 -0.127869 +v 0.369465 -0.229858 0.052965 +v -0.335531 -0.264199 -0.042689 +v -0.348457 -0.250298 -0.134697 +v -0.289265 -0.300951 -0.047010 +v -0.322035 -0.272196 0.047436 +v -0.381268 -0.226669 -0.037891 +v -0.391449 -0.216858 -0.127869 +v -0.303650 -0.283356 -0.142853 +v -0.274797 -0.313516 0.048073 +v -0.369465 -0.229858 0.052965 +v 0.539732 -0.132518 -0.039605 +v 0.547553 -0.121965 0.036876 +v 0.483879 -0.159958 -0.035714 +v 0.532639 -0.135645 -0.114243 +v 0.593018 -0.107784 -0.045559 +v 0.609648 -0.091011 0.023330 +v 0.482909 -0.154175 0.049466 +v 0.483368 -0.158508 -0.117615 +v 0.580781 -0.116494 -0.112305 +v -0.539732 -0.132518 -0.039605 +v -0.483879 -0.159958 -0.035714 +v -0.547553 -0.121965 0.036876 +v -0.593018 -0.107784 -0.045559 +v -0.532639 -0.135645 -0.114243 +v -0.483368 -0.158508 -0.117615 +v -0.482909 -0.154175 0.049466 +v -0.609648 -0.091011 0.023330 +v -0.580781 -0.116494 -0.112305 +v 0.543101 -0.078233 0.273820 +v 0.534670 -0.067738 0.345239 +v 0.483561 -0.108425 0.293313 +v 0.549842 -0.092119 0.195720 +v 0.600341 -0.045328 0.257534 +v 0.586884 -0.036957 0.330414 +v 0.479045 -0.095811 0.363532 +v 0.483519 -0.125488 0.216458 +v 0.614287 -0.056953 0.177261 +v -0.543101 -0.078233 0.273820 +v -0.483561 -0.108425 0.293313 +v -0.534671 -0.067738 0.345239 +v -0.600341 -0.045328 0.257534 +v -0.549842 -0.092119 0.195720 +v -0.483519 -0.125488 0.216458 +v -0.479045 -0.095811 0.363532 +v -0.586884 -0.036957 0.330414 +v -0.614287 -0.056953 0.177261 +v 0.320245 -0.194068 0.378183 +v 0.319585 -0.179951 0.415799 +v 0.279444 -0.219664 0.410703 +v 0.316009 -0.212164 0.337900 +v 0.369456 -0.166226 0.346169 +v 0.368612 -0.151963 0.398729 +v 0.278707 -0.205048 0.436666 +v 0.276081 -0.238062 0.382414 +v 0.364093 -0.185303 0.289172 +v -0.320245 -0.194068 0.378183 +v -0.279444 -0.219664 0.410703 +v -0.319585 -0.179951 0.415799 +v -0.369456 -0.166226 0.346169 +v -0.316009 -0.212164 0.337900 +v -0.276081 -0.238062 0.382414 +v -0.278707 -0.205048 0.436666 +v -0.368612 -0.151963 0.398729 +v -0.364093 -0.185303 0.289172 +v 0.280374 -0.280050 0.243021 +v 0.257043 -0.301255 0.263078 +v 0.292077 -0.279093 0.197959 +v 0.292764 -0.259467 0.267078 +v 0.262450 -0.281519 0.310555 +v 0.257259 -0.314126 0.205462 +v 0.324461 -0.243164 0.209256 +v -0.280374 -0.280050 0.243021 +v -0.292077 -0.279093 0.197959 +v -0.257043 -0.301255 0.263078 +v -0.292764 -0.259467 0.267078 +v -0.324461 -0.243164 0.209256 +v -0.257259 -0.314126 0.205462 +v -0.262450 -0.281519 0.310555 +v 0.285448 -0.166656 0.477927 +v 0.298778 -0.156019 0.482038 +v 0.261593 -0.177652 0.484046 +v 0.298365 -0.166523 0.467180 +v 0.331095 -0.147202 0.467048 +v 0.268966 -0.168281 0.491053 +v 0.265849 -0.185394 0.473612 +v -0.285448 -0.166656 0.477927 +v -0.261593 -0.177652 0.484046 +v -0.298778 -0.156019 0.482038 +v -0.298365 -0.166523 0.467180 +v -0.265849 -0.185394 0.473612 +v -0.268966 -0.168281 0.491053 +v -0.331095 -0.147202 0.467048 +v 0.627895 0.155444 -0.494093 +v 0.665832 0.145839 -0.463161 +v 0.599698 0.086868 -0.478116 +v 0.588788 0.166733 -0.526029 +v 0.655166 0.228023 -0.497035 +v 0.693877 0.217451 -0.463574 +v 0.638397 0.078796 -0.448639 +v 0.558734 0.093384 -0.510213 +v 0.616241 0.242493 -0.529449 +v -0.627895 0.155444 -0.494093 +v -0.599698 0.086868 -0.478116 +v -0.665832 0.145839 -0.463161 +v -0.655166 0.228023 -0.497035 +v -0.588788 0.166733 -0.526029 +v -0.558734 0.093384 -0.510213 +v -0.638397 0.078796 -0.448639 +v -0.693877 0.217451 -0.463574 +v -0.616241 0.242493 -0.529449 +v 0.470109 0.214859 -0.620608 +v 0.495125 0.300232 -0.618225 +v 0.511568 0.196976 -0.589587 +v 0.443659 0.136597 -0.613475 +v 0.420787 0.232285 -0.651431 +v 0.443726 0.321960 -0.647430 +v 0.538239 0.279358 -0.589630 +v 0.482155 0.118896 -0.579264 +v 0.397135 0.150146 -0.644816 +v -0.470109 0.214859 -0.620608 +v -0.511568 0.196976 -0.589587 +v -0.495125 0.300232 -0.618225 +v -0.420787 0.232285 -0.651431 +v -0.443659 0.136597 -0.613475 +v -0.482155 0.118896 -0.579264 +v -0.538239 0.279358 -0.589630 +v -0.443726 0.321960 -0.647430 +v -0.397135 0.150146 -0.644816 +v 0.540271 -0.073947 -0.333316 +v 0.531723 -0.107264 -0.262558 +v 0.500088 -0.083176 -0.351812 +v 0.554288 -0.029587 -0.396390 +v 0.580796 -0.070887 -0.314217 +v 0.574067 -0.098916 -0.248047 +v 0.490326 -0.121155 -0.276306 +v 0.512957 -0.033813 -0.421468 +v 0.594330 -0.031311 -0.372589 +v -0.540271 -0.073947 -0.333316 +v -0.500088 -0.083176 -0.351812 +v -0.531723 -0.107264 -0.262558 +v -0.580796 -0.070887 -0.314217 +v -0.554288 -0.029587 -0.396390 +v -0.512957 -0.033813 -0.421468 +v -0.490326 -0.121155 -0.276306 +v -0.574067 -0.098916 -0.248047 +v -0.594330 -0.031311 -0.372589 +v 0.384766 -0.157236 -0.386559 +v 0.341517 -0.187675 -0.396929 +v 0.393091 -0.115189 -0.454783 +v 0.423540 -0.126450 -0.377864 +v 0.373634 -0.196129 -0.309349 +v 0.330505 -0.226105 -0.320465 +v 0.349528 -0.146667 -0.463542 +v 0.432961 -0.079224 -0.449992 +v 0.412567 -0.166809 -0.299255 +v -0.384766 -0.157236 -0.386559 +v -0.393091 -0.115189 -0.454783 +v -0.341517 -0.187675 -0.396929 +v -0.373634 -0.196129 -0.309349 +v -0.423540 -0.126450 -0.377864 +v -0.432961 -0.079224 -0.449992 +v -0.349528 -0.146667 -0.463542 +v -0.330505 -0.226105 -0.320465 +v -0.412567 -0.166809 -0.299255 +v 0.381300 -0.019619 -0.580037 +v 0.398167 0.014679 -0.590200 +v 0.391148 -0.043106 -0.554281 +v 0.352681 -0.028976 -0.594350 +v 0.360921 0.019287 -0.616170 +v 0.422992 -0.003540 -0.556478 +v 0.352376 -0.068787 -0.562337 +v -0.381300 -0.019619 -0.580037 +v -0.391148 -0.043106 -0.554281 +v -0.398167 0.014679 -0.590200 +v -0.352681 -0.028976 -0.594350 +v -0.352376 -0.068787 -0.562337 +v -0.422992 -0.003540 -0.556478 +v -0.360921 0.019287 -0.616170 +v 0.964722 0.411856 -0.268616 +v 0.961685 0.421593 -0.278679 +v 0.936974 0.396301 -0.252136 +v 0.967758 0.399170 -0.265755 +v 0.996132 0.425926 -0.287781 +v 0.995178 0.436249 -0.298309 +v 0.932129 0.405396 -0.261719 +v 0.941742 0.384399 -0.249756 +v 0.997162 0.412476 -0.284454 +v -0.964722 0.411856 -0.268616 +v -0.936974 0.396301 -0.252136 +v -0.961685 0.421593 -0.278679 +v -0.996132 0.425926 -0.287781 +v -0.967758 0.399170 -0.265755 +v -0.941742 0.384399 -0.249756 +v -0.932129 0.405396 -0.261719 +v -0.995178 0.436249 -0.298309 +v -0.997162 0.412476 -0.284454 +v 1.115044 0.453634 -0.355694 +v 1.072334 0.448235 -0.332520 +v 1.109619 0.438507 -0.351074 +v 1.157249 0.451874 -0.376755 +v 1.120575 0.465263 -0.366745 +v 1.075745 0.459564 -0.343597 +v 1.069061 0.433472 -0.328278 +v 1.149689 0.436768 -0.371918 +v 1.164856 0.463501 -0.387543 +v -1.115044 0.453634 -0.355694 +v -1.109619 0.438507 -0.351074 +v -1.072334 0.448235 -0.332520 +v -1.120575 0.465263 -0.366745 +v -1.157249 0.451874 -0.376755 +v -1.149689 0.436768 -0.371918 +v -1.069061 0.433472 -0.328278 +v -1.075745 0.459564 -0.343597 +v -1.164856 0.463501 -0.387543 +v 1.255684 0.383139 -0.410851 +v 1.229424 0.416901 -0.404587 +v 1.242073 0.371582 -0.406883 +v 1.274025 0.341118 -0.413986 +v 1.269066 0.391930 -0.419380 +v 1.241028 0.427124 -0.414032 +v 1.217682 0.403564 -0.400116 +v 1.258911 0.331787 -0.410522 +v 1.288849 0.348114 -0.421692 +v -1.255684 0.383139 -0.410851 +v -1.242073 0.371582 -0.406883 +v -1.229424 0.416901 -0.404587 +v -1.269066 0.391930 -0.419380 +v -1.274025 0.341118 -0.413986 +v -1.258911 0.331787 -0.410522 +v -1.217682 0.403564 -0.400116 +v -1.241028 0.427124 -0.414032 +v -1.288849 0.348114 -0.421692 +v 1.272465 0.185665 -0.416355 +v 1.282845 0.240074 -0.416794 +v 1.256508 0.184555 -0.413071 +v 1.252449 0.132225 -0.412567 +v 1.288307 0.186035 -0.423737 +v 1.298981 0.242767 -0.423889 +v 1.266479 0.236084 -0.413696 +v 1.237579 0.133972 -0.408997 +v 1.267334 0.130280 -0.420471 +v -1.272465 0.185665 -0.416355 +v -1.256508 0.184555 -0.413071 +v -1.282845 0.240074 -0.416794 +v -1.288307 0.186035 -0.423737 +v -1.252449 0.132225 -0.412567 +v -1.237579 0.133972 -0.408997 +v -1.266479 0.236084 -0.413696 +v -1.298981 0.242767 -0.423889 +v -1.267334 0.130280 -0.420471 +v 1.138071 0.001753 -0.367073 +v 1.184181 0.038933 -0.388580 +v 1.130302 0.010803 -0.363106 +v 1.086609 -0.029114 -0.339600 +v 1.146103 -0.006233 -0.376793 +v 1.195068 0.032745 -0.397766 +v 1.173492 0.045837 -0.384583 +v 1.082092 -0.018250 -0.335907 +v 1.091492 -0.038635 -0.349640 +v -1.138071 0.001753 -0.367073 +v -1.130302 0.010803 -0.363106 +v -1.184181 0.038933 -0.388580 +v -1.146103 -0.006233 -0.376793 +v -1.086609 -0.029114 -0.339600 +v -1.082092 -0.018250 -0.335907 +v -1.173492 0.045837 -0.384583 +v -1.195068 0.032745 -0.397766 +v -1.091492 -0.038635 -0.349640 +v 0.919634 -0.084345 -0.228821 +v 0.975525 -0.071899 -0.267883 +v 0.925385 -0.070213 -0.227783 +v 0.865852 -0.091530 -0.194153 +v 0.915138 -0.096634 -0.237793 +v 0.974060 -0.083557 -0.277496 +v 0.977844 -0.058533 -0.265717 +v 0.875061 -0.076813 -0.194244 +v 0.858368 -0.104248 -0.202484 +v -0.919634 -0.084345 -0.228821 +v -0.925385 -0.070213 -0.227783 +v -0.975525 -0.071899 -0.267883 +v -0.915138 -0.096634 -0.237793 +v -0.865852 -0.091530 -0.194153 +v -0.875061 -0.076813 -0.194244 +v -0.977844 -0.058533 -0.265717 +v -0.974060 -0.083557 -0.277496 +v -0.858368 -0.104248 -0.202484 +v 0.946169 -0.032106 -0.257074 +v 0.989235 -0.022430 -0.291336 +v 0.952614 -0.023026 -0.272682 +v 0.905426 -0.037636 -0.227196 +v 0.939117 -0.043144 -0.243408 +v 0.984924 -0.032898 -0.278778 +v 0.993500 -0.013855 -0.306000 +v 0.914124 -0.028442 -0.243713 +v 0.895569 -0.048859 -0.212433 +v -0.946169 -0.032106 -0.257074 +v -0.952614 -0.023026 -0.272682 +v -0.989235 -0.022430 -0.291336 +v -0.939117 -0.043144 -0.243408 +v -0.905426 -0.037636 -0.227196 +v -0.914124 -0.028442 -0.243713 +v -0.993500 -0.013855 -0.306000 +v -0.984924 -0.032898 -0.278778 +v -0.895569 -0.048859 -0.212433 +v 1.116014 0.034925 -0.379387 +v 1.152222 0.063866 -0.398758 +v 1.114792 0.040398 -0.390800 +v 1.075752 0.010895 -0.354874 +v 1.118919 0.028015 -0.370155 +v 1.157013 0.058777 -0.390442 +v 1.149628 0.067780 -0.409180 +v 1.076141 0.017639 -0.367401 +v 1.076477 0.002502 -0.344574 +v -1.116014 0.034925 -0.379387 +v -1.114792 0.040398 -0.390800 +v -1.152222 0.063866 -0.398758 +v -1.118919 0.028015 -0.370155 +v -1.075752 0.010895 -0.354874 +v -1.076141 0.017639 -0.367401 +v -1.149628 0.067780 -0.409180 +v -1.157013 0.058777 -0.390442 +v -1.076477 0.002502 -0.344574 +v 1.222504 0.178040 -0.425179 +v 1.231323 0.220345 -0.426041 +v 1.217400 0.175369 -0.434311 +v 1.206299 0.136467 -0.421158 +v 1.230721 0.180588 -0.418015 +v 1.239868 0.225830 -0.418823 +v 1.225952 0.215118 -0.435242 +v 1.201752 0.136261 -0.430420 +v 1.213776 0.136169 -0.413879 +v -1.222504 0.178040 -0.425179 +v -1.217400 0.175369 -0.434311 +v -1.231323 0.220345 -0.426041 +v -1.230721 0.180588 -0.418015 +v -1.206299 0.136467 -0.421158 +v -1.201752 0.136261 -0.430420 +v -1.225952 0.215118 -0.435242 +v -1.239868 0.225830 -0.418823 +v -1.213776 0.136169 -0.413879 +v 1.212877 0.331406 -0.419617 +v 1.192818 0.357628 -0.413254 +v 1.208466 0.319275 -0.429573 +v 1.226318 0.298805 -0.423172 +v 1.219948 0.344727 -0.411858 +v 1.198761 0.372803 -0.405121 +v 1.189209 0.343842 -0.423676 +v 1.221313 0.288727 -0.432800 +v 1.234253 0.309814 -0.415649 +v -1.212877 0.331406 -0.419617 +v -1.208466 0.319275 -0.429573 +v -1.192818 0.357628 -0.413254 +v -1.219948 0.344727 -0.411858 +v -1.226318 0.298805 -0.423172 +v -1.221313 0.288727 -0.432800 +v -1.189209 0.343842 -0.423676 +v -1.198761 0.372803 -0.405121 +v -1.234253 0.309814 -0.415649 +v 1.100037 0.386707 -0.369440 +v 1.064720 0.382835 -0.349113 +v 1.099609 0.371201 -0.382362 +v 1.134865 0.385063 -0.387924 +v 1.101807 0.403778 -0.358948 +v 1.065033 0.399536 -0.337677 +v 1.065430 0.367645 -0.362976 +v 1.133301 0.369598 -0.399872 +v 1.138092 0.402100 -0.378387 +v -1.100037 0.386707 -0.369440 +v -1.099609 0.371201 -0.382362 +v -1.064720 0.382835 -0.349113 +v -1.101807 0.403778 -0.358948 +v -1.134865 0.385063 -0.387924 +v -1.133301 0.369598 -0.399872 +v -1.065430 0.367645 -0.362976 +v -1.065033 0.399536 -0.337677 +v -1.138092 0.402100 -0.378387 +v 0.978037 0.355349 -0.291376 +v 0.974411 0.369873 -0.278358 +v 0.956161 0.343094 -0.276016 +v 0.981682 0.342049 -0.306702 +v 1.002861 0.366142 -0.309044 +v 1.000458 0.381470 -0.296295 +v 0.951385 0.356812 -0.262939 +v 0.960663 0.330505 -0.291321 +v 1.005615 0.352142 -0.324158 +v -0.978037 0.355349 -0.291376 +v -0.956161 0.343094 -0.276016 +v -0.974411 0.369873 -0.278358 +v -1.002861 0.366142 -0.309044 +v -0.981682 0.342049 -0.306702 +v -0.960663 0.330505 -0.291321 +v -0.951385 0.356812 -0.262939 +v -1.000458 0.381470 -0.296295 +v -1.005615 0.352142 -0.324158 +v 0.693755 -0.045513 -0.083906 +v 0.671295 -0.064511 -0.067928 +v 0.685061 -0.060390 -0.116512 +v 0.708582 -0.028306 -0.101070 +v 0.711011 -0.022010 -0.050249 +v 0.692778 -0.040596 -0.021470 +v 0.658418 -0.079751 -0.112061 +v 0.704871 -0.041204 -0.127913 +v 0.721090 -0.006429 -0.075033 +v -0.693755 -0.045513 -0.083906 +v -0.685061 -0.060390 -0.116512 +v -0.671295 -0.064511 -0.067928 +v -0.711011 -0.022010 -0.050249 +v -0.708582 -0.028306 -0.101070 +v -0.704871 -0.041204 -0.127913 +v -0.658418 -0.079751 -0.112061 +v -0.692778 -0.040596 -0.021470 +v -0.721090 -0.006429 -0.075033 +v 0.734344 -0.087164 -0.153912 +v 0.716782 -0.101328 -0.161766 +v 0.771645 -0.092232 -0.157593 +v 0.753921 -0.070327 -0.156396 +v 0.705406 -0.079667 -0.151048 +v 0.681733 -0.096230 -0.158325 +v 0.758148 -0.105469 -0.165497 +v 0.787903 -0.076447 -0.159210 +v 0.727780 -0.060817 -0.155339 +v -0.734344 -0.087164 -0.153912 +v -0.771645 -0.092232 -0.157593 +v -0.716782 -0.101328 -0.161766 +v -0.705406 -0.079667 -0.151048 +v -0.753921 -0.070327 -0.156396 +v -0.787903 -0.076447 -0.159210 +v -0.758148 -0.105469 -0.165497 +v -0.681733 -0.096230 -0.158325 +v -0.727780 -0.060817 -0.155339 +v 0.788081 0.128482 0.005680 +v 0.782280 0.114515 0.046444 +v 0.761660 0.065982 -0.005561 +v 0.789357 0.140625 -0.029462 +v 0.809122 0.195627 0.018392 +v 0.803894 0.179810 0.059174 +v 0.752471 0.050713 0.035781 +v 0.764384 0.078451 -0.039469 +v 0.811499 0.210122 -0.019297 +v -0.788081 0.128482 0.005680 +v -0.761660 0.065982 -0.005561 +v -0.782280 0.114515 0.046444 +v -0.809122 0.195627 0.018392 +v -0.789356 0.140625 -0.029462 +v -0.764384 0.078451 -0.039469 +v -0.752471 0.050713 0.035781 +v -0.803894 0.179810 0.059174 +v -0.811499 0.210122 -0.019297 +v 0.870708 0.344856 -0.220505 +v 0.854603 0.328827 -0.211608 +v 0.877785 0.334442 -0.220352 +v 0.890068 0.362122 -0.228882 +v 0.863346 0.353088 -0.228195 +v 0.847183 0.340454 -0.216001 +v 0.861267 0.317139 -0.213928 +v 0.896942 0.351685 -0.227539 +v 0.883057 0.369751 -0.237793 +v -0.870708 0.344856 -0.220505 +v -0.877785 0.334442 -0.220352 +v -0.854603 0.328827 -0.211608 +v -0.863346 0.353088 -0.228195 +v -0.890068 0.362122 -0.228882 +v -0.896942 0.351685 -0.227539 +v -0.861267 0.317139 -0.213928 +v -0.847183 0.340454 -0.216001 +v -0.883057 0.369751 -0.237793 +v 0.812183 -0.033245 -0.200445 +v 0.837494 -0.037788 -0.198723 +v 0.827484 -0.026794 -0.219879 +v 0.792953 -0.026221 -0.207185 +v 0.794051 -0.042098 -0.182000 +v 0.822083 -0.048004 -0.181793 +v 0.850647 -0.029907 -0.217102 +v 0.810364 -0.021606 -0.227844 +v 0.772092 -0.032985 -0.186344 +v -0.812183 -0.033245 -0.200445 +v -0.827484 -0.026794 -0.219879 +v -0.837494 -0.037788 -0.198723 +v -0.794051 -0.042098 -0.182000 +v -0.792953 -0.026221 -0.207185 +v -0.810364 -0.021606 -0.227844 +v -0.850647 -0.029907 -0.217102 +v -0.822083 -0.048004 -0.181793 +v -0.772092 -0.032985 -0.186344 +v 0.774836 0.004480 -0.225902 +v 0.777012 0.017336 -0.230004 +v 0.753999 0.005857 -0.204093 +v 0.775171 -0.007066 -0.221294 +v 0.795418 0.003151 -0.246635 +v 0.798065 0.014191 -0.249664 +v 0.756273 0.021950 -0.208924 +v 0.753211 -0.008612 -0.198592 +v 0.795227 -0.006470 -0.242737 +v -0.774836 0.004480 -0.225902 +v -0.753999 0.005857 -0.204093 +v -0.777012 0.017336 -0.230004 +v -0.795418 0.003151 -0.246635 +v -0.775171 -0.007066 -0.221294 +v -0.753211 -0.008612 -0.198592 +v -0.756273 0.021950 -0.208924 +v -0.798065 0.014191 -0.249664 +v -0.795227 -0.006470 -0.242737 +v 0.786156 0.059442 -0.253184 +v 0.771852 0.067815 -0.244161 +v 0.783472 0.045636 -0.243360 +v 0.802703 0.054353 -0.263449 +v 0.788154 0.071282 -0.263640 +v 0.776611 0.079102 -0.258362 +v 0.765876 0.053973 -0.229025 +v 0.802582 0.040680 -0.257599 +v 0.801785 0.065891 -0.270318 +v -0.786156 0.059442 -0.253184 +v -0.783472 0.045636 -0.243360 +v -0.771852 0.067815 -0.244161 +v -0.788154 0.071282 -0.263640 +v -0.802703 0.054353 -0.263449 +v -0.802582 0.040680 -0.257599 +v -0.765876 0.053973 -0.229025 +v -0.776611 0.079102 -0.258362 +v -0.801785 0.065891 -0.270318 +v 0.780070 0.128337 -0.235469 +v 0.795906 0.155525 -0.236839 +v 0.773828 0.127377 -0.214483 +v 0.768343 0.102696 -0.235311 +v 0.787247 0.130112 -0.253517 +v 0.803040 0.153503 -0.254242 +v 0.790253 0.159515 -0.216553 +v 0.760627 0.095599 -0.214132 +v 0.775635 0.108643 -0.253479 +v -0.780070 0.128337 -0.235469 +v -0.773828 0.127377 -0.214483 +v -0.795906 0.155525 -0.236839 +v -0.787247 0.130112 -0.253517 +v -0.768343 0.102696 -0.235311 +v -0.760627 0.095599 -0.214132 +v -0.790253 0.159515 -0.216553 +v -0.803040 0.153503 -0.254242 +v -0.775635 0.108643 -0.253479 +v 0.841967 0.226830 -0.239763 +v 0.834785 0.237541 -0.223892 +v 0.827980 0.205605 -0.239250 +v 0.849854 0.218361 -0.254570 +v 0.855560 0.246269 -0.240433 +v 0.847595 0.257324 -0.226135 +v 0.821625 0.215302 -0.221680 +v 0.835510 0.198425 -0.254852 +v 0.863708 0.237030 -0.254639 +v -0.841967 0.226830 -0.239763 +v -0.827980 0.205605 -0.239250 +v -0.834785 0.237541 -0.223892 +v -0.855560 0.246269 -0.240433 +v -0.849854 0.218361 -0.254570 +v -0.835510 0.198425 -0.254852 +v -0.821625 0.215302 -0.221680 +v -0.847595 0.257324 -0.226135 +v -0.863708 0.237030 -0.254639 +v 0.900608 0.298853 -0.248131 +v 0.892830 0.310638 -0.235611 +v 0.884491 0.282066 -0.244095 +v 0.907722 0.288094 -0.262405 +v 0.917892 0.314713 -0.254410 +v 0.910980 0.327026 -0.241699 +v 0.876160 0.293457 -0.231506 +v 0.892273 0.271820 -0.258057 +v 0.924164 0.303406 -0.269104 +v -0.900608 0.298853 -0.248131 +v -0.884491 0.282066 -0.244095 +v -0.892830 0.310638 -0.235611 +v -0.917892 0.314713 -0.254410 +v -0.907722 0.288094 -0.262405 +v -0.892273 0.271820 -0.258057 +v -0.876160 0.293457 -0.231506 +v -0.910980 0.327026 -0.241699 +v -0.924164 0.303406 -0.269104 +v 0.829645 0.285798 -0.167344 +v 0.823320 0.264742 -0.150973 +v 0.828773 0.265953 -0.187881 +v 0.835102 0.301056 -0.184661 +v 0.830625 0.312174 -0.144945 +v 0.828467 0.292764 -0.119028 +v 0.819305 0.243744 -0.178223 +v 0.837585 0.283936 -0.197815 +v 0.831924 0.323120 -0.173154 +v -0.829645 0.285798 -0.167344 +v -0.828773 0.265953 -0.187881 +v -0.823320 0.264742 -0.150973 +v -0.830625 0.312174 -0.144945 +v -0.835102 0.301056 -0.184661 +v -0.837585 0.283936 -0.197815 +v -0.819305 0.243744 -0.178223 +v -0.828467 0.292764 -0.119028 +v -0.831924 0.323120 -0.173154 +v 0.776111 0.140517 -0.129658 +v 0.781890 0.146382 -0.096184 +v 0.756161 0.092249 -0.130661 +v 0.771733 0.133867 -0.161301 +v 0.796281 0.190279 -0.131625 +v 0.803931 0.206460 -0.094614 +v 0.759664 0.091471 -0.100179 +v 0.754076 0.090675 -0.161073 +v 0.790375 0.176727 -0.164307 +v -0.776111 0.140517 -0.129658 +v -0.756161 0.092249 -0.130661 +v -0.781890 0.146382 -0.096184 +v -0.796281 0.190279 -0.131625 +v -0.771733 0.133867 -0.161301 +v -0.754076 0.090675 -0.161073 +v -0.759664 0.091471 -0.100179 +v -0.803931 0.206460 -0.094614 +v -0.790375 0.176727 -0.164307 +v 0.725747 0.009584 -0.143881 +v 0.722091 -0.000769 -0.132798 +v 0.727682 0.007424 -0.160198 +v 0.729173 0.023656 -0.139013 +v 0.727763 0.016683 -0.117839 +v 0.724240 -0.008205 -0.152775 +v 0.732062 0.026507 -0.162497 +v -0.725747 0.009584 -0.143881 +v -0.727682 0.007424 -0.160198 +v -0.722091 -0.000769 -0.132798 +v -0.729173 0.023656 -0.139013 +v -0.732062 0.026507 -0.162497 +v -0.724240 -0.008205 -0.152775 +v -0.727763 0.016683 -0.117839 +v 0.920895 0.267029 -0.305338 +v 0.917458 0.272499 -0.291702 +v 0.906189 0.252258 -0.300064 +v 0.925301 0.261314 -0.317955 +v 0.936569 0.280945 -0.312889 +v 0.933075 0.286804 -0.299133 +v 0.902771 0.257294 -0.286621 +v 0.910461 0.246857 -0.312561 +v 0.941132 0.274963 -0.325470 +v -0.920895 0.267029 -0.305338 +v -0.906189 0.252258 -0.300064 +v -0.917458 0.272499 -0.291702 +v -0.936569 0.280945 -0.312889 +v -0.925301 0.261314 -0.317955 +v -0.910461 0.246857 -0.312561 +v -0.902771 0.257294 -0.286621 +v -0.933075 0.286804 -0.299133 +v -0.941132 0.274963 -0.325470 +v 0.865181 0.204401 -0.294666 +v 0.861511 0.207893 -0.282066 +v 0.851044 0.187294 -0.294922 +v 0.869354 0.200165 -0.306023 +v 0.878723 0.220886 -0.295120 +v 0.875183 0.224945 -0.282227 +v 0.847229 0.190125 -0.282562 +v 0.855286 0.183563 -0.305817 +v 0.882874 0.216217 -0.306946 +v -0.865181 0.204401 -0.294666 +v -0.851044 0.187294 -0.294922 +v -0.861511 0.207893 -0.282066 +v -0.878723 0.220886 -0.295120 +v -0.869354 0.200165 -0.306023 +v -0.855286 0.183563 -0.305817 +v -0.847229 0.190125 -0.282562 +v -0.875183 0.224945 -0.282227 +v -0.882874 0.216217 -0.306946 +v 0.802891 0.132992 -0.295674 +v 0.798721 0.132980 -0.283455 +v 0.790697 0.116981 -0.295490 +v 0.807741 0.131652 -0.305614 +v 0.818817 0.150948 -0.295654 +v 0.814758 0.152039 -0.283417 +v 0.786377 0.115723 -0.283508 +v 0.796332 0.117337 -0.304701 +v 0.823303 0.148529 -0.305939 +v -0.802891 0.132992 -0.295674 +v -0.790697 0.116981 -0.295490 +v -0.798721 0.132980 -0.283455 +v -0.818817 0.150948 -0.295654 +v -0.807741 0.131652 -0.305614 +v -0.796332 0.117337 -0.304701 +v -0.786377 0.115723 -0.283508 +v -0.814758 0.152039 -0.283417 +v -0.823303 0.148529 -0.305939 +v 0.795479 0.087948 -0.296257 +v 0.792182 0.085107 -0.285643 +v 0.806869 0.081399 -0.296766 +v 0.800623 0.090537 -0.305637 +v 0.787157 0.094855 -0.295734 +v 0.783447 0.092041 -0.284485 +v 0.803250 0.078952 -0.287286 +v 0.811920 0.083557 -0.306030 +v 0.792915 0.097439 -0.304701 +v -0.795479 0.087948 -0.296257 +v -0.806869 0.081399 -0.296766 +v -0.792182 0.085107 -0.285643 +v -0.787157 0.094855 -0.295734 +v -0.800623 0.090537 -0.305637 +v -0.811920 0.083557 -0.306030 +v -0.803250 0.078952 -0.287286 +v -0.783447 0.092041 -0.284485 +v -0.792915 0.097439 -0.304701 +v 0.834177 0.049254 -0.296419 +v 0.827392 0.049714 -0.285605 +v 0.837730 0.035294 -0.296074 +v 0.840492 0.050255 -0.306320 +v 0.827712 0.062356 -0.296828 +v 0.822049 0.061863 -0.287286 +v 0.830292 0.035919 -0.284332 +v 0.844360 0.036407 -0.306213 +v 0.833527 0.063538 -0.306274 +v -0.834177 0.049254 -0.296419 +v -0.837730 0.035294 -0.296074 +v -0.827392 0.049714 -0.285605 +v -0.827712 0.062356 -0.296828 +v -0.840492 0.050255 -0.306320 +v -0.844360 0.036407 -0.306213 +v -0.830292 0.035919 -0.284332 +v -0.822049 0.061863 -0.287286 +v -0.833527 0.063538 -0.306274 +v 0.830372 0.001503 -0.294179 +v 0.823341 0.001411 -0.281395 +v 0.827598 -0.005663 -0.291072 +v 0.836524 0.002646 -0.303401 +v 0.834709 0.010696 -0.295433 +v 0.827240 0.010895 -0.282745 +v 0.821106 -0.006226 -0.278381 +v 0.833523 -0.004218 -0.299479 +v 0.841187 0.011871 -0.305237 +v -0.830372 0.001503 -0.294179 +v -0.827598 -0.005663 -0.291072 +v -0.823341 0.001411 -0.281395 +v -0.834709 0.010696 -0.295433 +v -0.836524 0.002646 -0.303401 +v -0.833523 -0.004218 -0.299479 +v -0.821106 -0.006226 -0.278381 +v -0.827240 0.010895 -0.282745 +v -0.841187 0.011871 -0.305237 +v 0.850351 -0.016343 -0.269000 +v 0.846039 -0.019165 -0.254913 +v 0.870529 -0.016586 -0.265671 +v 0.853950 -0.012636 -0.281047 +v 0.836417 -0.014482 -0.276881 +v 0.831360 -0.016479 -0.263489 +v 0.866760 -0.020142 -0.251282 +v 0.873657 -0.012024 -0.278687 +v 0.840970 -0.011664 -0.286906 +v -0.850351 -0.016343 -0.269000 +v -0.870529 -0.016586 -0.265671 +v -0.846039 -0.019165 -0.254913 +v -0.836417 -0.014482 -0.276881 +v -0.853950 -0.012636 -0.281047 +v -0.873657 -0.012024 -0.278687 +v -0.866760 -0.020142 -0.251282 +v -0.831360 -0.016479 -0.263489 +v -0.840970 -0.011664 -0.286906 +v 0.992219 0.315861 -0.351320 +v 0.988487 0.322548 -0.337769 +v 0.971786 0.305481 -0.336052 +v 0.997111 0.309428 -0.362801 +v 1.015399 0.324908 -0.368179 +v 1.011719 0.331757 -0.354919 +v 0.968109 0.311951 -0.322327 +v 0.976654 0.299133 -0.348053 +v 1.020091 0.318461 -0.379028 +v -0.992219 0.315861 -0.351320 +v -0.971786 0.305481 -0.336052 +v -0.988487 0.322548 -0.337769 +v -1.015399 0.324908 -0.368179 +v -0.997111 0.309428 -0.362801 +v -0.976654 0.299133 -0.348053 +v -0.968109 0.311951 -0.322327 +v -1.011719 0.331757 -0.354919 +v -1.020091 0.318461 -0.379028 +v 1.104967 0.341936 -0.421282 +v 1.102539 0.348984 -0.409676 +v 1.072599 0.338773 -0.404068 +v 1.106878 0.335775 -0.429749 +v 1.136777 0.340452 -0.436455 +v 1.135010 0.347504 -0.425629 +v 1.069580 0.345795 -0.391785 +v 1.075796 0.332540 -0.413452 +v 1.137197 0.334127 -0.443726 +v -1.104967 0.341936 -0.421282 +v -1.072599 0.338773 -0.404068 +v -1.102539 0.348984 -0.409676 +v -1.136777 0.340452 -0.436455 +v -1.106878 0.335775 -0.429749 +v -1.075796 0.332540 -0.413452 +v -1.069580 0.345795 -0.391785 +v -1.135010 0.347504 -0.425629 +v -1.137197 0.334127 -0.443726 +v 1.208283 0.296296 -0.461447 +v 1.207611 0.301941 -0.452126 +v 1.189807 0.317797 -0.456627 +v 1.205844 0.290957 -0.466415 +v 1.220718 0.269485 -0.463867 +v 1.220093 0.274200 -0.454773 +v 1.188965 0.324188 -0.446991 +v 1.187937 0.311747 -0.462036 +v 1.217957 0.265167 -0.468567 +v -1.208283 0.296296 -0.461447 +v -1.189807 0.317797 -0.456627 +v -1.207611 0.301941 -0.452126 +v -1.220718 0.269485 -0.463867 +v -1.205844 0.290957 -0.466415 +v -1.187937 0.311747 -0.462036 +v -1.188965 0.324188 -0.446991 +v -1.220093 0.274200 -0.454773 +v -1.217957 0.265167 -0.468567 +v 1.217251 0.168945 -0.463385 +v 1.216515 0.170425 -0.455002 +v 1.225418 0.204361 -0.464844 +v 1.214134 0.168930 -0.467384 +v 1.202179 0.134018 -0.459496 +v 1.201385 0.134430 -0.451172 +v 1.224731 0.206940 -0.456238 +v 1.222351 0.202789 -0.469055 +v 1.199127 0.135529 -0.463470 +v -1.217251 0.168945 -0.463385 +v -1.225418 0.204361 -0.464844 +v -1.216515 0.170425 -0.455002 +v -1.202179 0.134018 -0.459496 +v -1.214134 0.168930 -0.467384 +v -1.222351 0.202789 -0.469055 +v -1.224731 0.206940 -0.456238 +v -1.201385 0.134430 -0.451172 +v -1.199127 0.135529 -0.463470 +v 1.117876 0.048368 -0.425320 +v 1.116867 0.046104 -0.415306 +v 1.151642 0.072800 -0.441032 +v 1.115990 0.053253 -0.431358 +v 1.080605 0.028145 -0.404900 +v 1.079437 0.025208 -0.393890 +v 1.150726 0.071320 -0.431885 +v 1.149200 0.076813 -0.446014 +v 1.079407 0.033661 -0.412231 +v -1.117876 0.048368 -0.425320 +v -1.151642 0.072800 -0.441032 +v -1.116867 0.046104 -0.415306 +v -1.080605 0.028145 -0.404900 +v -1.115990 0.053253 -0.431358 +v -1.149200 0.076813 -0.446014 +v -1.150726 0.071320 -0.431885 +v -1.079437 0.025208 -0.393890 +v -1.079407 0.033661 -0.412231 +v 0.963774 -0.007387 -0.317415 +v 0.961494 -0.011612 -0.303841 +v 1.002022 0.000587 -0.348778 +v 0.965134 -0.001335 -0.328979 +v 0.928146 -0.012436 -0.290024 +v 0.925354 -0.016724 -0.275940 +v 1.000214 -0.003357 -0.335907 +v 1.002502 0.006683 -0.359009 +v 0.930298 -0.006653 -0.302612 +v -0.963774 -0.007387 -0.317415 +v -1.002022 0.000587 -0.348778 +v -0.961494 -0.011612 -0.303841 +v -0.928146 -0.012436 -0.290024 +v -0.965134 -0.001335 -0.328979 +v -1.002502 0.006683 -0.359009 +v -1.000214 -0.003357 -0.335907 +v -0.925354 -0.016724 -0.275940 +v -0.930298 -0.006653 -0.302612 +v 0.876303 0.067186 -0.325281 +v 0.860970 0.059074 -0.320663 +v 0.881615 0.053703 -0.324547 +v 0.893509 0.077347 -0.329338 +v 0.867859 0.080154 -0.324844 +v 0.853180 0.072083 -0.320435 +v 0.865601 0.045563 -0.320129 +v 0.899628 0.063782 -0.328644 +v 0.884369 0.090332 -0.328552 +v -0.876303 0.067186 -0.325281 +v -0.881615 0.053703 -0.324547 +v -0.860970 0.059074 -0.320663 +v -0.867859 0.080154 -0.324844 +v -0.893509 0.077347 -0.329338 +v -0.899628 0.063782 -0.328644 +v -0.865601 0.045563 -0.320129 +v -0.853180 0.072083 -0.320435 +v -0.884369 0.090332 -0.328552 +v 0.946012 0.116947 -0.345428 +v 0.928970 0.102554 -0.339195 +v 0.954826 0.102440 -0.347603 +v 0.962502 0.132080 -0.351967 +v 0.935303 0.130241 -0.342461 +v 0.918671 0.115723 -0.337097 +v 0.936859 0.088440 -0.339996 +v 0.972290 0.117218 -0.355591 +v 0.951447 0.145477 -0.348175 +v -0.946012 0.116947 -0.345428 +v -0.954826 0.102440 -0.347603 +v -0.928970 0.102554 -0.339195 +v -0.935303 0.130241 -0.342461 +v -0.962502 0.132080 -0.351967 +v -0.972290 0.117218 -0.355591 +v -0.936859 0.088440 -0.339996 +v -0.918671 0.115723 -0.337097 +v -0.951447 0.145477 -0.348175 +v 1.008284 0.178389 -0.369617 +v 0.993538 0.163208 -0.364265 +v 1.021294 0.163460 -0.375809 +v 1.022873 0.192680 -0.374458 +v 0.996338 0.191696 -0.364868 +v 0.981842 0.176605 -0.359528 +v 1.005493 0.148102 -0.369995 +v 1.036713 0.178101 -0.380798 +v 1.010834 0.205902 -0.369965 +v -1.008284 0.178389 -0.369617 +v -1.021294 0.163460 -0.375809 +v -0.993538 0.163208 -0.364265 +v -0.996338 0.191696 -0.364868 +v -1.022873 0.192680 -0.374458 +v -1.036713 0.178101 -0.380798 +v -1.005493 0.148102 -0.369995 +v -0.981842 0.176605 -0.359528 +v -1.010834 0.205902 -0.369965 +v 1.068800 0.226685 -0.387423 +v 1.052658 0.216698 -0.382668 +v 1.082142 0.213891 -0.392831 +v 1.086677 0.236309 -0.394441 +v 1.058047 0.240092 -0.384888 +v 1.041229 0.229950 -0.379364 +v 1.066864 0.203247 -0.388611 +v 1.098131 0.224094 -0.399021 +v 1.076701 0.249949 -0.392609 +v -1.068800 0.226685 -0.387423 +v -1.082142 0.213891 -0.392831 +v -1.052658 0.216698 -0.382668 +v -1.058047 0.240092 -0.384888 +v -1.086677 0.236309 -0.394441 +v -1.098131 0.224094 -0.399021 +v -1.066864 0.203247 -0.388611 +v -1.041229 0.229950 -0.379364 +v -1.076701 0.249949 -0.392609 +v 1.024265 0.280202 -0.380640 +v 1.013204 0.291952 -0.377296 +v 1.005508 0.269691 -0.370575 +v 1.036207 0.267293 -0.382568 +v 1.044065 0.289838 -0.391853 +v 1.034292 0.301290 -0.390503 +v 0.993622 0.281555 -0.365265 +v 1.018158 0.256683 -0.374359 +v 1.055379 0.277334 -0.392242 +v -1.024265 0.280202 -0.380640 +v -1.005508 0.269691 -0.370575 +v -1.013204 0.291952 -0.377296 +v -1.044065 0.289838 -0.391853 +v -1.036207 0.267293 -0.382568 +v -1.018158 0.256683 -0.374359 +v -0.993622 0.281555 -0.365265 +v -1.034292 0.301290 -0.390503 +v -1.055379 0.277334 -0.392242 +v 0.955927 0.231501 -0.347471 +v 0.941002 0.216949 -0.341881 +v 0.970154 0.218124 -0.354492 +v 0.971451 0.245308 -0.354034 +v 0.942787 0.243767 -0.339104 +v 0.927795 0.229401 -0.333557 +v 0.955353 0.203339 -0.348907 +v 0.985321 0.232147 -0.360565 +v 0.958588 0.257385 -0.346100 +v -0.955927 0.231501 -0.347471 +v -0.970154 0.218124 -0.354492 +v -0.941002 0.216949 -0.341881 +v -0.942787 0.243767 -0.339104 +v -0.971451 0.245308 -0.354034 +v -0.985321 0.232147 -0.360565 +v -0.955353 0.203339 -0.348907 +v -0.927795 0.229401 -0.333557 +v -0.958588 0.257385 -0.346100 +v 0.896904 0.171263 -0.329769 +v 0.885040 0.183746 -0.323570 +v 0.881691 0.156029 -0.327019 +v 0.909973 0.157433 -0.334740 +v 0.911736 0.186615 -0.333122 +v 0.899231 0.199249 -0.325989 +v 0.870422 0.168060 -0.321808 +v 0.894135 0.142578 -0.330994 +v 0.925446 0.172699 -0.339020 +v -0.896904 0.171263 -0.329769 +v -0.881691 0.156029 -0.327019 +v -0.885040 0.183746 -0.323570 +v -0.911736 0.186615 -0.333122 +v -0.909973 0.157433 -0.334740 +v -0.894135 0.142578 -0.330994 +v -0.870422 0.168060 -0.321808 +v -0.899231 0.199249 -0.325989 +v -0.925446 0.172699 -0.339020 +v 0.833466 0.113552 -0.321067 +v 0.845337 0.103592 -0.322769 +v 0.849373 0.126640 -0.323082 +v 0.822878 0.121978 -0.318065 +v 0.819574 0.102714 -0.317997 +v 0.831085 0.094055 -0.319214 +v 0.861298 0.115234 -0.325378 +v 0.838440 0.136444 -0.319489 +v 0.810493 0.110257 -0.315565 +v -0.833466 0.113552 -0.321067 +v -0.849373 0.126640 -0.323082 +v -0.845337 0.103592 -0.322769 +v -0.819574 0.102714 -0.317997 +v -0.822878 0.121978 -0.318065 +v -0.838440 0.136444 -0.319489 +v -0.861298 0.115234 -0.325378 +v -0.831085 0.094055 -0.319214 +v -0.810493 0.110257 -0.315565 +v 0.872421 0.013181 -0.310372 +v 0.864997 0.001921 -0.301676 +v 0.892204 0.019287 -0.311836 +v 0.879082 0.026146 -0.317253 +v 0.856391 0.008414 -0.310267 +v 0.850735 -0.000556 -0.303263 +v 0.884644 0.005798 -0.301880 +v 0.898529 0.034241 -0.319977 +v 0.862427 0.019562 -0.315247 +v -0.872421 0.013181 -0.310372 +v -0.892204 0.019287 -0.311836 +v -0.864997 0.001921 -0.301676 +v -0.856391 0.008414 -0.310267 +v -0.879082 0.026146 -0.317253 +v -0.898529 0.034241 -0.319977 +v -0.884644 0.005798 -0.301880 +v -0.850735 -0.000556 -0.303263 +v -0.862427 0.019562 -0.315247 +v 0.966555 0.044441 -0.347544 +v 0.994125 0.055634 -0.366638 +v 0.964989 0.065544 -0.348976 +v 0.939964 0.034851 -0.330299 +v 0.966660 0.024696 -0.344055 +v 0.998596 0.034393 -0.367798 +v 0.988159 0.078278 -0.363403 +v 0.942108 0.054016 -0.335724 +v 0.936401 0.017029 -0.322876 +v -0.966555 0.044441 -0.347544 +v -0.964989 0.065544 -0.348976 +v -0.994125 0.055634 -0.366638 +v -0.966660 0.024696 -0.344055 +v -0.939964 0.034851 -0.330299 +v -0.942108 0.054016 -0.335724 +v -0.988159 0.078278 -0.363403 +v -0.998596 0.034393 -0.367798 +v -0.936401 0.017029 -0.322876 +v 1.076298 0.101198 -0.411806 +v 1.100922 0.119972 -0.421028 +v 1.055992 0.124062 -0.397720 +v 1.049728 0.084015 -0.399902 +v 1.094902 0.079926 -0.424065 +v 1.123444 0.100616 -0.435394 +v 1.076630 0.140991 -0.404968 +v 1.034058 0.107697 -0.388550 +v 1.063782 0.061859 -0.409302 +v -1.076298 0.101198 -0.411806 +v -1.055992 0.124062 -0.397720 +v -1.100922 0.119972 -0.421028 +v -1.094902 0.079926 -0.424065 +v -1.049728 0.084015 -0.399902 +v -1.034058 0.107697 -0.388550 +v -1.076630 0.140991 -0.404968 +v -1.123444 0.100616 -0.435394 +v -1.063782 0.061859 -0.409302 +v 1.155055 0.182703 -0.436230 +v 1.164563 0.204499 -0.438546 +v 1.126271 0.191583 -0.418252 +v 1.140961 0.161079 -0.432838 +v 1.182426 0.175705 -0.453011 +v 1.191101 0.202667 -0.454895 +v 1.137560 0.208103 -0.421848 +v 1.112152 0.174927 -0.414734 +v 1.167999 0.149078 -0.449432 +v -1.155055 0.182703 -0.436230 +v -1.126271 0.191583 -0.418252 +v -1.164563 0.204499 -0.438546 +v -1.182426 0.175705 -0.453011 +v -1.140961 0.161079 -0.432838 +v -1.112152 0.174927 -0.414734 +v -1.137560 0.208103 -0.421848 +v -1.191101 0.202667 -0.454895 +v -1.167999 0.149078 -0.449432 +v 1.157168 0.266223 -0.438290 +v 1.145597 0.283521 -0.436653 +v 1.131883 0.256064 -0.420838 +v 1.165326 0.246827 -0.439431 +v 1.180097 0.276075 -0.454147 +v 1.166005 0.294820 -0.451538 +v 1.121786 0.271718 -0.419586 +v 1.140123 0.240329 -0.422947 +v 1.189636 0.253815 -0.455383 +v -1.157168 0.266223 -0.438290 +v -1.131883 0.256064 -0.420838 +v -1.145597 0.283521 -0.436653 +v -1.180097 0.276075 -0.454147 +v -1.165326 0.246827 -0.439431 +v -1.140123 0.240329 -0.422947 +v -1.121786 0.271718 -0.419586 +v -1.166005 0.294820 -0.451538 +v -1.189636 0.253815 -0.455383 +v 1.102872 0.312178 -0.427195 +v 1.086474 0.307223 -0.417580 +v 1.115985 0.307559 -0.431129 +v 1.105677 0.320658 -0.431946 +v 1.081370 0.316996 -0.419067 +v 1.097209 0.297312 -0.417755 +v 1.128123 0.317851 -0.441040 +v -1.102872 0.312178 -0.427195 +v -1.115985 0.307559 -0.431129 +v -1.086474 0.307223 -0.417580 +v -1.105677 0.320658 -0.431946 +v -1.128123 0.317851 -0.441040 +v -1.097209 0.297312 -0.417755 +v -1.081370 0.316996 -0.419067 +v 0.912420 -0.100916 -0.320795 +v 0.911446 -0.108963 -0.285645 +v 0.852921 -0.107582 -0.288803 +v 0.914993 -0.074852 -0.356422 +v 0.974861 -0.087708 -0.357170 +v 0.973816 -0.095520 -0.324005 +v 0.851654 -0.116211 -0.251678 +v 0.856903 -0.081207 -0.326874 +v 0.976410 -0.061951 -0.390320 +v -0.912420 -0.100916 -0.320795 +v -0.852921 -0.107582 -0.288803 +v -0.911446 -0.108963 -0.285645 +v -0.974861 -0.087708 -0.357170 +v -0.914993 -0.074852 -0.356422 +v -0.856903 -0.081207 -0.326874 +v -0.851654 -0.116211 -0.251678 +v -0.973816 -0.095520 -0.324005 +v -0.976410 -0.061951 -0.390320 +v 1.163730 -0.008301 -0.441861 +v 1.159592 -0.014931 -0.415764 +v 1.102760 -0.041382 -0.420372 +v 1.165494 0.014076 -0.467402 +v 1.218664 0.031250 -0.457057 +v 1.213135 0.025543 -0.433411 +v 1.100037 -0.048645 -0.391754 +v 1.103973 -0.017273 -0.448425 +v 1.221090 0.051117 -0.480265 +v -1.163730 -0.008301 -0.441861 +v -1.102760 -0.041382 -0.420372 +v -1.159592 -0.014931 -0.415764 +v -1.218664 0.031250 -0.457057 +v -1.165494 0.014076 -0.467402 +v -1.103973 -0.017273 -0.448425 +v -1.100037 -0.048645 -0.391754 +v -1.213135 0.025543 -0.433411 +v -1.221090 0.051117 -0.480265 +v 1.320020 0.185033 -0.469897 +v 1.313179 0.184082 -0.451569 +v 1.298274 0.129333 -0.469915 +v 1.322371 0.191792 -0.488079 +v 1.329871 0.241536 -0.468227 +v 1.323761 0.242645 -0.450745 +v 1.291260 0.126495 -0.450256 +v 1.301249 0.141327 -0.489461 +v 1.330684 0.242540 -0.485153 +v -1.320020 0.185033 -0.469897 +v -1.298274 0.129333 -0.469915 +v -1.313179 0.184082 -0.451569 +v -1.329871 0.241536 -0.468227 +v -1.322371 0.191792 -0.488079 +v -1.301249 0.141327 -0.489461 +v -1.291260 0.126495 -0.450256 +v -1.323761 0.242645 -0.450745 +v -1.330684 0.242540 -0.485153 +v 1.295236 0.389848 -0.471263 +v 1.289360 0.396660 -0.450905 +v 1.316779 0.346333 -0.469051 +v 1.298812 0.374768 -0.490909 +v 1.264598 0.424606 -0.471258 +v 1.258850 0.432983 -0.448822 +v 1.311188 0.351410 -0.450500 +v 1.318233 0.336168 -0.486740 +v 1.269267 0.405090 -0.492849 +v -1.295236 0.389848 -0.471263 +v -1.316779 0.346333 -0.469051 +v -1.289360 0.396660 -0.450905 +v -1.264598 0.424606 -0.471258 +v -1.298812 0.374768 -0.490909 +v -1.318233 0.336168 -0.486740 +v -1.311188 0.351410 -0.450500 +v -1.258850 0.432983 -0.448822 +v -1.269267 0.405090 -0.492849 +v 1.132457 0.460623 -0.439149 +v 1.129242 0.472008 -0.410324 +v 1.181203 0.459641 -0.455531 +v 1.135938 0.432739 -0.466998 +v 1.082634 0.454262 -0.419228 +v 1.080872 0.466034 -0.388885 +v 1.176819 0.470337 -0.428680 +v 1.185689 0.433655 -0.481333 +v 1.084625 0.425232 -0.448669 +v -1.132457 0.460623 -0.439149 +v -1.181203 0.459641 -0.455531 +v -1.129242 0.472008 -0.410324 +v -1.082634 0.454262 -0.419228 +v -1.135938 0.432739 -0.466998 +v -1.185689 0.433655 -0.481333 +v -1.176819 0.470337 -0.428680 +v -1.080872 0.466034 -0.388885 +v -1.084625 0.425232 -0.448669 +v 0.951157 0.415916 -0.354380 +v 0.955032 0.427299 -0.323692 +v 0.990623 0.430550 -0.375191 +v 0.946762 0.386879 -0.383743 +v 0.916054 0.399376 -0.336639 +v 0.921875 0.410522 -0.306152 +v 0.992493 0.442230 -0.344086 +v 0.988556 0.401062 -0.405212 +v 0.909332 0.371063 -0.365601 +v -0.951157 0.415916 -0.354380 +v -0.990623 0.430550 -0.375191 +v -0.955032 0.427299 -0.323692 +v -0.916054 0.399376 -0.336639 +v -0.946762 0.386879 -0.383743 +v -0.988556 0.401062 -0.405212 +v -0.992493 0.442230 -0.344086 +v -0.921875 0.410522 -0.306152 +v -0.909332 0.371063 -0.365601 +v 0.929916 0.153919 -0.418509 +v 0.982208 0.166809 -0.444733 +v 0.924088 0.056953 -0.408089 +v 0.881744 0.143692 -0.396294 +v 0.935959 0.251350 -0.418167 +v 0.984283 0.264587 -0.442322 +v 0.980194 0.069641 -0.436707 +v 0.871918 0.048798 -0.383636 +v 0.891876 0.238861 -0.397827 +v -0.929916 0.153919 -0.418509 +v -0.924088 0.056953 -0.408089 +v -0.982208 0.166809 -0.444733 +v -0.935959 0.251350 -0.418167 +v -0.881744 0.143692 -0.396294 +v -0.871918 0.048798 -0.383636 +v -0.980194 0.069641 -0.436707 +v -0.984283 0.264587 -0.442322 +v -0.891876 0.238861 -0.397827 +v 1.153636 0.218904 -0.506770 +v 1.146731 0.307495 -0.502775 +v 1.206553 0.237007 -0.517408 +v 1.159991 0.131355 -0.501928 +v 1.096222 0.200134 -0.491119 +v 1.091583 0.293640 -0.486755 +v 1.198466 0.318176 -0.514089 +v 1.214335 0.157196 -0.512411 +v 1.100433 0.107483 -0.486023 +v -1.153636 0.218904 -0.506770 +v -1.206553 0.237007 -0.517408 +v -1.146731 0.307495 -0.502775 +v -1.096222 0.200134 -0.491119 +v -1.159991 0.131355 -0.501928 +v -1.214335 0.157196 -0.512411 +v -1.198466 0.318176 -0.514089 +v -1.091583 0.293640 -0.486755 +v -1.100433 0.107483 -0.486023 +v 1.301468 0.272042 -0.519497 +v 1.303491 0.308789 -0.517144 +v 1.312707 0.241597 -0.515413 +v 1.284597 0.264992 -0.523684 +v 1.278463 0.321838 -0.520884 +v 1.318640 0.280504 -0.510258 +v 1.292867 0.210785 -0.518351 +v -1.301468 0.272042 -0.519497 +v -1.312707 0.241597 -0.515413 +v -1.303491 0.308789 -0.517144 +v -1.284597 0.264992 -0.523684 +v -1.292867 0.210785 -0.518351 +v -1.318640 0.280504 -0.510258 +v -1.278463 0.321838 -0.520884 +v 0.836903 0.344056 -0.314164 +v 0.819103 0.337963 -0.311678 +v 0.847050 0.355652 -0.277114 +v 0.858955 0.360405 -0.317169 +v 0.824051 0.318424 -0.350391 +v 0.803496 0.313398 -0.356030 +v 0.830337 0.348511 -0.267637 +v 0.867920 0.371948 -0.284180 +v 0.848175 0.333832 -0.348755 +v -0.836903 0.344056 -0.314164 +v -0.847050 0.355652 -0.277114 +v -0.819103 0.337963 -0.311678 +v -0.824051 0.318424 -0.350391 +v -0.858955 0.360405 -0.317169 +v -0.867920 0.371948 -0.284180 +v -0.830337 0.348511 -0.267637 +v -0.803496 0.313398 -0.356030 +v -0.848175 0.333832 -0.348755 +v 0.766663 0.130389 -0.392011 +v 0.788131 0.208072 -0.392444 +v 0.800690 0.131668 -0.381981 +v 0.745682 0.052742 -0.378235 +v 0.734466 0.132808 -0.410304 +v 0.759795 0.206831 -0.409009 +v 0.818512 0.215790 -0.383911 +v 0.783173 0.047211 -0.367645 +v 0.710297 0.060730 -0.397247 +v -0.766663 0.130389 -0.392011 +v -0.800690 0.131668 -0.381981 +v -0.788131 0.208072 -0.392444 +v -0.734466 0.132808 -0.410304 +v -0.745682 0.052742 -0.378235 +v -0.783173 0.047211 -0.367645 +v -0.818512 0.215790 -0.383911 +v -0.759795 0.206831 -0.409009 +v -0.710297 0.060730 -0.397247 +v 0.703705 -0.091858 -0.265974 +v 0.661850 -0.081537 -0.278572 +v 0.712845 -0.062096 -0.311829 +v 0.748550 -0.101570 -0.260666 +v 0.700486 -0.106730 -0.220329 +v 0.657930 -0.100015 -0.225708 +v 0.672577 -0.049377 -0.328522 +v 0.755951 -0.073517 -0.303558 +v 0.746063 -0.113525 -0.219086 +v -0.703705 -0.091858 -0.265974 +v -0.712845 -0.062096 -0.311829 +v -0.661850 -0.081537 -0.278572 +v -0.700486 -0.106730 -0.220329 +v -0.748550 -0.101570 -0.260666 +v -0.755951 -0.073517 -0.303558 +v -0.672577 -0.049377 -0.328522 +v -0.657930 -0.100015 -0.225708 +v -0.746063 -0.113525 -0.219086 +v 0.520325 0.242645 0.696014 +v 0.501999 0.242817 0.717041 +v 0.485718 0.243027 0.735138 +v 0.471069 0.220016 0.750214 +v 0.464474 0.197952 0.751785 +v 0.453720 0.177750 0.754242 +v 0.447372 0.150833 0.743866 +v 0.458523 0.138607 0.726967 +v 0.471191 0.125031 0.707214 +v 0.507202 0.139740 0.679993 +v 0.524368 0.171753 0.676109 +v 0.535095 0.206451 0.673615 +v -0.535095 0.206451 0.673615 +v -0.524368 0.171753 0.676109 +v -0.507202 0.139740 0.679993 +v -0.471191 0.125031 0.707214 +v -0.458523 0.138607 0.726967 +v -0.447372 0.150833 0.743866 +v -0.453720 0.177750 0.754242 +v -0.464474 0.197952 0.751785 +v -0.471069 0.220016 0.750214 +v -0.485718 0.243027 0.735138 +v -0.501999 0.242817 0.717041 +v -0.520325 0.242645 0.696014 +v 0.586182 0.243103 0.594727 +v 0.571365 0.242836 0.621666 +v 0.555725 0.242645 0.647797 +v 0.496094 0.100128 0.660950 +v 0.507324 0.089928 0.635376 +v 0.518066 0.080627 0.608826 +v 0.559082 0.108398 0.575684 +v 0.581665 0.150269 0.571167 +v 0.595703 0.195801 0.568359 +v -0.595703 0.195801 0.568359 +v -0.581665 0.150269 0.571167 +v -0.559082 0.108398 0.575684 +v -0.518066 0.080627 0.608826 +v -0.507324 0.089928 0.635376 +v -0.496094 0.100128 0.660950 +v -0.555725 0.242645 0.647797 +v -0.571365 0.242836 0.621666 +v -0.586182 0.243103 0.594727 +v 0.455688 0.088837 0.691101 +v 0.423237 0.071785 0.697773 +v 0.388138 0.061096 0.704727 +v 0.351639 0.040955 0.689774 +v 0.351582 0.026535 0.666157 +v 0.351562 0.013428 0.641357 +v 0.400391 0.005859 0.606934 +v 0.447388 0.019775 0.597900 +v 0.490723 0.041992 0.589355 +v -0.490723 0.041992 0.589355 +v -0.447388 0.019775 0.597900 +v -0.400391 0.005859 0.606934 +v -0.351562 0.013428 0.641357 +v -0.351582 0.026535 0.666157 +v -0.351639 0.040955 0.689774 +v -0.388138 0.061096 0.704727 +v -0.423237 0.071785 0.697773 +v -0.455688 0.088837 0.691101 +v 0.420456 0.145828 0.761322 +v 0.399178 0.135174 0.765625 +v 0.376205 0.128510 0.770172 +v 0.352402 0.113068 0.762726 +v 0.352173 0.095695 0.748150 +v 0.351944 0.076355 0.731094 +v -0.351944 0.076355 0.731094 +v -0.352173 0.095695 0.748150 +v -0.352402 0.113068 0.762726 +v -0.376205 0.128510 0.770172 +v -0.399178 0.135174 0.765625 +v -0.420456 0.145828 0.761322 +v 0.329391 0.128510 0.779205 +v 0.307327 0.135174 0.783268 +v 0.287125 0.145828 0.786728 +v 0.260208 0.150833 0.778427 +v 0.247879 0.138607 0.765381 +v 0.233994 0.125031 0.750137 +v 0.248276 0.088837 0.728836 +v 0.280518 0.071785 0.723915 +v 0.315445 0.061096 0.718094 +v -0.315445 0.061096 0.718094 +v -0.280518 0.071785 0.723915 +v -0.248276 0.088837 0.728836 +v -0.233994 0.125031 0.750137 +v -0.247879 0.138607 0.765381 +v -0.260208 0.150833 0.778427 +v -0.287125 0.145828 0.786728 +v -0.307327 0.135174 0.783268 +v -0.329391 0.128510 0.779205 +v 0.207443 0.100128 0.712723 +v 0.195904 0.089928 0.691059 +v 0.185059 0.080627 0.668213 +v 0.212402 0.041992 0.639160 +v 0.255737 0.019775 0.632324 +v 0.302734 0.005859 0.624512 +v -0.302734 0.005859 0.624512 +v -0.255737 0.019775 0.632324 +v -0.212402 0.041992 0.639160 +v -0.185059 0.080627 0.668213 +v -0.195904 0.089928 0.691059 +v -0.207443 0.100128 0.712723 +v 0.197067 0.139740 0.735001 +v 0.179958 0.171753 0.736309 +v 0.169250 0.206451 0.736847 +v 0.147888 0.242645 0.717987 +v 0.131882 0.242836 0.697472 +v 0.116943 0.243103 0.675903 +v 0.107422 0.195801 0.653320 +v 0.121460 0.150269 0.651733 +v 0.144043 0.108398 0.648926 +v -0.144043 0.108398 0.648926 +v -0.121460 0.150269 0.651733 +v -0.107422 0.195801 0.653320 +v -0.116943 0.243103 0.675903 +v -0.131882 0.242836 0.697472 +v -0.147888 0.242645 0.717987 +v -0.169250 0.206451 0.736847 +v -0.179958 0.171753 0.736309 +v -0.197067 0.139740 0.735001 +v 0.255203 0.177750 0.790939 +v 0.244549 0.197952 0.791729 +v 0.237885 0.220016 0.791992 +v 0.222443 0.243027 0.781250 +v 0.204948 0.242817 0.768517 +v 0.185242 0.242645 0.753754 +v -0.185242 0.242645 0.753754 +v -0.204948 0.242817 0.768517 +v -0.222443 0.243027 0.781250 +v -0.237885 0.220016 0.791992 +v -0.244549 0.197952 0.791729 +v -0.255203 0.177750 0.790939 +v 0.237885 0.266830 0.791992 +v 0.244549 0.289822 0.791729 +v 0.255203 0.311157 0.790939 +v 0.260208 0.338074 0.778427 +v 0.247879 0.349167 0.765381 +v 0.233994 0.361816 0.750137 +v 0.197067 0.346313 0.735001 +v 0.179958 0.313881 0.736309 +v 0.169250 0.278839 0.736847 +v -0.169250 0.278839 0.736847 +v -0.179958 0.313881 0.736309 +v -0.197067 0.346313 0.735001 +v -0.233994 0.361816 0.750137 +v -0.247879 0.349167 0.765381 +v -0.260208 0.338074 0.778427 +v -0.255203 0.311157 0.790939 +v -0.244549 0.289822 0.791729 +v -0.237885 0.266830 0.791992 +v 0.207443 0.386719 0.712723 +v 0.195904 0.397949 0.691059 +v 0.185059 0.408691 0.668213 +v 0.144043 0.381348 0.648926 +v 0.121460 0.338135 0.651733 +v 0.107422 0.291504 0.653320 +v -0.107422 0.291504 0.653320 +v -0.121460 0.338135 0.651733 +v -0.144043 0.381348 0.648926 +v -0.185059 0.408691 0.668213 +v -0.195904 0.397949 0.691059 +v -0.207443 0.386719 0.712723 +v 0.248276 0.397827 0.728836 +v 0.280518 0.414993 0.723915 +v 0.315445 0.425720 0.718094 +v 0.351639 0.446350 0.689774 +v 0.351582 0.461990 0.666157 +v 0.351562 0.476807 0.641357 +v 0.302734 0.486328 0.624512 +v 0.255737 0.472290 0.632324 +v 0.212402 0.449707 0.639160 +v -0.212402 0.449707 0.639160 +v -0.255737 0.472290 0.632324 +v -0.302734 0.486328 0.624512 +v -0.351562 0.476807 0.641357 +v -0.351582 0.461990 0.666157 +v -0.351639 0.446350 0.689774 +v -0.315445 0.425720 0.718094 +v -0.280518 0.414993 0.723915 +v -0.248276 0.397827 0.728836 +v 0.287125 0.344727 0.786728 +v 0.307327 0.355690 0.783268 +v 0.329391 0.362457 0.779205 +v 0.352402 0.376678 0.762726 +v 0.352173 0.392708 0.748150 +v 0.351944 0.410950 0.731094 +v -0.351944 0.410950 0.731094 +v -0.352173 0.392708 0.748150 +v -0.352402 0.376678 0.762726 +v -0.329391 0.362457 0.779205 +v -0.307327 0.355690 0.783268 +v -0.287125 0.344727 0.786728 +v 0.376205 0.362457 0.770172 +v 0.399178 0.355690 0.765625 +v 0.420456 0.344727 0.761322 +v 0.447372 0.338074 0.743866 +v 0.458523 0.349167 0.726967 +v 0.471191 0.361816 0.707214 +v 0.455688 0.397827 0.691101 +v 0.423237 0.414993 0.697773 +v 0.388138 0.425720 0.704727 +v -0.388138 0.425720 0.704727 +v -0.423237 0.414993 0.697773 +v -0.455688 0.397827 0.691101 +v -0.471191 0.361816 0.707214 +v -0.458523 0.349167 0.726967 +v -0.447372 0.338074 0.743866 +v -0.420456 0.344727 0.761322 +v -0.399178 0.355690 0.765625 +v -0.376205 0.362457 0.770172 +v 0.496094 0.386719 0.660950 +v 0.507324 0.397949 0.635376 +v 0.518066 0.408691 0.608826 +v 0.490723 0.449707 0.589355 +v 0.447388 0.472290 0.597900 +v 0.400391 0.486328 0.606934 +v -0.400391 0.486328 0.606934 +v -0.447388 0.472290 0.597900 +v -0.490723 0.449707 0.589355 +v -0.518066 0.408691 0.608826 +v -0.507324 0.397949 0.635376 +v -0.496094 0.386719 0.660950 +v 0.507202 0.346313 0.679993 +v 0.524368 0.313881 0.676109 +v 0.535095 0.278839 0.673615 +v 0.595703 0.291504 0.568359 +v 0.581665 0.338135 0.571167 +v 0.559082 0.381348 0.575684 +v -0.559082 0.381348 0.575684 +v -0.581665 0.338135 0.571167 +v -0.595703 0.291504 0.568359 +v -0.535095 0.278839 0.673615 +v -0.524368 0.313881 0.676109 +v -0.507202 0.346313 0.679993 +v 0.453720 0.311157 0.754242 +v 0.464474 0.289822 0.751785 +v 0.471069 0.266830 0.750214 +v -0.471069 0.266830 0.750214 +v -0.464474 0.289822 0.751785 +v -0.453720 0.311157 0.754242 +v 0.458577 0.243357 0.773600 +v 0.462758 0.243376 0.767413 +v 0.466675 0.243332 0.759918 +v 0.434616 0.325623 0.767303 +v 0.432093 0.323316 0.774621 +v 0.429255 0.320669 0.780645 +v 0.436681 0.299978 0.783351 +v 0.445881 0.282608 0.780941 +v 0.450195 0.263316 0.780029 +v -0.450195 0.263316 0.780029 +v -0.445881 0.282608 0.780941 +v -0.436681 0.299978 0.783351 +v -0.429255 0.320669 0.780645 +v -0.432093 0.323316 0.774621 +v -0.434616 0.325623 0.767303 +v -0.466675 0.243332 0.759918 +v -0.462758 0.243376 0.767413 +v -0.458577 0.243357 0.773600 +v 0.352707 0.358978 0.784210 +v 0.352751 0.355998 0.791665 +v 0.352732 0.352610 0.797735 +v 0.372691 0.344330 0.798467 +v 0.391893 0.339279 0.793917 +v 0.408992 0.329112 0.790044 +v -0.408992 0.329112 0.790044 +v -0.391893 0.339279 0.793917 +v -0.372691 0.344330 0.798467 +v -0.352732 0.352610 0.797735 +v -0.352751 0.355998 0.791665 +v -0.352707 0.358978 0.784210 +v 0.274307 0.325623 0.798264 +v 0.276839 0.323316 0.805401 +v 0.279338 0.320669 0.811035 +v 0.298482 0.329112 0.813192 +v 0.314725 0.339279 0.810258 +v 0.333059 0.344330 0.806910 +v -0.333059 0.344330 0.806910 +v -0.314725 0.339279 0.810258 +v -0.298482 0.329112 0.813192 +v -0.279338 0.320669 0.811035 +v -0.276839 0.323316 0.805401 +v -0.274307 0.325623 0.798264 +v 0.242096 0.243332 0.800781 +v 0.245346 0.243376 0.807836 +v 0.248566 0.243357 0.813375 +v 0.256317 0.263316 0.817566 +v 0.261309 0.282608 0.817296 +v 0.271098 0.299978 0.816691 +v -0.271098 0.299978 0.816691 +v -0.261309 0.282608 0.817296 +v -0.256317 0.263316 0.817566 +v -0.248566 0.243357 0.813375 +v -0.245346 0.243376 0.807836 +v -0.242096 0.243332 0.800781 +v 0.274307 0.164932 0.798264 +v 0.276839 0.167464 0.805401 +v 0.279338 0.169963 0.811035 +v 0.271098 0.189107 0.816691 +v 0.261309 0.205350 0.817296 +v 0.256317 0.223684 0.817566 +v -0.256317 0.223684 0.817566 +v -0.261309 0.205350 0.817296 +v -0.271098 0.189107 0.816691 +v -0.279338 0.169963 0.811035 +v -0.276839 0.167464 0.805401 +v -0.274307 0.164932 0.798264 +v 0.352707 0.132721 0.784210 +v 0.352751 0.135971 0.791665 +v 0.352732 0.139191 0.797735 +v 0.333059 0.146942 0.806910 +v 0.314725 0.151934 0.810258 +v 0.298482 0.161723 0.813192 +v -0.298482 0.161723 0.813192 +v -0.314725 0.151934 0.810258 +v -0.333059 0.146942 0.806910 +v -0.352732 0.139191 0.797735 +v -0.352751 0.135971 0.791665 +v -0.352707 0.132721 0.784210 +v 0.434616 0.164932 0.767303 +v 0.432093 0.167464 0.774621 +v 0.429255 0.169963 0.780645 +v 0.408992 0.161723 0.790044 +v 0.391893 0.151934 0.793917 +v 0.372691 0.146942 0.798467 +v -0.372691 0.146942 0.798467 +v -0.391893 0.151934 0.793917 +v -0.408992 0.161723 0.790044 +v -0.429255 0.169963 0.780645 +v -0.432093 0.167464 0.774621 +v -0.434616 0.164932 0.767303 +v 0.450195 0.223684 0.780029 +v 0.445881 0.205350 0.780941 +v 0.436681 0.189107 0.783351 +v -0.436681 0.189107 0.783351 +v -0.445881 0.205350 0.780941 +v -0.450195 0.223684 0.780029 +v 0.437419 0.243154 0.787923 +v 0.417742 0.243056 0.797094 +v 0.392405 0.243067 0.807027 +v 0.381053 0.214788 0.809835 +v 0.399604 0.197221 0.801607 +v 0.413976 0.183797 0.793727 +v -0.392405 0.243067 0.807027 +v -0.417742 0.243056 0.797094 +v -0.437419 0.243154 0.787923 +v -0.413976 0.183797 0.793727 +v -0.399604 0.197221 0.801607 +v -0.381053 0.214788 0.809835 +v 0.352325 0.202947 0.816508 +v 0.352402 0.177789 0.812735 +v 0.352529 0.158600 0.808131 +v -0.352529 0.158600 0.808131 +v -0.352402 0.177789 0.812735 +v -0.352325 0.202947 0.816508 +v 0.324087 0.214788 0.821635 +v 0.306577 0.197221 0.821009 +v 0.293172 0.183797 0.818848 +v -0.293172 0.183797 0.818848 +v -0.306577 0.197221 0.821009 +v -0.324087 0.214788 0.821635 +v 0.312246 0.243067 0.822896 +v 0.287145 0.243056 0.822622 +v 0.267975 0.243154 0.820780 +v -0.267975 0.243154 0.820780 +v -0.287145 0.243056 0.822622 +v -0.312246 0.243067 0.822896 +v 0.324087 0.272364 0.821635 +v 0.306577 0.291030 0.821009 +v 0.293172 0.305491 0.818848 +v -0.293172 0.305491 0.818848 +v -0.306577 0.291030 0.821009 +v -0.324087 0.272364 0.821635 +v 0.352325 0.284734 0.816508 +v 0.352402 0.311242 0.812735 +v 0.352529 0.331655 0.808131 +v -0.352529 0.331655 0.808131 +v -0.352402 0.311242 0.812735 +v -0.352325 0.284734 0.816508 +v 0.381053 0.272364 0.809835 +v 0.399604 0.291030 0.801607 +v 0.413976 0.305491 0.793727 +v -0.413976 0.305491 0.793727 +v -0.399604 0.291030 0.801607 +v -0.381053 0.272364 0.809835 +v 0.043701 -0.970276 0.561707 +v 0.086838 -0.967369 0.558323 +v 0.128845 -0.963165 0.553421 +v 0.166199 -0.954315 0.571243 +v 0.160564 -0.947433 0.592876 +v 0.151840 -0.938202 0.612762 +v 0.108566 -0.931610 0.632660 +v 0.074215 -0.935104 0.634117 +v 0.037659 -0.937469 0.635254 +v 0.000000 -0.949371 0.620361 +v 0.000000 -0.958946 0.603470 +v 0.000000 -0.966492 0.584534 +v -0.037659 -0.937469 0.635254 +v -0.074215 -0.935104 0.634117 +v -0.108566 -0.931610 0.632660 +v -0.151840 -0.938202 0.612762 +v -0.160564 -0.947433 0.592876 +v -0.166199 -0.954315 0.571243 +v -0.128845 -0.963165 0.553421 +v -0.086838 -0.967369 0.558323 +v -0.043701 -0.970276 0.561707 +v 0.207214 -0.952789 0.541275 +v 0.241810 -0.946236 0.535240 +v 0.271744 -0.937622 0.530106 +v 0.284256 -0.925049 0.555374 +v 0.267536 -0.919109 0.582790 +v 0.246582 -0.909103 0.608551 +v 0.207336 -0.907822 0.630341 +v 0.188732 -0.916401 0.629684 +v 0.166245 -0.922577 0.630096 +v -0.166245 -0.922577 0.630096 +v -0.188732 -0.916401 0.629684 +v -0.207336 -0.907822 0.630341 +v -0.246582 -0.909103 0.608551 +v -0.267536 -0.919109 0.582790 +v -0.284256 -0.925049 0.555374 +v -0.271744 -0.937622 0.530106 +v -0.241810 -0.946236 0.535240 +v -0.207214 -0.952789 0.541275 +v 0.312820 -0.910767 0.525162 +v 0.323826 -0.891022 0.525829 +v 0.329895 -0.866211 0.528351 +v 0.322937 -0.842468 0.566376 +v 0.305096 -0.838848 0.598209 +v 0.280502 -0.827621 0.627487 +v 0.248093 -0.836716 0.647446 +v 0.242458 -0.859612 0.641499 +v 0.233948 -0.879562 0.636322 +v -0.233948 -0.879562 0.636322 +v -0.242458 -0.859612 0.641499 +v -0.248093 -0.836716 0.647446 +v -0.280502 -0.827621 0.627487 +v -0.305096 -0.838848 0.598209 +v -0.322937 -0.842468 0.566376 +v -0.329895 -0.866211 0.528351 +v -0.323826 -0.891022 0.525829 +v -0.312820 -0.910767 0.525162 +v 0.331482 -0.798828 0.538239 +v 0.328514 -0.756340 0.544651 +v 0.323639 -0.708954 0.551010 +v 0.310272 -0.673187 0.591965 +v 0.293514 -0.680187 0.623932 +v 0.269547 -0.680862 0.652054 +v 0.246414 -0.719742 0.670731 +v 0.250076 -0.754883 0.665283 +v 0.251694 -0.784897 0.659592 +v -0.251694 -0.784897 0.659592 +v -0.250076 -0.754883 0.665283 +v -0.246414 -0.719742 0.670731 +v -0.269547 -0.680862 0.652054 +v -0.293514 -0.680187 0.623932 +v -0.310272 -0.673187 0.591965 +v -0.323639 -0.708954 0.551010 +v -0.328514 -0.756340 0.544651 +v -0.331482 -0.798828 0.538239 +v 0.309967 -0.602386 0.560471 +v 0.301723 -0.546715 0.562992 +v 0.292679 -0.493164 0.564301 +v 0.274734 -0.454407 0.601410 +v 0.257946 -0.459976 0.635677 +v 0.234833 -0.462402 0.666153 +v 0.216461 -0.515442 0.689041 +v 0.225323 -0.571476 0.685387 +v 0.233658 -0.626907 0.680923 +v -0.233658 -0.626907 0.680923 +v -0.225323 -0.571476 0.685387 +v -0.216461 -0.515442 0.689041 +v -0.234833 -0.462402 0.666153 +v -0.257946 -0.459976 0.635677 +v -0.274734 -0.454407 0.601410 +v -0.292679 -0.493164 0.564301 +v -0.301723 -0.546715 0.562992 +v -0.309967 -0.602386 0.560471 +v 0.184418 -0.127909 0.714630 +v 0.206287 -0.149501 0.666538 +v 0.222275 -0.165161 0.620468 +v 0.267868 -0.159668 0.575058 +v 0.314640 -0.144722 0.567055 +v 0.368378 -0.128937 0.557907 +v 0.418121 -0.100616 0.579453 +v 0.410202 -0.084011 0.611088 +v 0.401398 -0.063797 0.641022 +v 0.334747 -0.056168 0.686188 +v 0.274971 -0.067893 0.709610 +v 0.214844 -0.079173 0.735016 +v -0.214844 -0.079173 0.735016 +v -0.274971 -0.067893 0.709610 +v -0.334747 -0.056168 0.686188 +v -0.401398 -0.063797 0.641022 +v -0.410202 -0.084011 0.611088 +v -0.418121 -0.100616 0.579453 +v -0.368378 -0.128937 0.557907 +v -0.314640 -0.144722 0.567055 +v -0.267868 -0.159668 0.575058 +v -0.222275 -0.165161 0.620468 +v -0.206287 -0.149501 0.666538 +v -0.184418 -0.127909 0.714630 +v 0.477020 -0.090607 0.543198 +v 0.527470 -0.066532 0.537804 +v 0.575974 -0.038559 0.531601 +v 0.620712 0.004044 0.549484 +v 0.614338 0.018959 0.574955 +v 0.603729 0.035416 0.598480 +v 0.545746 0.021622 0.627960 +v 0.498535 -0.003403 0.637985 +v 0.447418 -0.024734 0.650299 +v -0.447418 -0.024734 0.650299 +v -0.498535 -0.003403 0.637985 +v -0.545746 0.021622 0.627960 +v -0.603729 0.035416 0.598480 +v -0.614338 0.018959 0.574955 +v -0.620712 0.004044 0.549484 +v -0.575974 -0.038559 0.531601 +v -0.527470 -0.066532 0.537804 +v -0.477020 -0.090607 0.543198 +v 0.669052 0.030960 0.510422 +v 0.711998 0.073021 0.497952 +v 0.749741 0.120010 0.489685 +v 0.770920 0.177567 0.518433 +v 0.754742 0.184803 0.547676 +v 0.733643 0.191772 0.575348 +v 0.688171 0.156189 0.599030 +v 0.660500 0.118046 0.603195 +v 0.627167 0.082596 0.610382 +v -0.627167 0.082596 0.610382 +v -0.660500 0.118046 0.603195 +v -0.688171 0.156189 0.599030 +v -0.733643 0.191772 0.575348 +v -0.754742 0.184803 0.547676 +v -0.770920 0.177567 0.518433 +v -0.749741 0.120010 0.489685 +v -0.711998 0.073021 0.497952 +v -0.669052 0.030960 0.510422 +v 0.801193 0.228958 0.502136 +v 0.812706 0.286472 0.522495 +v 0.814621 0.340027 0.546341 +v 0.796494 0.380615 0.593765 +v 0.778820 0.373005 0.615021 +v 0.756287 0.362869 0.632187 +v 0.734802 0.318314 0.629745 +v 0.732574 0.280125 0.616276 +v 0.724304 0.238708 0.605621 +v -0.724304 0.238708 0.605621 +v -0.732574 0.280125 0.616276 +v -0.734802 0.318314 0.629745 +v -0.756287 0.362869 0.632187 +v -0.778820 0.373005 0.615021 +v -0.796494 0.380615 0.593765 +v -0.814621 0.340027 0.546341 +v -0.812706 0.286472 0.522495 +v -0.801193 0.228958 0.502136 +v 0.789291 0.417297 0.586685 +v 0.762978 0.440792 0.600498 +v 0.728928 0.459885 0.612427 +v 0.684494 0.471664 0.651794 +v 0.676769 0.460041 0.675266 +v 0.665649 0.445648 0.692825 +v 0.684082 0.411530 0.687698 +v 0.707260 0.394207 0.673187 +v 0.722595 0.375015 0.658859 +v -0.722595 0.375015 0.658859 +v -0.707260 0.394207 0.673187 +v -0.684082 0.411530 0.687698 +v -0.665649 0.445648 0.692825 +v -0.676769 0.460041 0.675266 +v -0.684494 0.471664 0.651794 +v -0.728928 0.459885 0.612427 +v -0.762978 0.440792 0.600498 +v -0.789291 0.417297 0.586685 +v 0.641708 0.501938 0.637695 +v 0.591908 0.528534 0.652088 +v 0.541107 0.557999 0.666702 +v 0.485748 0.583755 0.710098 +v 0.477894 0.571796 0.735210 +v 0.469284 0.554993 0.753693 +v 0.509567 0.506958 0.749115 +v 0.560314 0.479378 0.733974 +v 0.609131 0.453278 0.718277 +v -0.609131 0.453278 0.718277 +v -0.560314 0.479378 0.733974 +v -0.509567 0.506958 0.749115 +v -0.469284 0.554993 0.753693 +v -0.477894 0.571796 0.735210 +v -0.485748 0.583755 0.710098 +v -0.541107 0.557999 0.666702 +v -0.591908 0.528534 0.652088 +v -0.641708 0.501938 0.637695 +v 0.445892 0.623001 0.693741 +v 0.403042 0.654739 0.705406 +v 0.362320 0.681747 0.715775 +v 0.320450 0.690475 0.753738 +v 0.318745 0.671410 0.777889 +v 0.318054 0.645554 0.794830 +v 0.349304 0.602158 0.794647 +v 0.382366 0.583584 0.785721 +v 0.419174 0.560547 0.775238 +v -0.419174 0.560547 0.775238 +v -0.382366 0.583584 0.785721 +v -0.349304 0.602158 0.794647 +v -0.318054 0.645554 0.794830 +v -0.318745 0.671410 0.777889 +v -0.320450 0.690475 0.753738 +v -0.362320 0.681747 0.715775 +v -0.403042 0.654739 0.705406 +v -0.445892 0.623001 0.693741 +v 0.284134 0.708908 0.732437 +v 0.246655 0.705807 0.738564 +v 0.211273 0.691467 0.743057 +v 0.181122 0.655518 0.773819 +v 0.187057 0.637634 0.796810 +v 0.195526 0.614014 0.812592 +v 0.232330 0.606384 0.816742 +v 0.260124 0.617397 0.813187 +v 0.288879 0.620041 0.808319 +v -0.288879 0.620041 0.808319 +v -0.260124 0.617397 0.813187 +v -0.232330 0.606384 0.816742 +v -0.195526 0.614014 0.812592 +v -0.187057 0.637634 0.796810 +v -0.181122 0.655518 0.773819 +v -0.211273 0.691467 0.743057 +v -0.246655 0.705807 0.738564 +v -0.284134 0.708908 0.732437 +v 0.149872 0.628723 0.746658 +v 0.123989 0.585476 0.746075 +v 0.100479 0.541306 0.744476 +v 0.080460 0.493515 0.769745 +v 0.086163 0.481129 0.792339 +v 0.095245 0.466400 0.808304 +v 0.130463 0.485992 0.818253 +v 0.154602 0.522545 0.819637 +v 0.179596 0.557312 0.819916 +v -0.179596 0.557312 0.819916 +v -0.154602 0.522545 0.819637 +v -0.130463 0.485992 0.818253 +v -0.095245 0.466400 0.808304 +v -0.086163 0.481129 0.792339 +v -0.080460 0.493515 0.769745 +v -0.100479 0.541306 0.744476 +v -0.123989 0.585476 0.746075 +v -0.149872 0.628723 0.746658 +v 0.058060 0.471176 0.739960 +v 0.038242 0.450062 0.737831 +v 0.018982 0.437714 0.736267 +v 0.000000 0.424713 0.761780 +v 0.000000 0.409452 0.783107 +v 0.000000 0.387899 0.798381 +v 0.035675 0.376363 0.806559 +v 0.061249 0.398115 0.809203 +v 0.084564 0.422638 0.812576 +v -0.084564 0.422638 0.812576 +v -0.061249 0.398115 0.809203 +v -0.035675 0.376363 0.806559 +v -0.018982 0.437714 0.736267 +v -0.038242 0.450062 0.737831 +v -0.058060 0.471176 0.739960 +v 0.121246 0.438324 0.813248 +v 0.136635 0.426704 0.804276 +v 0.152344 0.416199 0.792770 +v 0.185120 0.427368 0.781723 +v 0.204494 0.446114 0.779888 +v 0.224899 0.462067 0.777283 +v 0.236435 0.499237 0.786865 +v 0.226368 0.527306 0.801598 +v 0.215912 0.557007 0.813812 +v -0.215912 0.557007 0.813812 +v -0.226368 0.527306 0.801598 +v -0.236435 0.499237 0.786865 +v -0.224899 0.462067 0.777283 +v -0.204494 0.446114 0.779888 +v -0.185120 0.427368 0.781723 +v -0.152344 0.416199 0.792770 +v -0.136635 0.426704 0.804276 +v -0.121246 0.438324 0.813248 +v 0.266220 0.483185 0.770691 +v 0.286903 0.487862 0.766949 +v 0.308151 0.488770 0.762924 +v 0.327011 0.514221 0.770859 +v 0.323589 0.546703 0.785374 +v 0.320618 0.581039 0.797394 +v -0.320618 0.581039 0.797394 +v -0.323589 0.546703 0.785374 +v -0.327011 0.514221 0.770859 +v -0.308151 0.488770 0.762924 +v -0.286903 0.487862 0.766949 +v -0.266220 0.483185 0.770691 +v 0.353989 0.479736 0.754074 +v 0.379837 0.470409 0.748684 +v 0.408768 0.458542 0.741882 +v 0.445145 0.464890 0.741089 +v 0.449356 0.487759 0.751961 +v 0.454575 0.511536 0.760895 +v -0.454575 0.511536 0.760895 +v -0.449356 0.487759 0.751961 +v -0.445145 0.464890 0.741089 +v -0.408768 0.458542 0.741882 +v -0.379837 0.470409 0.748684 +v -0.353989 0.479736 0.754074 +v 0.479263 0.429184 0.721741 +v 0.518291 0.412601 0.709057 +v 0.555328 0.395294 0.696274 +v 0.602325 0.388641 0.690048 +v 0.618996 0.401291 0.697418 +v 0.635864 0.415253 0.702896 +v -0.635864 0.415253 0.702896 +v -0.618996 0.401291 0.697418 +v -0.602325 0.388641 0.690048 +v -0.555328 0.395294 0.696274 +v -0.518291 0.412601 0.709057 +v -0.479263 0.429184 0.721741 +v 0.609894 0.360138 0.675339 +v 0.625393 0.341938 0.668034 +v 0.634842 0.322311 0.662323 +v 0.659866 0.310959 0.655609 +v 0.682407 0.323624 0.654057 +v 0.706482 0.337296 0.651047 +v -0.706482 0.337296 0.651047 +v -0.682407 0.323624 0.654057 +v -0.659866 0.310959 0.655609 +v -0.634842 0.322311 0.662323 +v -0.625393 0.341938 0.668034 +v -0.609894 0.360138 0.675339 +v 0.641006 0.275742 0.654144 +v 0.638195 0.248665 0.651642 +v 0.630280 0.219894 0.650665 +v 0.636871 0.192490 0.642303 +v 0.660042 0.195400 0.631439 +v 0.684692 0.197266 0.617584 +v -0.684692 0.197266 0.617584 +v -0.660042 0.195400 0.631439 +v -0.636871 0.192490 0.642303 +v -0.630280 0.219894 0.650665 +v -0.638195 0.248665 0.651642 +v -0.641006 0.275742 0.654144 +v 0.595612 0.159775 0.654694 +v 0.569595 0.130249 0.659595 +v 0.539948 0.102676 0.665817 +v 0.526459 0.072952 0.661911 +v 0.547592 0.067543 0.649906 +v 0.569183 0.060501 0.635956 +v -0.569183 0.060501 0.635956 +v -0.547592 0.067543 0.649906 +v -0.526459 0.072952 0.661911 +v -0.539948 0.102676 0.665817 +v -0.569595 0.130249 0.659595 +v -0.595612 0.159775 0.654694 +v 0.476227 0.058182 0.680405 +v 0.443035 0.043037 0.688564 +v 0.407974 0.033401 0.697632 +v 0.373856 0.014359 0.701294 +v 0.378841 -0.003006 0.693733 +v 0.385162 -0.021988 0.682770 +v -0.385162 -0.021988 0.682770 +v -0.378841 -0.003006 0.693733 +v -0.373856 0.014359 0.701294 +v -0.407974 0.033401 0.697632 +v -0.443035 0.043037 0.688564 +v -0.476227 0.058182 0.680405 +v 0.329300 0.032974 0.719482 +v 0.287842 0.041256 0.731159 +v 0.248825 0.053192 0.741531 +v 0.201462 0.040558 0.752029 +v 0.185436 0.005605 0.757175 +v 0.168213 -0.037181 0.762268 +v -0.168213 -0.037181 0.762268 +v -0.185436 0.005605 0.757175 +v -0.201462 0.040558 0.752029 +v -0.248825 0.053192 0.741531 +v -0.287842 0.041256 0.731159 +v -0.329300 0.032974 0.719482 +v 0.189682 0.082001 0.752945 +v 0.170837 0.097534 0.753979 +v 0.156998 0.114044 0.753693 +v 0.121353 0.115509 0.750397 +v 0.089283 0.095098 0.748006 +v 0.050964 0.071279 0.745534 +v 0.045441 0.005910 0.751058 +v 0.078903 -0.023877 0.760442 +v 0.110992 -0.055278 0.767517 +v -0.110992 -0.055278 0.767517 +v -0.078903 -0.023877 0.760442 +v -0.045441 0.005910 0.751058 +v -0.050964 0.071279 0.745534 +v -0.089283 0.095098 0.748006 +v -0.121353 0.115509 0.750397 +v -0.156998 0.114044 0.753693 +v -0.170837 0.097534 0.753979 +v -0.189682 0.082001 0.752945 +v 0.129150 0.335754 0.778564 +v 0.139206 0.360214 0.780743 +v 0.152039 0.383911 0.782150 +v 0.040802 0.336324 0.798808 +v 0.072029 0.325643 0.790507 +v 0.099121 0.317322 0.782166 +v -0.099121 0.317322 0.782166 +v -0.072029 0.325643 0.790507 +v -0.040802 0.336324 0.798808 +v -0.152039 0.383911 0.782150 +v -0.139206 0.360214 0.780743 +v -0.129150 0.335754 0.778564 +v 0.118362 0.237747 0.766251 +v 0.117207 0.261723 0.769596 +v 0.118286 0.286194 0.772827 +v 0.000000 0.309774 0.794108 +v 0.000000 0.274877 0.783595 +v 0.000000 0.241882 0.773865 +v 0.034119 0.210022 0.764221 +v 0.066715 0.211155 0.763126 +v 0.096268 0.212723 0.762405 +v -0.096268 0.212723 0.762405 +v -0.066715 0.211155 0.763126 +v -0.034119 0.210022 0.764221 +v -0.118286 0.286194 0.772827 +v -0.117207 0.261723 0.769596 +v -0.118362 0.237747 0.766251 +v 0.000000 0.176697 0.756531 +v 0.000000 0.140798 0.749547 +v 0.000000 0.099386 0.744588 +v 0.137894 0.150665 0.754547 +v 0.130882 0.170849 0.756710 +v 0.125381 0.192154 0.759598 +v -0.125381 0.192154 0.759598 +v -0.130882 0.170849 0.756710 +v -0.137894 0.150665 0.754547 +v 0.123459 -0.915497 0.648529 +v 0.105518 -0.903545 0.663940 +v 0.087921 -0.892288 0.676666 +v 0.055695 -0.887894 0.683929 +v 0.037651 -0.891415 0.682541 +v 0.018982 -0.893341 0.681732 +v 0.000000 -0.903534 0.673615 +v 0.000000 -0.914673 0.662971 +v 0.000000 -0.926544 0.650146 +v -0.018982 -0.893341 0.681732 +v -0.037651 -0.891415 0.682541 +v -0.055695 -0.887894 0.683929 +v -0.087921 -0.892288 0.676666 +v -0.105518 -0.903545 0.663940 +v -0.123459 -0.915497 0.648529 +v 0.195679 -0.880234 0.654205 +v 0.168812 -0.863857 0.673199 +v 0.143875 -0.848389 0.688736 +v 0.114334 -0.852112 0.695694 +v 0.102688 -0.865162 0.691830 +v 0.088654 -0.875137 0.688568 +v -0.088654 -0.875137 0.688568 +v -0.102688 -0.865162 0.691830 +v -0.114334 -0.852112 0.695694 +v -0.143875 -0.848389 0.688736 +v -0.168812 -0.863857 0.673199 +v -0.195679 -0.880234 0.654205 +v 0.218796 -0.793503 0.676132 +v 0.184737 -0.774964 0.695119 +v 0.150037 -0.757524 0.710787 +v 0.125165 -0.767504 0.716677 +v 0.129134 -0.791562 0.710683 +v 0.128189 -0.814758 0.705154 +v -0.128189 -0.814758 0.705154 +v -0.129134 -0.791562 0.710683 +v -0.125165 -0.767504 0.716677 +v -0.150037 -0.757524 0.710787 +v -0.184737 -0.774964 0.695119 +v -0.218796 -0.793503 0.676132 +v 0.178680 -0.462524 0.710770 +v 0.148998 -0.461765 0.724628 +v 0.119705 -0.460953 0.734131 +v 0.097000 -0.512222 0.738220 +v 0.103111 -0.565559 0.735313 +v 0.109146 -0.615982 0.732086 +v 0.145645 -0.662979 0.721405 +v 0.177971 -0.667946 0.710232 +v 0.210037 -0.673111 0.695206 +v -0.210037 -0.673111 0.695206 +v -0.177971 -0.667946 0.710232 +v -0.145645 -0.662979 0.721405 +v -0.109146 -0.615982 0.732086 +v -0.103111 -0.565559 0.735313 +v -0.097000 -0.512222 0.738220 +v -0.119705 -0.460953 0.734131 +v -0.148998 -0.461765 0.724628 +v -0.178680 -0.462524 0.710770 +v 0.116653 -0.690140 0.726959 +v 0.117309 -0.712326 0.725449 +v 0.116254 -0.728502 0.724398 +v -0.116254 -0.728502 0.724398 +v -0.117309 -0.712326 0.725449 +v -0.116653 -0.690140 0.726959 +v 0.088882 -0.414749 0.740784 +v 0.087728 -0.374260 0.740876 +v 0.088201 -0.338149 0.741519 +v 0.065648 -0.317184 0.747805 +v 0.043268 -0.326458 0.749085 +v 0.021484 -0.332397 0.749512 +v 0.000000 -0.355713 0.747070 +v 0.000000 -0.384155 0.746460 +v 0.000000 -0.419678 0.746582 +v 0.021362 -0.461792 0.746338 +v 0.043369 -0.461201 0.745728 +v 0.066666 -0.460648 0.743896 +v -0.066666 -0.460648 0.743896 +v -0.043369 -0.461201 0.745728 +v -0.021362 -0.461792 0.746338 +v -0.021484 -0.332397 0.749512 +v -0.043268 -0.326458 0.749085 +v -0.065648 -0.317184 0.747805 +v -0.088201 -0.338149 0.741519 +v -0.087728 -0.374260 0.740876 +v -0.088882 -0.414749 0.740784 +v 0.027466 -0.655762 0.736816 +v 0.055347 -0.655903 0.736046 +v 0.084061 -0.656815 0.733734 +v 0.000000 -0.511230 0.744629 +v 0.000000 -0.562775 0.742065 +v 0.000000 -0.612427 0.739258 +v -0.084061 -0.656815 0.733734 +v -0.055347 -0.655903 0.736046 +v -0.027466 -0.655762 0.736816 +v 0.000000 -0.688843 0.735352 +v 0.000000 -0.713051 0.734413 +v 0.000000 -0.730255 0.733551 +v 0.027039 -0.739960 0.732391 +v 0.054332 -0.736592 0.731766 +v 0.082135 -0.735460 0.729586 +v -0.082135 -0.735460 0.729586 +v -0.054332 -0.736592 0.731766 +v -0.027039 -0.739960 0.732391 +v 0.105809 -0.279159 0.745822 +v 0.115278 -0.260238 0.747433 +v 0.121170 -0.242508 0.749283 +v 0.120499 -0.223038 0.760147 +v 0.119518 -0.221790 0.769920 +v 0.119293 -0.220764 0.779984 +v 0.114716 -0.235352 0.788651 +v 0.107254 -0.248714 0.786396 +v 0.096283 -0.260727 0.783691 +v 0.082245 -0.275864 0.772644 +v 0.082612 -0.280830 0.763489 +v 0.084386 -0.288619 0.754214 +v -0.084386 -0.288619 0.754214 +v -0.082612 -0.280830 0.763489 +v -0.082245 -0.275864 0.772644 +v -0.096283 -0.260727 0.783691 +v -0.107254 -0.248714 0.786396 +v -0.114716 -0.235352 0.788651 +v -0.119293 -0.220764 0.779984 +v -0.119518 -0.221790 0.769920 +v -0.120499 -0.223038 0.760147 +v -0.121170 -0.242508 0.749283 +v -0.115278 -0.260238 0.747433 +v -0.105809 -0.279159 0.745822 +v 0.122330 -0.204605 0.753250 +v 0.117340 -0.184330 0.754917 +v 0.108261 -0.165039 0.755920 +v 0.094711 -0.151611 0.758484 +v 0.095844 -0.152218 0.764660 +v 0.097260 -0.151596 0.773056 +v 0.108185 -0.165817 0.784531 +v 0.115112 -0.183331 0.786987 +v 0.118622 -0.202026 0.788956 +v -0.118622 -0.202026 0.788956 +v -0.115112 -0.183331 0.786987 +v -0.108185 -0.165817 0.784531 +v -0.097260 -0.151596 0.773056 +v -0.095844 -0.152218 0.764660 +v -0.094711 -0.151611 0.758484 +v -0.108261 -0.165039 0.755920 +v -0.117340 -0.184330 0.754917 +v -0.122330 -0.204605 0.753250 +v 0.076706 -0.135010 0.754578 +v 0.054783 -0.126961 0.752632 +v 0.029877 -0.125275 0.750824 +v 0.018402 -0.140320 0.756256 +v 0.028393 -0.142143 0.764103 +v 0.035355 -0.141876 0.773056 +v 0.054398 -0.137299 0.780930 +v 0.069759 -0.137318 0.780479 +v 0.084747 -0.141953 0.780930 +v -0.084747 -0.141953 0.780930 +v -0.069759 -0.137318 0.780479 +v -0.054398 -0.137299 0.780930 +v -0.035355 -0.141876 0.773056 +v -0.028393 -0.142143 0.764103 +v -0.018402 -0.140320 0.756256 +v -0.029877 -0.125275 0.750824 +v -0.054783 -0.126961 0.752632 +v -0.076706 -0.135010 0.754578 +v 0.000000 -0.150208 0.755432 +v 0.000000 -0.161057 0.759689 +v 0.000000 -0.165405 0.764282 +v 0.000000 -0.168335 0.767700 +v 0.000000 -0.171707 0.771828 +v 0.000000 -0.172302 0.780060 +v 0.008545 -0.168640 0.789032 +v 0.017673 -0.160316 0.787006 +v 0.027969 -0.150360 0.784531 +v -0.027969 -0.150360 0.784531 +v -0.017673 -0.160316 0.787006 +v -0.008545 -0.168640 0.789032 +v 0.000000 -0.307068 0.770508 +v 0.000000 -0.312363 0.762207 +v 0.000000 -0.320801 0.754883 +v 0.064301 -0.283249 0.779785 +v 0.044228 -0.293026 0.779053 +v 0.022522 -0.299988 0.778809 +v -0.022522 -0.299988 0.778809 +v -0.044228 -0.293026 0.779053 +v -0.064301 -0.283249 0.779785 +v 0.000000 -0.281708 0.798828 +v 0.000000 -0.290382 0.792725 +v 0.000000 -0.297302 0.786133 +v 0.080292 -0.267624 0.788879 +v 0.077372 -0.263210 0.795428 +v 0.073191 -0.259338 0.800791 +v 0.057902 -0.260284 0.804545 +v 0.041178 -0.264969 0.804652 +v 0.021362 -0.269073 0.804688 +v -0.021362 -0.269073 0.804688 +v -0.041178 -0.264969 0.804652 +v -0.057902 -0.260284 0.804545 +v -0.073191 -0.259338 0.800791 +v -0.077372 -0.263210 0.795428 +v -0.080292 -0.267624 0.788879 +v 0.000000 -0.174255 0.798981 +v 0.000000 -0.178640 0.807208 +v 0.000000 -0.186300 0.814616 +v 0.020935 -0.188131 0.818461 +v 0.033384 -0.176028 0.815252 +v 0.042531 -0.165639 0.811686 +v 0.046956 -0.153493 0.804728 +v 0.045221 -0.147878 0.798575 +v 0.043106 -0.143829 0.790939 +v -0.043106 -0.143829 0.790939 +v -0.045221 -0.147878 0.798575 +v -0.046956 -0.153493 0.804728 +v -0.042531 -0.165639 0.811686 +v -0.033384 -0.176028 0.815252 +v -0.020935 -0.188131 0.818461 +v 0.056997 -0.158407 0.809367 +v 0.068293 -0.159454 0.809713 +v 0.079213 -0.162516 0.809367 +v 0.090963 -0.161570 0.804728 +v 0.094755 -0.156768 0.798575 +v 0.097137 -0.153244 0.790939 +v -0.097137 -0.153244 0.790939 +v -0.094755 -0.156768 0.798575 +v -0.090963 -0.161570 0.804728 +v -0.079213 -0.162516 0.809367 +v -0.068293 -0.159454 0.809713 +v -0.056997 -0.158407 0.809367 +v 0.089589 -0.174937 0.811686 +v 0.091604 -0.188014 0.814879 +v 0.092010 -0.202850 0.816971 +v 0.102692 -0.218536 0.812637 +v 0.110947 -0.219139 0.806313 +v 0.115997 -0.219543 0.798599 +v -0.115997 -0.219543 0.798599 +v -0.110947 -0.219139 0.806313 +v -0.102692 -0.218536 0.812637 +v -0.092010 -0.202850 0.816971 +v -0.091604 -0.188014 0.814879 +v -0.089589 -0.174937 0.811686 +v 0.086884 -0.229950 0.815323 +v 0.081538 -0.240631 0.811835 +v 0.074961 -0.249908 0.807749 +v -0.074961 -0.249908 0.807749 +v -0.081538 -0.240631 0.811835 +v -0.086884 -0.229950 0.815323 +v 0.029388 -0.208913 0.821818 +v 0.053131 -0.212972 0.821501 +v 0.073761 -0.215729 0.820145 +v -0.073761 -0.215729 0.820145 +v -0.053131 -0.212972 0.821501 +v -0.029388 -0.208913 0.821818 +v 0.000000 -0.224447 0.819865 +v 0.000000 -0.242414 0.815806 +v 0.000000 -0.257904 0.810547 +v 0.000000 -0.023051 0.746358 +v 0.000000 -0.066920 0.747487 +v 0.000000 -0.102051 0.747986 +v 0.097092 -0.140137 0.758362 +v 0.103947 -0.128421 0.762871 +v 0.117767 -0.113780 0.766510 +v -0.117767 -0.113780 0.766510 +v -0.103947 -0.128421 0.762871 +v -0.097092 -0.140137 0.758362 +v 0.129837 -0.226028 0.743851 +v 0.139076 -0.227879 0.735207 +v 0.150833 -0.230164 0.722610 +v 0.158401 -0.208130 0.714371 +v 0.152218 -0.178958 0.728558 +v 0.147980 -0.144419 0.744781 +v -0.147980 -0.144419 0.744781 +v -0.152218 -0.178958 0.728558 +v -0.158401 -0.208130 0.714371 +v -0.150833 -0.230164 0.722610 +v -0.139076 -0.227879 0.735207 +v -0.129837 -0.226028 0.743851 +v 0.114354 -0.308334 0.735934 +v 0.136483 -0.312694 0.725838 +v 0.158630 -0.316467 0.711853 +v 0.176453 -0.296082 0.692017 +v 0.172993 -0.275162 0.693165 +v 0.169327 -0.254761 0.696609 +v -0.169327 -0.254761 0.696609 +v -0.172993 -0.275162 0.693165 +v -0.176453 -0.296082 0.692017 +v -0.158630 -0.316467 0.711853 +v -0.136483 -0.312694 0.725838 +v -0.114354 -0.308334 0.735934 +v 0.199493 -0.417786 0.692764 +v 0.192131 -0.379395 0.692966 +v 0.185730 -0.346863 0.692627 +v -0.185730 -0.346863 0.692627 +v -0.192131 -0.379395 0.692966 +v -0.199493 -0.417786 0.692764 +v 0.272598 -0.403564 0.564056 +v 0.262207 -0.369240 0.563110 +v 0.252304 -0.340485 0.562164 +v 0.234848 -0.319855 0.597382 +v 0.220318 -0.321373 0.632607 +v 0.201599 -0.321106 0.664978 +v -0.201599 -0.321106 0.664978 +v -0.220318 -0.321373 0.632607 +v -0.234848 -0.319855 0.597382 +v -0.252304 -0.340485 0.562164 +v -0.262207 -0.369240 0.563110 +v -0.272598 -0.403564 0.564056 +v 0.236130 -0.295441 0.561920 +v 0.229866 -0.277058 0.563141 +v 0.224106 -0.260056 0.565399 +v 0.209030 -0.242661 0.603790 +v 0.195724 -0.240124 0.640301 +v 0.180313 -0.236633 0.674698 +v -0.180313 -0.236633 0.674698 +v -0.195724 -0.240124 0.640301 +v -0.209030 -0.242661 0.603790 +v -0.224106 -0.260056 0.565399 +v -0.229866 -0.277058 0.563141 +v -0.236130 -0.295441 0.561920 +v 0.211960 -0.226059 0.573456 +v 0.209389 -0.208523 0.577885 +v 0.214951 -0.191284 0.580612 +v -0.214951 -0.191284 0.580612 +v -0.209389 -0.208523 0.577885 +v -0.211960 -0.226059 0.573456 +v 0.083603 -0.754959 0.721359 +v 0.088233 -0.751587 0.725285 +v 0.096722 -0.747728 0.726168 +v 0.000000 -0.750641 0.730255 +v 0.000000 -0.756371 0.726807 +v 0.000000 -0.760193 0.721405 +v 0.024048 -0.760803 0.713898 +v 0.046612 -0.757343 0.714542 +v 0.066208 -0.755386 0.714828 +v -0.066208 -0.755386 0.714828 +v -0.046612 -0.757343 0.714542 +v -0.024048 -0.760803 0.713898 +v -0.096722 -0.747728 0.726168 +v -0.088233 -0.751587 0.725285 +v -0.083603 -0.754959 0.721359 +v 0.092987 -0.817993 0.706314 +v 0.098812 -0.821289 0.708733 +v 0.108414 -0.826782 0.706741 +v 0.090561 -0.767410 0.711960 +v 0.094547 -0.781879 0.708572 +v 0.094025 -0.798828 0.704361 +v -0.094025 -0.798828 0.704361 +v -0.094547 -0.781879 0.708572 +v -0.090561 -0.767410 0.711960 +v -0.108414 -0.826782 0.706741 +v -0.098812 -0.821289 0.708733 +v -0.092987 -0.817993 0.706314 +v 0.051376 -0.865067 0.689270 +v 0.055454 -0.869499 0.691978 +v 0.062286 -0.875137 0.690948 +v 0.082306 -0.830688 0.694901 +v 0.072575 -0.843204 0.690327 +v 0.061264 -0.853409 0.686279 +v -0.061264 -0.853409 0.686279 +v -0.072575 -0.843204 0.690327 +v -0.082306 -0.830688 0.694901 +v -0.062286 -0.875137 0.690948 +v -0.055454 -0.869499 0.691978 +v -0.051376 -0.865067 0.689270 +v 0.000000 -0.876465 0.684845 +v 0.000000 -0.880943 0.687057 +v 0.000000 -0.886566 0.685944 +v 0.036911 -0.866776 0.681030 +v 0.024639 -0.870258 0.679893 +v 0.012329 -0.872070 0.679413 +v -0.012329 -0.872070 0.679413 +v -0.024639 -0.870258 0.679893 +v -0.036911 -0.866776 0.681030 +v 0.000000 -0.856476 0.651520 +v 0.000000 -0.863655 0.660599 +v 0.000000 -0.868652 0.670563 +v 0.047775 -0.857559 0.673645 +v 0.047565 -0.852825 0.663087 +v 0.048737 -0.845978 0.653509 +v 0.039581 -0.840118 0.646245 +v 0.026833 -0.843292 0.645746 +v 0.013550 -0.845306 0.645477 +v -0.013550 -0.845306 0.645477 +v -0.026833 -0.843292 0.645746 +v -0.039581 -0.840118 0.646245 +v -0.048737 -0.845978 0.653509 +v -0.047565 -0.852825 0.663087 +v -0.047775 -0.857559 0.673645 +v 0.087738 -0.813965 0.689041 +v 0.086377 -0.812157 0.677169 +v 0.084920 -0.810394 0.666853 +v 0.079061 -0.816132 0.655165 +v 0.071744 -0.824402 0.650482 +v 0.062408 -0.830841 0.648097 +v -0.062408 -0.830841 0.648097 +v -0.071744 -0.824402 0.650482 +v -0.079061 -0.816132 0.655165 +v -0.084920 -0.810394 0.666853 +v -0.086377 -0.812157 0.677169 +v -0.087738 -0.813965 0.689041 +v 0.080002 -0.760025 0.703476 +v 0.078785 -0.762993 0.690782 +v 0.076935 -0.768021 0.677572 +v 0.082550 -0.781326 0.663371 +v 0.086576 -0.789677 0.661205 +v 0.086019 -0.800232 0.660078 +v -0.086019 -0.800232 0.660078 +v -0.086576 -0.789677 0.661205 +v -0.082550 -0.781326 0.663371 +v -0.076935 -0.768021 0.677572 +v -0.078785 -0.762993 0.690782 +v -0.080002 -0.760025 0.703476 +v 0.000000 -0.765320 0.702484 +v 0.000000 -0.768715 0.689743 +v 0.000000 -0.774384 0.676605 +v 0.021606 -0.781891 0.664825 +v 0.041939 -0.778759 0.665494 +v 0.059723 -0.776260 0.665853 +v -0.059723 -0.776260 0.665853 +v -0.041939 -0.778759 0.665494 +v -0.021606 -0.781891 0.664825 +v 0.000000 -0.797699 0.654510 +v 0.000000 -0.814331 0.647583 +v 0.000000 -0.831207 0.644318 +v 0.056183 -0.821564 0.645716 +v 0.061996 -0.805309 0.648825 +v 0.068268 -0.789566 0.655558 +v -0.068268 -0.789566 0.655558 +v -0.061996 -0.805309 0.648825 +v -0.056183 -0.821564 0.645716 +v 0.164642 0.219620 0.771286 +v 0.154202 0.217903 0.768631 +v 0.140152 0.216202 0.765396 +v 0.164261 0.142853 0.757843 +v 0.176208 0.150230 0.762493 +v 0.184128 0.155090 0.766251 +v 0.183456 0.172852 0.768509 +v 0.178532 0.188007 0.769531 +v 0.174957 0.204178 0.770798 +v -0.174957 0.204178 0.770798 +v -0.178532 0.188007 0.769531 +v -0.183456 0.172852 0.768509 +v -0.184128 0.155090 0.766251 +v -0.176208 0.150230 0.762493 +v -0.164261 0.142853 0.757843 +v -0.140152 0.216202 0.765396 +v -0.154202 0.217903 0.768631 +v -0.164642 0.219620 0.771286 +v 0.168533 0.300003 0.774826 +v 0.156300 0.302525 0.773830 +v 0.140991 0.306091 0.773621 +v 0.171783 0.239639 0.773056 +v 0.172298 0.258705 0.773842 +v 0.174393 0.278336 0.774460 +v -0.174393 0.278336 0.774460 +v -0.172298 0.258705 0.773842 +v -0.171783 0.239639 0.773056 +v -0.140991 0.306091 0.773621 +v -0.156300 0.302525 0.773830 +v -0.168533 0.300003 0.774826 +v 0.203171 0.380280 0.775513 +v 0.192822 0.388237 0.775692 +v 0.180908 0.397034 0.777451 +v 0.183853 0.318314 0.775375 +v 0.191360 0.337872 0.775616 +v 0.200729 0.356415 0.775574 +v -0.200729 0.356415 0.775574 +v -0.191360 0.337872 0.775616 +v -0.183853 0.318314 0.775375 +v -0.180908 0.397034 0.777451 +v -0.192822 0.388237 0.775692 +v -0.203171 0.380280 0.775513 +v 0.225327 0.085358 0.751236 +v 0.232788 0.096905 0.755890 +v 0.238297 0.104019 0.761093 +v 0.222488 0.120865 0.767441 +v 0.207886 0.132999 0.768311 +v 0.197433 0.145508 0.768204 +v -0.197433 0.145508 0.768204 +v -0.207886 0.132999 0.768311 +v -0.222488 0.120865 0.767441 +v -0.238297 0.104019 0.761093 +v -0.232788 0.096905 0.755890 +v -0.225327 0.085358 0.751236 +v 0.368301 0.043228 0.715820 +v 0.367432 0.054703 0.723946 +v 0.367294 0.064499 0.730988 +v 0.334579 0.077072 0.743927 +v 0.301147 0.085449 0.752094 +v 0.269669 0.096512 0.759323 +v -0.269669 0.096512 0.759323 +v -0.301147 0.085449 0.752094 +v -0.334579 0.077072 0.743927 +v -0.367294 0.064499 0.730988 +v -0.367432 0.054703 0.723946 +v -0.368301 0.043228 0.715820 +v 0.495575 0.084976 0.683823 +v 0.487000 0.092472 0.693783 +v 0.481232 0.100098 0.701904 +v 0.451324 0.091858 0.714478 +v 0.425068 0.080700 0.721352 +v 0.397385 0.074203 0.728302 +v -0.397385 0.074203 0.728302 +v -0.425068 0.080700 0.721352 +v -0.451324 0.091858 0.714478 +v -0.481232 0.100098 0.701904 +v -0.487000 0.092472 0.693783 +v -0.495575 0.084976 0.683823 +v 0.599762 0.189621 0.660614 +v 0.586277 0.190712 0.668797 +v 0.575180 0.192856 0.675461 +v 0.548630 0.171494 0.684982 +v 0.527119 0.148052 0.691753 +v 0.502716 0.126282 0.699463 +v -0.502716 0.126282 0.699463 +v -0.527119 0.148052 0.691753 +v -0.548630 0.171494 0.684982 +v -0.575180 0.192856 0.675461 +v -0.586277 0.190712 0.668797 +v -0.599762 0.189621 0.660614 +v 0.622330 0.293442 0.662811 +v 0.607628 0.288975 0.669086 +v 0.595047 0.286285 0.675156 +v 0.585159 0.264130 0.677841 +v 0.583054 0.242252 0.676964 +v 0.576889 0.219284 0.677475 +v -0.576889 0.219284 0.677475 +v -0.583054 0.242252 0.676964 +v -0.585159 0.264130 0.677841 +v -0.595047 0.286285 0.675156 +v -0.607628 0.288975 0.669086 +v -0.622330 0.293442 0.662811 +v 0.574127 0.368744 0.684982 +v 0.563259 0.361511 0.688850 +v 0.553909 0.355621 0.693954 +v 0.562943 0.336029 0.689743 +v 0.574303 0.320267 0.684532 +v 0.580948 0.303192 0.681442 +v -0.580948 0.303192 0.681442 +v -0.574303 0.320267 0.684532 +v -0.562943 0.336029 0.689743 +v -0.553909 0.355621 0.693954 +v -0.563259 0.361511 0.688850 +v -0.574127 0.368744 0.684982 +v 0.438492 0.428696 0.732788 +v 0.435642 0.416409 0.737240 +v 0.432983 0.407104 0.743515 +v 0.460754 0.389404 0.737900 +v 0.491840 0.377525 0.724358 +v 0.521011 0.364594 0.710312 +v -0.521011 0.364594 0.710312 +v -0.491840 0.377525 0.724358 +v -0.460754 0.389404 0.737900 +v -0.432983 0.407104 0.743515 +v -0.435642 0.416409 0.737240 +v -0.438492 0.428696 0.732788 +v 0.333176 0.464539 0.753464 +v 0.335430 0.448475 0.753098 +v 0.337158 0.436539 0.755264 +v 0.357971 0.423477 0.757339 +v 0.379517 0.417423 0.756790 +v 0.403503 0.409546 0.754440 +v -0.403503 0.409546 0.754440 +v -0.379517 0.417423 0.756790 +v -0.357971 0.423477 0.757339 +v -0.337158 0.436539 0.755264 +v -0.335430 0.448475 0.753098 +v -0.333176 0.464539 0.753464 +v 0.253708 0.455292 0.767944 +v 0.260555 0.440285 0.766262 +v 0.266296 0.428650 0.767075 +v 0.286926 0.425049 0.765305 +v 0.303223 0.428242 0.762177 +v 0.320251 0.429031 0.759476 +v -0.320251 0.429031 0.759476 +v -0.303223 0.428242 0.762177 +v -0.286926 0.425049 0.765305 +v -0.266296 0.428650 0.767075 +v -0.260555 0.440285 0.766262 +v -0.253708 0.455292 0.767944 +v 0.225082 0.388275 0.774231 +v 0.239605 0.400963 0.772808 +v 0.255066 0.411377 0.770859 +v -0.255066 0.411377 0.770859 +v -0.239605 0.400963 0.772808 +v -0.225082 0.388275 0.774231 +v 0.280212 0.399109 0.764526 +v 0.277710 0.405029 0.766705 +v 0.274719 0.411682 0.768112 +v 0.219223 0.367828 0.772827 +v 0.225323 0.363243 0.769043 +v 0.230652 0.359314 0.764282 +v 0.246582 0.367676 0.760254 +v 0.258057 0.378052 0.761475 +v 0.270020 0.386719 0.762207 +v -0.270020 0.386719 0.762207 +v -0.258057 0.378052 0.761475 +v -0.246582 0.367676 0.760254 +v -0.230652 0.359314 0.764282 +v -0.225323 0.363243 0.769043 +v -0.219223 0.367828 0.772827 +v -0.274719 0.411682 0.768112 +v -0.277710 0.405029 0.766705 +v -0.280212 0.399109 0.764526 +v 0.339600 0.407837 0.755005 +v 0.339478 0.413471 0.757069 +v 0.339111 0.419876 0.758133 +v 0.295410 0.398438 0.760254 +v 0.309082 0.401489 0.757690 +v 0.323730 0.402832 0.754883 +v -0.323730 0.402832 0.754883 +v -0.309082 0.401489 0.757690 +v -0.295410 0.398438 0.760254 +v -0.339111 0.419876 0.758133 +v -0.339478 0.413471 0.757069 +v -0.339600 0.407837 0.755005 +v 0.422729 0.387390 0.743958 +v 0.425293 0.390671 0.747890 +v 0.427856 0.394775 0.749802 +v 0.356934 0.400879 0.750977 +v 0.375977 0.397461 0.749146 +v 0.396973 0.392090 0.745605 +v -0.396973 0.392090 0.745605 +v -0.375977 0.397461 0.749146 +v -0.356934 0.400879 0.750977 +v -0.427856 0.394775 0.749802 +v -0.425293 0.390671 0.747890 +v -0.422729 0.387390 0.743958 +v 0.523499 0.339233 0.693665 +v 0.530537 0.342690 0.697048 +v 0.537857 0.346466 0.698776 +v 0.445801 0.374512 0.728027 +v 0.471802 0.362671 0.714722 +v 0.496094 0.349609 0.701172 +v -0.496094 0.349609 0.701172 +v -0.471802 0.362671 0.714722 +v -0.445801 0.374512 0.728027 +v -0.537857 0.346466 0.698776 +v -0.530537 0.342690 0.697048 +v -0.523499 0.339233 0.693665 +v 0.557617 0.280151 0.676697 +v 0.565739 0.281532 0.679501 +v 0.574478 0.282990 0.680710 +v 0.531250 0.322266 0.681641 +v 0.541016 0.308350 0.676880 +v 0.546875 0.293945 0.674316 +v -0.546875 0.293945 0.674316 +v -0.541016 0.308350 0.676880 +v -0.531250 0.322266 0.681641 +v -0.574478 0.282990 0.680710 +v -0.565739 0.281532 0.679501 +v -0.557617 0.280151 0.676697 +v 0.541870 0.204285 0.679321 +v 0.549297 0.201321 0.681179 +v 0.557175 0.198410 0.681625 +v 0.550781 0.262695 0.672363 +v 0.549072 0.245483 0.672485 +v 0.543945 0.227051 0.673828 +v -0.543945 0.227051 0.673828 +v -0.549072 0.245483 0.672485 +v -0.550781 0.262695 0.672363 +v -0.557175 0.198410 0.681625 +v -0.549297 0.201321 0.681179 +v -0.541870 0.204285 0.679321 +v 0.464844 0.122742 0.705505 +v 0.468788 0.118240 0.708206 +v 0.472809 0.113159 0.709106 +v 0.520508 0.186035 0.681641 +v 0.502686 0.164673 0.687866 +v 0.482422 0.144531 0.694824 +v -0.482422 0.144531 0.694824 +v -0.502686 0.164673 0.687866 +v -0.520508 0.186035 0.681641 +v -0.472809 0.113159 0.709106 +v -0.468788 0.118240 0.708206 +v -0.464844 0.122742 0.705505 +v 0.367676 0.091675 0.731995 +v 0.367657 0.086117 0.735313 +v 0.367599 0.079941 0.736847 +v 0.439453 0.113281 0.708496 +v 0.417236 0.103638 0.714844 +v 0.393555 0.098145 0.721191 +v -0.393555 0.098145 0.721191 +v -0.417236 0.103638 0.714844 +v -0.439453 0.113281 0.708496 +v -0.367599 0.079941 0.736847 +v -0.367657 0.086117 0.735313 +v -0.367676 0.091675 0.731995 +v 0.253540 0.122009 0.757263 +v 0.250141 0.117741 0.761898 +v 0.246536 0.113358 0.764694 +v 0.338867 0.100098 0.734863 +v 0.309326 0.106689 0.741699 +v 0.281250 0.115723 0.747559 +v -0.281250 0.115723 0.747559 +v -0.309326 0.106689 0.741699 +v -0.338867 0.100098 0.734863 +v -0.246536 0.113358 0.764694 +v -0.250141 0.117741 0.761898 +v -0.253540 0.122009 0.757263 +v 0.203796 0.169495 0.757385 +v 0.199444 0.165894 0.762760 +v 0.194809 0.162292 0.766556 +v 0.238281 0.137207 0.753418 +v 0.224609 0.148682 0.753418 +v 0.214844 0.160645 0.752441 +v -0.214844 0.160645 0.752441 +v -0.224609 0.148682 0.753418 +v -0.238281 0.137207 0.753418 +v -0.194809 0.162292 0.766556 +v -0.199444 0.165894 0.762760 +v -0.203796 0.169495 0.757385 +v 0.185806 0.297134 0.772568 +v 0.191868 0.296406 0.768291 +v 0.196899 0.295959 0.762756 +v 0.207520 0.312012 0.757324 +v 0.215576 0.327637 0.757812 +v 0.225098 0.342285 0.758301 +v -0.225098 0.342285 0.758301 +v -0.215576 0.327637 0.757812 +v -0.207520 0.312012 0.757324 +v -0.196899 0.295959 0.762756 +v -0.191868 0.296406 0.768291 +v -0.185806 0.297134 0.772568 +v 0.179657 0.223221 0.769638 +v 0.185753 0.225117 0.764828 +v 0.191284 0.227051 0.758423 +v 0.195801 0.245117 0.752441 +v 0.196045 0.261841 0.753906 +v 0.197754 0.278809 0.755371 +v -0.197754 0.278809 0.755371 +v -0.196045 0.261841 0.753906 +v -0.195801 0.245117 0.752441 +v -0.191284 0.227051 0.758423 +v -0.185753 0.225117 0.764828 +v -0.179657 0.223221 0.769638 +v 0.203125 0.186035 0.750488 +v 0.199829 0.199585 0.750244 +v 0.197754 0.213867 0.750488 +v -0.197754 0.213867 0.750488 +v -0.199829 0.199585 0.750244 +v -0.203125 0.186035 0.750488 +v 0.000000 0.438833 0.646301 +v 0.000000 0.436986 0.675201 +v 0.000000 0.436554 0.705872 +v 0.081497 0.502853 0.711761 +v 0.089772 0.500684 0.679674 +v 0.104494 0.497930 0.647559 +v 0.081535 0.472367 0.620367 +v 0.048714 0.456137 0.622606 +v 0.022664 0.447968 0.622498 +v -0.022664 0.447968 0.622498 +v -0.048714 0.456137 0.622606 +v -0.081535 0.472367 0.620367 +v -0.104494 0.497930 0.647559 +v -0.089772 0.500684 0.679674 +v -0.081497 0.502853 0.711761 +v 0.181000 0.666626 0.714920 +v 0.187462 0.660011 0.684895 +v 0.197754 0.647980 0.659424 +v 0.198212 0.611633 0.636292 +v 0.183315 0.576346 0.629350 +v 0.163220 0.537013 0.622809 +v -0.163220 0.537013 0.622809 +v -0.183315 0.576346 0.629350 +v -0.198212 0.611633 0.636292 +v -0.197754 0.647980 0.659424 +v -0.187462 0.660011 0.684895 +v -0.181000 0.666626 0.714920 +v 0.326004 0.700668 0.693497 +v 0.328119 0.692097 0.663840 +v 0.327779 0.677338 0.639842 +v 0.300161 0.661774 0.623820 +v 0.266393 0.656754 0.626338 +v 0.232941 0.646637 0.633972 +v -0.232941 0.646637 0.633972 +v -0.266393 0.656754 0.626338 +v -0.300161 0.661774 0.623820 +v -0.327779 0.677338 0.639842 +v -0.328119 0.692097 0.663840 +v -0.326004 0.700668 0.693497 +v 0.494415 0.588455 0.649857 +v 0.491268 0.581384 0.618439 +v 0.479445 0.569844 0.587815 +v 0.405104 0.587849 0.584519 +v 0.372086 0.618825 0.600539 +v 0.344716 0.645264 0.614268 +v -0.344716 0.645264 0.614268 +v -0.372086 0.618825 0.600539 +v -0.405104 0.587849 0.584519 +v -0.479445 0.569844 0.587815 +v -0.491268 0.581384 0.618439 +v -0.494415 0.588455 0.649857 +v 0.687119 0.479904 0.594421 +v 0.682312 0.476807 0.562111 +v 0.674698 0.471512 0.527069 +v 0.623611 0.485977 0.502594 +v 0.575744 0.507920 0.518978 +v 0.520186 0.530568 0.537430 +v -0.520186 0.530568 0.537430 +v -0.575744 0.507920 0.518978 +v -0.623611 0.485977 0.502594 +v -0.674698 0.471512 0.527069 +v -0.682312 0.476807 0.562111 +v -0.687119 0.479904 0.594421 +v 0.807419 0.385986 0.542191 +v 0.800858 0.384556 0.512577 +v 0.789810 0.382217 0.480286 +v 0.758560 0.410110 0.459839 +v 0.733376 0.431801 0.470284 +v 0.702042 0.449173 0.479156 +v -0.702042 0.449173 0.479156 +v -0.733376 0.431801 0.470284 +v -0.758560 0.410110 0.459839 +v -0.789810 0.382217 0.480286 +v -0.800858 0.384556 0.512577 +v -0.807419 0.385986 0.542191 +v 0.780502 0.169937 0.463623 +v 0.775944 0.171001 0.436298 +v 0.770538 0.174713 0.403885 +v 0.784210 0.235138 0.378128 +v 0.790462 0.289104 0.400013 +v 0.787796 0.338760 0.423828 +v -0.787796 0.338760 0.423828 +v -0.790462 0.289104 0.400013 +v -0.784210 0.235138 0.378128 +v -0.770538 0.174713 0.403885 +v -0.775944 0.171001 0.436298 +v -0.780502 0.169937 0.463623 +v 0.621384 -0.008713 0.496445 +v 0.618828 -0.006477 0.466240 +v 0.618332 -0.002548 0.428818 +v 0.667221 0.038467 0.369186 +v 0.707676 0.081738 0.360641 +v 0.742096 0.129303 0.357559 +v -0.742096 0.129303 0.357559 +v -0.707676 0.081738 0.360641 +v -0.667221 0.038467 0.369186 +v -0.618332 -0.002548 0.428818 +v -0.618828 -0.006477 0.466240 +v -0.621384 -0.008713 0.496445 +v 0.427277 -0.113556 0.524094 +v 0.427088 -0.111780 0.500505 +v 0.423167 -0.110177 0.475484 +v 0.474792 -0.088662 0.421732 +v 0.527859 -0.062094 0.405600 +v 0.576828 -0.033005 0.392319 +v -0.576828 -0.033005 0.392319 +v -0.527859 -0.062094 0.405600 +v -0.474792 -0.088662 0.421732 +v -0.423167 -0.110177 0.475484 +v -0.427088 -0.111780 0.500505 +v -0.427277 -0.113556 0.524094 +v 0.100845 -0.563400 0.339279 +v 0.067707 -0.572300 0.332455 +v 0.033997 -0.578583 0.327942 +v 0.000000 -0.544220 0.315369 +v 0.000000 -0.514694 0.302048 +v 0.000000 -0.491608 0.286377 +v 0.044006 -0.470062 0.267578 +v 0.086174 -0.459999 0.267975 +v 0.124664 -0.445862 0.272583 +v 0.150360 -0.451660 0.302917 +v 0.143139 -0.479519 0.320354 +v 0.137039 -0.513290 0.335678 +v -0.137039 -0.513290 0.335678 +v -0.143139 -0.479519 0.320354 +v -0.150360 -0.451660 0.302917 +v -0.124664 -0.445862 0.272583 +v -0.086174 -0.459999 0.267975 +v -0.044006 -0.470062 0.267578 +v -0.033997 -0.578583 0.327942 +v -0.067707 -0.572300 0.332455 +v -0.100845 -0.563400 0.339279 +v 0.106720 -0.763351 0.371750 +v 0.071007 -0.772594 0.368114 +v 0.035461 -0.779175 0.365356 +v 0.000000 -0.729370 0.351685 +v 0.000000 -0.676521 0.342484 +v 0.000000 -0.626129 0.334656 +v 0.132462 -0.598923 0.356003 +v 0.134319 -0.648842 0.361992 +v 0.137970 -0.700790 0.367844 +v -0.137970 -0.700790 0.367844 +v -0.134319 -0.648842 0.361992 +v -0.132462 -0.598923 0.356003 +v -0.035461 -0.779175 0.365356 +v -0.071007 -0.772594 0.368114 +v -0.106720 -0.763351 0.371750 +v 0.121902 -0.927414 0.448029 +v 0.081364 -0.934307 0.452171 +v 0.040710 -0.939209 0.454895 +v 0.000000 -0.912354 0.428894 +v 0.000000 -0.875031 0.403885 +v 0.000000 -0.830933 0.381958 +v 0.147736 -0.802658 0.387070 +v 0.152859 -0.848473 0.402241 +v 0.157791 -0.888107 0.420868 +v -0.157791 -0.888107 0.420868 +v -0.152859 -0.848473 0.402241 +v -0.147736 -0.802658 0.387070 +v -0.040710 -0.939209 0.454895 +v -0.081364 -0.934307 0.452171 +v -0.121902 -0.927414 0.448029 +v 0.166031 -0.940536 0.467743 +v 0.168690 -0.952900 0.494442 +v 0.169861 -0.958221 0.521500 +v 0.000000 -0.973083 0.538391 +v 0.000000 -0.969772 0.511658 +v 0.000000 -0.959717 0.483826 +v -0.169861 -0.958221 0.521500 +v -0.168690 -0.952900 0.494442 +v -0.166031 -0.940536 0.467743 +v 0.292684 -0.898295 0.446594 +v 0.300348 -0.911830 0.468678 +v 0.301285 -0.921387 0.496475 +v 0.202408 -0.911484 0.436676 +v 0.238351 -0.902999 0.432030 +v 0.266134 -0.893626 0.431091 +v -0.266134 -0.893626 0.431091 +v -0.238351 -0.902999 0.432030 +v -0.202408 -0.911484 0.436676 +v -0.301285 -0.921387 0.496475 +v -0.300348 -0.911830 0.468678 +v -0.292684 -0.898295 0.446594 +v 0.301605 -0.762085 0.432648 +v 0.319984 -0.789047 0.463135 +v 0.330627 -0.815735 0.497284 +v 0.286886 -0.864329 0.424072 +v 0.287793 -0.830798 0.415581 +v 0.283722 -0.788696 0.410614 +v -0.283722 -0.788696 0.410614 +v -0.287793 -0.830798 0.415581 +v -0.286886 -0.864329 0.424072 +v -0.330627 -0.815735 0.497284 +v -0.319984 -0.789047 0.463135 +v -0.301605 -0.762085 0.432648 +v 0.275757 -0.567444 0.442764 +v 0.297356 -0.598274 0.479218 +v 0.312103 -0.630341 0.518051 +v 0.269684 -0.691895 0.408051 +v 0.262459 -0.641724 0.408932 +v 0.255737 -0.592041 0.410294 +v -0.255737 -0.592041 0.410294 +v -0.262459 -0.641724 0.408932 +v -0.269684 -0.691895 0.408051 +v -0.312103 -0.630341 0.518051 +v -0.297356 -0.598274 0.479218 +v -0.275757 -0.567444 0.442764 +v 0.247406 -0.733154 0.392853 +v 0.214325 -0.734463 0.383762 +v 0.178986 -0.742050 0.378769 +v 0.164261 -0.542465 0.357773 +v 0.194172 -0.535107 0.370560 +v 0.222778 -0.534485 0.387894 +v -0.222778 -0.534485 0.387894 +v -0.194172 -0.535107 0.370560 +v -0.164261 -0.542465 0.357773 +v -0.178986 -0.742050 0.378769 +v -0.214325 -0.734463 0.383762 +v -0.247406 -0.733154 0.392853 +v 0.183258 -0.413269 0.306274 +v 0.203632 -0.399330 0.335972 +v 0.220871 -0.390350 0.371109 +v 0.239731 -0.420868 0.410233 +v 0.242458 -0.457848 0.411079 +v 0.245728 -0.499146 0.411575 +v -0.245728 -0.499146 0.411575 +v -0.242458 -0.457848 0.411079 +v -0.239731 -0.420868 0.410233 +v -0.220871 -0.390350 0.371109 +v -0.203632 -0.399330 0.335972 +v -0.183258 -0.413269 0.306274 +v 0.254379 -0.397430 0.448318 +v 0.269775 -0.412148 0.487530 +v 0.280289 -0.429138 0.526459 +v -0.280289 -0.429138 0.526459 +v -0.269775 -0.412148 0.487530 +v -0.254379 -0.397430 0.448318 +v 0.244308 -0.310211 0.527679 +v 0.241435 -0.302621 0.492374 +v 0.238767 -0.294318 0.452557 +v 0.248614 -0.259772 0.423372 +v 0.250480 -0.241026 0.442622 +v 0.249156 -0.225815 0.459666 +v 0.230235 -0.227036 0.492198 +v 0.223998 -0.236275 0.513652 +v 0.221664 -0.241501 0.538971 +v -0.221664 -0.241501 0.538971 +v -0.223998 -0.236275 0.513652 +v -0.230235 -0.227036 0.492198 +v -0.249156 -0.225815 0.459666 +v -0.250480 -0.241026 0.442622 +v -0.248614 -0.259772 0.423372 +v -0.238767 -0.294318 0.452557 +v -0.241435 -0.302621 0.492374 +v -0.244308 -0.310211 0.527679 +v 0.234055 -0.362518 0.408707 +v 0.232247 -0.339037 0.407490 +v 0.233264 -0.315649 0.404848 +v -0.233264 -0.315649 0.404848 +v -0.232247 -0.339037 0.407490 +v -0.234055 -0.362518 0.408707 +v 0.249997 -0.174998 0.511288 +v 0.243663 -0.177905 0.526820 +v 0.239059 -0.178467 0.549362 +v 0.239553 -0.201828 0.484975 +v 0.237693 -0.191161 0.493125 +v 0.245480 -0.180247 0.498837 +v -0.245480 -0.180247 0.498837 +v -0.237693 -0.191161 0.493125 +v -0.239553 -0.201828 0.484975 +v -0.239059 -0.178467 0.549362 +v -0.243663 -0.177905 0.526820 +v -0.249997 -0.174998 0.511288 +v 0.278286 -0.161357 0.498013 +v 0.313323 -0.147298 0.489282 +v 0.357168 -0.132913 0.474090 +v -0.357168 -0.132913 0.474090 +v -0.313323 -0.147298 0.489282 +v -0.278286 -0.161357 0.498013 +v 0.000000 -0.115021 -0.657043 +v 0.000000 -0.053276 -0.699280 +v 0.000000 0.017151 -0.732849 +v 0.089172 0.097107 -0.748596 +v 0.174856 0.093582 -0.725118 +v 0.253560 0.088745 -0.693868 +v 0.310282 0.017578 -0.640971 +v 0.304240 -0.038002 -0.614558 +v 0.303080 -0.085602 -0.579142 +v 0.238302 -0.147308 -0.555623 +v 0.164327 -0.160141 -0.578771 +v 0.083801 -0.167816 -0.597351 +v -0.083801 -0.167816 -0.597351 +v -0.164327 -0.160141 -0.578771 +v -0.238302 -0.147308 -0.555623 +v -0.303080 -0.085602 -0.579142 +v -0.304240 -0.038002 -0.614558 +v -0.310282 0.017578 -0.640971 +v -0.253560 0.088745 -0.693868 +v -0.174856 0.093582 -0.725118 +v -0.089172 0.097107 -0.748596 +v 0.000000 -0.309418 -0.388672 +v 0.000000 -0.268127 -0.471939 +v 0.000000 -0.221588 -0.544006 +v 0.297994 -0.173126 -0.477498 +v 0.290326 -0.215298 -0.410776 +v 0.280060 -0.253983 -0.334061 +v 0.208893 -0.311050 -0.262894 +v 0.143089 -0.329144 -0.277576 +v 0.072693 -0.340729 -0.288818 +v -0.072693 -0.340729 -0.288818 +v -0.143089 -0.329144 -0.277576 +v -0.208893 -0.311050 -0.262894 +v -0.280060 -0.253983 -0.334061 +v -0.290326 -0.215298 -0.410776 +v -0.297994 -0.173126 -0.477498 +v 0.000000 -0.416016 0.028259 +v 0.000000 -0.397453 -0.075577 +v 0.000000 -0.373993 -0.186035 +v 0.254181 -0.313614 -0.152481 +v 0.239910 -0.334102 -0.052383 +v 0.225722 -0.350187 0.048173 +v 0.160699 -0.393339 0.127529 +v 0.108763 -0.413459 0.118140 +v 0.054871 -0.426025 0.115540 +v -0.054871 -0.426025 0.115540 +v -0.108763 -0.413459 0.118140 +v -0.160699 -0.393339 0.127529 +v -0.225722 -0.350187 0.048173 +v -0.239910 -0.334102 -0.052383 +v -0.254181 -0.313614 -0.152481 +v 0.000000 -0.461212 0.248291 +v 0.000000 -0.450996 0.220078 +v 0.000000 -0.441406 0.177917 +v 0.185744 -0.385129 0.207902 +v 0.172252 -0.399566 0.241871 +v 0.163910 -0.413330 0.265686 +v -0.163910 -0.413330 0.265686 +v -0.172252 -0.399566 0.241871 +v -0.185744 -0.385129 0.207902 +v 0.226689 -0.343961 0.224982 +v 0.233124 -0.325395 0.284627 +v 0.238686 -0.305273 0.339714 +v -0.238686 -0.305273 0.339714 +v -0.233124 -0.325395 0.284627 +v -0.226689 -0.343961 0.224982 +v 0.794830 0.210144 0.184982 +v 0.783035 0.197990 0.247238 +v 0.773346 0.188263 0.308365 +v 0.635483 -0.001755 0.318039 +v 0.652969 -0.007186 0.244684 +v 0.672194 -0.015514 0.164115 +v 0.737867 0.038868 0.089194 +v 0.771404 0.102079 0.096926 +v 0.794769 0.164856 0.107590 +v -0.794769 0.164856 0.107590 +v -0.771404 0.102079 0.096926 +v -0.737867 0.038868 0.089194 +v -0.672194 -0.015514 0.164115 +v -0.652969 -0.007186 0.244684 +v -0.635483 -0.001755 0.318039 +v -0.773346 0.188263 0.308365 +v -0.783035 0.197990 0.247238 +v -0.794830 0.210144 0.184982 +v 0.000000 0.193176 -0.776550 +v 0.000000 0.296295 -0.783890 +v 0.000000 0.403137 -0.776978 +v 0.110046 0.503113 -0.742798 +v 0.215714 0.488289 -0.719845 +v 0.312622 0.467133 -0.689316 +v 0.379883 0.343964 -0.678452 +v 0.359807 0.249443 -0.683534 +v 0.339376 0.161621 -0.676290 +v -0.339376 0.161621 -0.676290 +v -0.359807 0.249443 -0.683534 +v -0.379883 0.343964 -0.678452 +v -0.312622 0.467133 -0.689316 +v -0.215714 0.488289 -0.719845 +v -0.110046 0.503113 -0.742798 +v 0.000000 0.936279 0.006714 +v 0.000000 0.919182 0.095558 +v 0.000000 0.889374 0.179230 +v 0.113586 0.842560 0.253754 +v 0.222965 0.833591 0.242950 +v 0.323929 0.818710 0.225128 +v 0.411880 0.839218 0.132599 +v 0.411686 0.866936 0.060116 +v 0.411621 0.882675 -0.015915 +v 0.323242 0.912277 -0.086288 +v 0.222412 0.929089 -0.084347 +v 0.113281 0.938843 -0.085571 +v -0.113281 0.938843 -0.085571 +v -0.222412 0.929089 -0.084347 +v -0.323242 0.912277 -0.086288 +v -0.411621 0.882675 -0.015915 +v -0.411686 0.866936 0.060116 +v -0.411880 0.839218 0.132599 +v -0.323929 0.818710 0.225128 +v -0.222965 0.833591 0.242950 +v -0.113586 0.842560 0.253754 +v 0.000000 0.890411 -0.383728 +v 0.000000 0.920967 -0.285049 +v 0.000000 0.937500 -0.185181 +v 0.411621 0.884445 -0.174179 +v 0.411648 0.869007 -0.254387 +v 0.411728 0.838654 -0.334091 +v 0.323776 0.814423 -0.429672 +v 0.222927 0.831100 -0.451183 +v 0.113586 0.840790 -0.469788 +v -0.113586 0.840790 -0.469788 +v -0.222927 0.831100 -0.451183 +v -0.323776 0.814423 -0.429672 +v -0.411728 0.838654 -0.334091 +v -0.411648 0.869007 -0.254387 +v -0.411621 0.884445 -0.174179 +v 0.000000 0.608948 -0.706665 +v 0.000000 0.700127 -0.643387 +v 0.000000 0.779449 -0.566101 +v 0.412155 0.721100 -0.488327 +v 0.410809 0.636353 -0.557426 +v 0.406128 0.541595 -0.615158 +v -0.406128 0.541595 -0.615158 +v -0.410809 0.636353 -0.557426 +v -0.412155 0.721100 -0.488327 +v 0.636200 0.473785 0.356812 +v 0.644787 0.463604 0.403679 +v 0.654861 0.462173 0.447845 +v 0.764847 0.380325 0.407288 +v 0.754848 0.384014 0.366776 +v 0.748199 0.393280 0.323975 +v 0.720795 0.437775 0.294250 +v 0.693604 0.459763 0.299194 +v 0.663727 0.478424 0.301208 +v -0.663727 0.478424 0.301208 +v -0.693604 0.459763 0.299194 +v -0.720795 0.437775 0.294250 +v -0.748199 0.393280 0.323975 +v -0.754848 0.384014 0.366776 +v -0.764847 0.380325 0.407288 +v -0.654861 0.462173 0.447845 +v -0.644787 0.463604 0.403679 +v -0.636200 0.473785 0.356812 +v 0.631012 0.630295 0.146179 +v 0.628906 0.581470 0.202225 +v 0.628265 0.534454 0.255981 +v 0.750031 0.436005 0.232666 +v 0.757420 0.467224 0.184349 +v 0.765976 0.499542 0.134125 +v 0.744247 0.570709 0.087555 +v 0.710789 0.607487 0.085426 +v 0.673615 0.641220 0.083008 +v -0.673615 0.641220 0.083008 +v -0.710789 0.607487 0.085426 +v -0.744247 0.570709 0.087555 +v -0.765976 0.499542 0.134125 +v -0.757420 0.467224 0.184349 +v -0.750031 0.436005 0.232666 +v -0.628265 0.534454 0.255981 +v -0.628906 0.581470 0.202225 +v -0.631012 0.630295 0.146179 +v 0.635742 0.736374 -0.099319 +v 0.635506 0.725868 -0.035526 +v 0.634796 0.705429 0.027039 +v 0.777145 0.551056 0.027557 +v 0.778267 0.566040 -0.028549 +v 0.777573 0.573700 -0.086090 +v 0.749435 0.621246 -0.147736 +v 0.716167 0.662449 -0.155849 +v 0.677734 0.700424 -0.163162 +v -0.677734 0.700424 -0.163162 +v -0.716167 0.662449 -0.155849 +v -0.749435 0.621246 -0.147736 +v -0.777573 0.573700 -0.086090 +v -0.778267 0.566040 -0.028549 +v -0.777145 0.551056 0.027557 +v -0.634796 0.705429 0.027039 +v -0.635506 0.725868 -0.035526 +v -0.635742 0.736374 -0.099319 +v 0.634583 0.687012 -0.351257 +v 0.635452 0.715847 -0.291309 +v 0.635742 0.732040 -0.228287 +v 0.774216 0.566620 -0.204132 +v 0.770621 0.553680 -0.260216 +v 0.763346 0.537018 -0.308899 +v 0.738505 0.544098 -0.359528 +v 0.710765 0.573814 -0.379356 +v 0.674377 0.606873 -0.395935 +v -0.674377 0.606873 -0.395935 +v -0.710765 0.573814 -0.379356 +v -0.738505 0.544098 -0.359528 +v -0.763346 0.537018 -0.308899 +v -0.770621 0.553680 -0.260216 +v -0.774216 0.566620 -0.204132 +v -0.635742 0.732040 -0.228287 +v -0.635452 0.715847 -0.291309 +v -0.634583 0.687012 -0.351257 +v 0.759867 0.482361 -0.358917 +v 0.762488 0.425424 -0.378743 +v 0.761274 0.360936 -0.397021 +v 0.717085 0.291265 -0.453998 +v 0.678115 0.303201 -0.489388 +v 0.638931 0.320465 -0.521622 +v 0.614761 0.425812 -0.529251 +v 0.624409 0.507454 -0.497013 +v 0.629944 0.581543 -0.455627 +v -0.629944 0.581543 -0.455627 +v -0.624409 0.507454 -0.497013 +v -0.614761 0.425812 -0.529251 +v -0.638931 0.320465 -0.521622 +v -0.678115 0.303201 -0.489388 +v -0.717085 0.291265 -0.453998 +v -0.761274 0.360936 -0.397021 +v -0.762488 0.425424 -0.378743 +v -0.759867 0.482361 -0.358917 +v 0.559891 0.365265 -0.577347 +v 0.515705 0.390503 -0.602722 +v 0.462646 0.416473 -0.628708 +v 0.483017 0.758514 -0.406418 +v 0.540997 0.721565 -0.406796 +v 0.589661 0.682190 -0.408508 +v -0.589661 0.682190 -0.408508 +v -0.540997 0.721565 -0.406796 +v -0.483017 0.758514 -0.406418 +v -0.462646 0.416473 -0.628708 +v -0.515705 0.390503 -0.602722 +v -0.559891 0.365265 -0.577347 +v 0.483398 0.855820 -0.111618 +v 0.542236 0.818367 -0.132553 +v 0.591797 0.778244 -0.151749 +v -0.591797 0.778244 -0.151749 +v -0.542236 0.818367 -0.132553 +v -0.483398 0.855820 -0.111618 +v 0.483780 0.771408 0.168976 +v 0.542065 0.740746 0.136147 +v 0.590729 0.707687 0.107300 +v -0.590729 0.707687 0.107300 +v -0.542065 0.740746 0.136147 +v -0.483780 0.771408 0.168976 +v 0.412918 0.741623 0.262482 +v 0.414330 0.678967 0.318829 +v 0.417007 0.618774 0.369522 +v 0.491104 0.553833 0.382095 +v 0.547070 0.535316 0.351215 +v 0.592438 0.515656 0.325256 +v -0.592438 0.515656 0.325256 +v -0.547070 0.535316 0.351215 +v -0.491104 0.553833 0.382095 +v -0.417007 0.618774 0.369522 +v -0.414330 0.678967 0.318829 +v -0.412918 0.741623 0.262482 +v 0.428177 0.540894 0.454178 +v 0.436027 0.529599 0.489762 +v 0.443861 0.533558 0.522935 +v -0.443861 0.533558 0.522935 +v -0.436027 0.529599 0.489762 +v -0.428177 0.540894 0.454178 +v 0.000000 0.786469 0.329742 +v 0.000000 0.717779 0.398201 +v 0.000000 0.645196 0.464722 +v 0.126516 0.585483 0.504364 +v 0.235505 0.587869 0.475262 +v 0.335037 0.581909 0.445511 +v -0.335037 0.581909 0.445511 +v -0.235505 0.587869 0.475262 +v -0.126516 0.585483 0.504364 +v 0.363905 0.549275 0.563492 +v 0.283154 0.537734 0.575201 +v 0.205477 0.521337 0.591559 +v -0.205477 0.521337 0.591559 +v -0.283154 0.537734 0.575201 +v -0.363905 0.549275 0.563492 +v 0.072042 0.533074 0.559875 +v 0.106400 0.511914 0.575975 +v 0.124961 0.498795 0.592413 +v -0.124961 0.498795 0.592413 +v -0.106400 0.511914 0.575975 +v -0.072042 0.533074 0.559875 +v 0.000000 0.517429 0.572815 +v 0.000000 0.486146 0.589752 +v 0.000000 0.461985 0.604553 +v 0.803802 0.280579 0.162704 +v 0.790878 0.330498 0.205212 +v 0.770599 0.373932 0.246765 +v -0.770599 0.373932 0.246765 +v -0.790878 0.330498 0.205212 +v -0.803802 0.280579 0.162704 +v 0.814484 0.243103 0.080429 +v 0.820479 0.262191 0.039192 +v 0.824854 0.280271 -0.001052 +v 0.826868 0.364866 -0.008010 +v 0.815554 0.424873 0.029408 +v 0.797409 0.479889 0.060944 +v -0.797409 0.479889 0.060944 +v -0.815554 0.424873 0.029408 +v -0.826868 0.364866 -0.008010 +v -0.824854 0.280271 -0.001052 +v -0.820479 0.262191 0.039192 +v -0.814484 0.243103 0.080429 +v 0.830927 0.331327 -0.084396 +v 0.829617 0.348434 -0.119975 +v 0.827384 0.355377 -0.158932 +v 0.815696 0.402008 -0.186886 +v 0.806429 0.458298 -0.168037 +v 0.794052 0.517609 -0.152740 +v -0.794052 0.517609 -0.152740 +v -0.806429 0.458298 -0.168037 +v -0.815696 0.402008 -0.186886 +v -0.827384 0.355377 -0.158932 +v -0.829617 0.348434 -0.119975 +v -0.830927 0.331327 -0.084396 +v 0.817710 0.359436 -0.255276 +v 0.806260 0.349939 -0.306080 +v 0.789168 0.328160 -0.357715 +v -0.789168 0.328160 -0.357715 +v -0.806260 0.349939 -0.306080 +v -0.817710 0.359436 -0.255276 +v 0.351906 -0.233124 0.137686 +v 0.307656 -0.276443 0.129658 +v 0.263157 -0.318693 0.134772 +v 0.317535 -0.258987 -0.235367 +v 0.361073 -0.228329 -0.224834 +v 0.401413 -0.197784 -0.215485 +v 0.435471 -0.185455 -0.122223 +v 0.429658 -0.191032 -0.034828 +v 0.421588 -0.189514 0.056397 +v -0.421588 -0.189514 0.056397 +v -0.429658 -0.191032 -0.034828 +v -0.435471 -0.185455 -0.122223 +v -0.401413 -0.197784 -0.215485 +v -0.361073 -0.228329 -0.224834 +v -0.317535 -0.258987 -0.235367 +v -0.263157 -0.318693 0.134772 +v -0.307656 -0.276443 0.129658 +v -0.351906 -0.233124 0.137686 +v 0.623979 -0.098661 -0.111633 +v 0.638588 -0.085276 -0.054644 +v 0.660841 -0.063243 0.005118 +v 0.620222 -0.072094 0.096793 +v 0.552218 -0.107371 0.115288 +v 0.482206 -0.142303 0.134624 +v 0.484482 -0.146637 -0.197418 +v 0.529046 -0.128257 -0.188179 +v 0.574083 -0.114438 -0.179077 +v -0.574083 -0.114438 -0.179077 +v -0.529046 -0.128257 -0.188179 +v -0.484482 -0.146637 -0.197418 +v -0.482206 -0.142303 0.134624 +v -0.552218 -0.107371 0.115288 +v -0.620222 -0.072094 0.096793 +v -0.660841 -0.063243 0.005118 +v -0.638588 -0.085276 -0.054644 +v -0.623979 -0.098661 -0.111633 +v 0.422770 -0.123208 0.382344 +v 0.424690 -0.137293 0.317075 +v 0.420031 -0.156677 0.245576 +v -0.420031 -0.156677 0.245576 +v -0.424690 -0.137293 0.317075 +v -0.422770 -0.123208 0.382344 +v 0.357259 -0.144845 0.440979 +v 0.312726 -0.170531 0.446785 +v 0.274099 -0.193771 0.458303 +v 0.269966 -0.259070 0.349723 +v 0.306856 -0.234033 0.299386 +v 0.349953 -0.210510 0.239584 +v -0.349953 -0.210510 0.239584 +v -0.306856 -0.234033 0.299386 +v -0.269966 -0.259070 0.349723 +v -0.274099 -0.193771 0.458303 +v -0.312726 -0.170531 0.446785 +v -0.357259 -0.144845 0.440979 +v 0.729658 0.210485 -0.432941 +v 0.701667 0.138200 -0.434731 +v 0.675156 0.069855 -0.421707 +v 0.613815 0.018890 -0.418839 +v 0.574016 0.024467 -0.446438 +v 0.531613 0.025513 -0.477020 +v 0.518541 0.102631 -0.544179 +v 0.549637 0.180359 -0.558137 +v 0.577225 0.259918 -0.560440 +v -0.577225 0.259918 -0.560440 +v -0.549637 0.180359 -0.558137 +v -0.518541 0.102631 -0.544179 +v -0.531613 0.025513 -0.477020 +v -0.574016 0.024467 -0.446438 +v -0.613815 0.018890 -0.418839 +v -0.675156 0.069855 -0.421707 +v -0.701667 0.138200 -0.434731 +v -0.729658 0.210485 -0.432941 +v 0.451972 0.050323 -0.565837 +v 0.418703 0.068764 -0.601710 +v 0.376078 0.078247 -0.631694 +v -0.376078 0.078247 -0.631694 +v -0.418703 0.068764 -0.601710 +v -0.451972 0.050323 -0.565837 +v 0.633591 -0.038177 -0.349991 +v 0.621407 -0.073562 -0.295605 +v 0.616442 -0.096403 -0.235016 +v 0.450424 -0.140900 -0.288727 +v 0.460945 -0.100151 -0.367421 +v 0.471971 -0.048767 -0.441457 +v -0.471971 -0.048767 -0.441457 +v -0.460945 -0.100151 -0.367421 +v -0.450424 -0.140900 -0.288727 +v -0.616442 -0.096403 -0.235016 +v -0.621407 -0.073562 -0.295605 +v -0.633591 -0.038177 -0.349991 +v 0.353495 -0.106049 -0.519084 +v 0.396066 -0.075356 -0.511523 +v 0.434790 -0.034973 -0.511058 +v -0.434790 -0.034973 -0.511058 +v -0.396066 -0.075356 -0.511523 +v -0.353495 -0.106049 -0.519084 +v 1.030685 0.424011 -0.305435 +v 1.031803 0.438168 -0.309219 +v 1.033051 0.449020 -0.320114 +v 0.994049 0.442123 -0.316818 +v 0.958504 0.427151 -0.296764 +v 0.927124 0.410522 -0.279419 +v 0.906067 0.388000 -0.247925 +v 0.912289 0.379608 -0.238754 +v 0.918381 0.368469 -0.236877 +v 0.946518 0.370850 -0.253662 +v 0.970940 0.384766 -0.269276 +v 0.998520 0.397217 -0.287552 +v -0.998520 0.397217 -0.287552 +v -0.970940 0.384766 -0.269276 +v -0.946518 0.370850 -0.253662 +v -0.918381 0.368469 -0.236877 +v -0.912289 0.379608 -0.238754 +v -0.906067 0.388000 -0.247925 +v -0.927124 0.410522 -0.279419 +v -0.958504 0.427151 -0.296764 +v -0.994049 0.442123 -0.316818 +v -1.033051 0.449020 -0.320114 +v -1.031803 0.438168 -0.309219 +v -1.030685 0.424011 -0.305435 +v 1.171661 0.470154 -0.404709 +v 1.125534 0.471889 -0.384773 +v 1.078766 0.465988 -0.362167 +v 1.066452 0.416748 -0.330215 +v 1.104980 0.421387 -0.352341 +v 1.143021 0.419678 -0.372604 +v 1.186600 0.425903 -0.388901 +v 1.196270 0.440460 -0.393715 +v 1.205902 0.451660 -0.403976 +v -1.205902 0.451660 -0.403976 +v -1.196270 0.440460 -0.393715 +v -1.186600 0.425903 -0.388901 +v -1.143021 0.419678 -0.372604 +v -1.104980 0.421387 -0.352341 +v -1.066452 0.416748 -0.330215 +v -1.078766 0.465988 -0.362167 +v -1.125534 0.471889 -0.384773 +v -1.171661 0.470154 -0.404709 +v 1.301773 0.351852 -0.433807 +v 1.280773 0.396816 -0.432667 +v 1.251251 0.432922 -0.428696 +v 1.207047 0.388428 -0.400375 +v 1.229679 0.358398 -0.407284 +v 1.245117 0.321045 -0.411133 +v 1.267334 0.286011 -0.412354 +v 1.283421 0.292782 -0.415474 +v 1.299210 0.297714 -0.422638 +v -1.299210 0.297714 -0.422638 +v -1.283421 0.292782 -0.415474 +v -1.267334 0.286011 -0.412354 +v -1.245117 0.321045 -0.411133 +v -1.229679 0.358398 -0.407284 +v -1.207047 0.388428 -0.400375 +v -1.251251 0.432922 -0.428696 +v -1.280773 0.396816 -0.432667 +v -1.301773 0.351852 -0.433807 +v 1.280762 0.128311 -0.432953 +v 1.302418 0.185547 -0.435410 +v 1.313187 0.243759 -0.435150 +v 1.251587 0.231201 -0.414429 +v 1.242054 0.182823 -0.413689 +v 1.224197 0.135345 -0.409515 +v 1.209854 0.086884 -0.399994 +v 1.222965 0.082424 -0.403839 +v 1.236206 0.078262 -0.412384 +v -1.236206 0.078262 -0.412384 +v -1.222965 0.082424 -0.403839 +v -1.209854 0.086884 -0.399994 +v -1.224197 0.135345 -0.409515 +v -1.242054 0.182823 -0.413689 +v -1.251587 0.231201 -0.414429 +v -1.313187 0.243759 -0.435150 +v -1.302418 0.185547 -0.435410 +v -1.280762 0.128311 -0.432953 +v 1.096161 -0.045563 -0.366776 +v 1.153557 -0.012135 -0.392834 +v 1.205078 0.028030 -0.412567 +v 1.164078 0.052704 -0.385345 +v 1.123638 0.019897 -0.364323 +v 1.078522 -0.007294 -0.337814 +v 1.030670 -0.041351 -0.303329 +v 1.031769 -0.053665 -0.306442 +v 1.033417 -0.064423 -0.316483 +v -1.033417 -0.064423 -0.316483 +v -1.031769 -0.053665 -0.306442 +v -1.030670 -0.041351 -0.303329 +v -1.078522 -0.007294 -0.337814 +v -1.123638 0.019897 -0.364323 +v -1.164078 0.052704 -0.385345 +v -1.205078 0.028030 -0.412567 +v -1.153557 -0.012135 -0.392834 +v -1.096161 -0.045563 -0.366776 +v 0.853378 -0.113098 -0.221054 +v 0.912281 -0.105400 -0.256226 +v 0.973480 -0.091949 -0.295792 +v 0.980988 -0.045013 -0.269760 +v 0.932007 -0.055920 -0.233154 +v 0.885223 -0.061966 -0.200943 +v 0.828644 -0.078751 -0.169815 +v 0.815937 -0.093983 -0.168777 +v 0.805466 -0.106995 -0.176682 +v -0.805466 -0.106995 -0.176682 +v -0.815937 -0.093983 -0.168777 +v -0.828644 -0.078751 -0.169815 +v -0.885223 -0.061966 -0.200943 +v -0.932007 -0.055920 -0.233154 +v -0.980988 -0.045013 -0.269760 +v -0.973480 -0.091949 -0.295792 +v -0.912281 -0.105400 -0.256226 +v -0.853378 -0.113098 -0.221054 +v 1.031281 -0.017792 -0.314011 +v 1.032978 -0.008224 -0.325447 +v 1.035202 -0.000458 -0.339096 +v 0.997299 -0.007416 -0.321335 +v 0.957905 -0.016121 -0.288761 +v 0.920990 -0.021423 -0.260468 +v 0.879608 -0.030579 -0.223663 +v 0.868652 -0.039402 -0.206234 +v 0.855988 -0.050369 -0.190384 +v -0.855988 -0.050369 -0.190384 +v -0.868652 -0.039402 -0.206234 +v -0.879608 -0.030579 -0.223663 +v -0.920990 -0.021423 -0.260468 +v -0.957905 -0.016121 -0.288761 +v -0.997299 -0.007416 -0.321335 +v -1.035202 -0.000458 -0.339096 +v -1.032978 -0.008224 -0.325447 +v -1.031281 -0.017792 -0.314011 +v 1.189163 0.094818 -0.405121 +v 1.182831 0.097713 -0.412758 +v 1.179123 0.099747 -0.422424 +v 1.149155 0.070328 -0.420593 +v 1.115120 0.044201 -0.403179 +v 1.077438 0.022491 -0.380844 +v -1.077438 0.022491 -0.380844 +v -1.115120 0.044201 -0.403179 +v -1.149155 0.070328 -0.420593 +v -1.179123 0.099747 -0.422424 +v -1.182831 0.097713 -0.412758 +v -1.189163 0.094818 -0.405121 +v 1.241089 0.269653 -0.417603 +v 1.232635 0.261295 -0.424965 +v 1.227295 0.253555 -0.434357 +v 1.223755 0.210526 -0.445587 +v 1.215405 0.172768 -0.444542 +v 1.200119 0.135574 -0.440735 +v -1.200119 0.135574 -0.440735 +v -1.215405 0.172768 -0.444542 +v -1.223755 0.210526 -0.445587 +v -1.227295 0.253555 -0.434357 +v -1.232635 0.261295 -0.424965 +v -1.241089 0.269653 -0.417603 +v 1.171280 0.392456 -0.394333 +v 1.166645 0.376003 -0.403038 +v 1.164001 0.361069 -0.414108 +v 1.187927 0.332382 -0.435287 +v 1.206711 0.309174 -0.440739 +v 1.219238 0.280289 -0.443634 +v -1.219238 0.280289 -0.443634 +v -1.206711 0.309174 -0.440739 +v -1.187927 0.332382 -0.435287 +v -1.164001 0.361069 -0.414108 +v -1.166645 0.376003 -0.403038 +v -1.171280 0.392456 -0.394333 +v 1.030380 0.391418 -0.316238 +v 1.031475 0.375347 -0.328472 +v 1.033264 0.360703 -0.343109 +v 1.067078 0.354965 -0.377716 +v 1.100464 0.358284 -0.396259 +v 1.133362 0.356735 -0.412888 +v -1.133362 0.356735 -0.412888 +v -1.100464 0.358284 -0.396259 +v -1.067078 0.354965 -0.377716 +v -1.033264 0.360703 -0.343109 +v -1.031475 0.375347 -0.328472 +v -1.030380 0.391418 -0.316238 +v 0.930527 0.342468 -0.250549 +v 0.936394 0.329506 -0.263512 +v 0.941757 0.317596 -0.278595 +v 0.964706 0.319855 -0.307281 +v 0.985210 0.330830 -0.322708 +v 1.008606 0.340378 -0.339996 +v -1.008606 0.340378 -0.339996 +v -0.985210 0.330830 -0.322708 +v -0.964706 0.319855 -0.307281 +v -0.941757 0.317596 -0.278595 +v -0.936394 0.329506 -0.263512 +v -0.930527 0.342468 -0.250549 +v 0.740367 0.027974 -0.053324 +v 0.734442 0.013895 -0.021985 +v 0.720594 -0.003643 0.016654 +v 0.659960 -0.089323 -0.145111 +v 0.687940 -0.070492 -0.141186 +v 0.710943 -0.050312 -0.148393 +v 0.717911 -0.023405 -0.141567 +v 0.717465 -0.013267 -0.117881 +v 0.725759 0.006388 -0.097127 +v -0.725759 0.006388 -0.097127 +v -0.717465 -0.013267 -0.117881 +v -0.717911 -0.023405 -0.141567 +v -0.710943 -0.050312 -0.148393 +v -0.687940 -0.070492 -0.141186 +v -0.659960 -0.089323 -0.145111 +v -0.720594 -0.003643 0.016654 +v -0.734442 0.013895 -0.021985 +v -0.740367 0.027974 -0.053324 +v 0.663622 -0.104886 -0.181702 +v 0.704430 -0.109092 -0.182919 +v 0.749069 -0.113464 -0.185226 +v 0.805267 -0.060806 -0.168045 +v 0.774245 -0.054182 -0.166624 +v 0.749720 -0.043862 -0.168127 +v -0.749720 -0.043862 -0.168127 +v -0.774245 -0.054182 -0.166624 +v -0.805267 -0.060806 -0.168045 +v -0.749069 -0.113464 -0.185226 +v -0.704430 -0.109092 -0.182919 +v -0.663622 -0.104886 -0.181702 +v 0.762950 0.087240 -0.069743 +v 0.786996 0.147679 -0.062308 +v 0.810083 0.216123 -0.056198 +v -0.810083 0.216123 -0.056198 +v -0.786996 0.147679 -0.062308 +v -0.762950 0.087240 -0.069743 +v 0.875732 0.373413 -0.255737 +v 0.855520 0.357300 -0.246154 +v 0.839072 0.348267 -0.232784 +v 0.836173 0.330811 -0.198544 +v 0.842489 0.314178 -0.200294 +v 0.847748 0.300171 -0.206879 +v 0.868134 0.305359 -0.221161 +v 0.885014 0.322739 -0.225975 +v 0.903854 0.339600 -0.232300 +v -0.903854 0.339600 -0.232300 +v -0.885014 0.322739 -0.225975 +v -0.868134 0.305359 -0.221161 +v -0.847748 0.300171 -0.206879 +v -0.842489 0.314178 -0.200294 +v -0.836173 0.330811 -0.198544 +v -0.839072 0.348267 -0.232784 +v -0.855520 0.357300 -0.246154 +v -0.875732 0.373413 -0.255737 +v 0.860687 -0.024109 -0.235199 +v 0.839066 -0.022263 -0.238602 +v 0.823456 -0.018555 -0.247040 +v 0.799530 -0.014709 -0.236847 +v 0.780412 -0.017301 -0.215336 +v 0.757909 -0.021534 -0.192613 +v -0.757909 -0.021534 -0.192613 +v -0.780412 -0.017301 -0.215336 +v -0.799530 -0.014709 -0.236847 +v -0.823456 -0.018555 -0.247040 +v -0.839066 -0.022263 -0.238602 +v -0.860687 -0.024109 -0.235199 +v 0.811249 -0.006348 -0.262054 +v 0.812626 0.002064 -0.265423 +v 0.815964 0.012161 -0.267410 +v 0.801132 0.026688 -0.252945 +v 0.780343 0.031166 -0.235267 +v 0.760182 0.038408 -0.216198 +v 0.739929 0.025763 -0.186423 +v 0.736937 0.006950 -0.181945 +v 0.734513 -0.009520 -0.175472 +v -0.734513 -0.009520 -0.175472 +v -0.736937 0.006950 -0.181945 +v -0.739929 0.025763 -0.186423 +v -0.760182 0.038408 -0.216198 +v -0.780343 0.031166 -0.235267 +v -0.801132 0.026688 -0.252945 +v -0.815964 0.012161 -0.267410 +v -0.812626 0.002064 -0.265423 +v -0.811249 -0.006348 -0.262054 +v 0.801432 0.074392 -0.278249 +v 0.789988 0.080167 -0.274526 +v 0.780396 0.087341 -0.271759 +v 0.771118 0.091003 -0.254852 +v 0.764384 0.081542 -0.237748 +v 0.756144 0.069393 -0.218110 +v 0.819443 0.037857 -0.271378 +v 0.817710 0.051469 -0.274431 +v 0.814189 0.062795 -0.278249 +v -0.814189 0.062795 -0.278249 +v -0.817710 0.051469 -0.274431 +v -0.819443 0.037857 -0.271378 +v -0.756144 0.069393 -0.218110 +v -0.764384 0.081542 -0.237748 +v -0.771118 0.091003 -0.254852 +v -0.780396 0.087341 -0.271759 +v -0.789988 0.080167 -0.274526 +v -0.801432 0.074392 -0.278249 +v 0.781860 0.113098 -0.269318 +v 0.793938 0.131920 -0.269291 +v 0.809906 0.152618 -0.269516 +v 0.820099 0.176910 -0.254929 +v 0.812870 0.182026 -0.238430 +v 0.807114 0.189621 -0.219299 +v 0.787827 0.166306 -0.192627 +v 0.770417 0.128724 -0.189960 +v 0.755025 0.090532 -0.189526 +v -0.755025 0.090532 -0.189526 +v -0.770417 0.128724 -0.189960 +v -0.787827 0.166306 -0.192627 +v -0.807114 0.189621 -0.219299 +v -0.812870 0.182026 -0.238430 +v -0.820099 0.176910 -0.254929 +v -0.809906 0.152618 -0.269516 +v -0.793938 0.131920 -0.269291 +v -0.781860 0.113098 -0.269318 +v 0.877655 0.254745 -0.255615 +v 0.869492 0.264492 -0.241722 +v 0.861053 0.275635 -0.228607 +v 0.841156 0.269958 -0.211884 +v 0.829868 0.250561 -0.206688 +v 0.818161 0.227890 -0.201538 +v 0.842499 0.193390 -0.269089 +v 0.856888 0.212070 -0.268581 +v 0.870697 0.229843 -0.268616 +v -0.870697 0.229843 -0.268616 +v -0.856888 0.212070 -0.268581 +v -0.842499 0.193390 -0.269089 +v -0.818161 0.227890 -0.201538 +v -0.829868 0.250561 -0.206688 +v -0.841156 0.269958 -0.211884 +v -0.861053 0.275635 -0.228607 +v -0.869492 0.264492 -0.241722 +v -0.877655 0.254745 -0.255615 +v 0.898651 0.263351 -0.272522 +v 0.913548 0.279072 -0.277306 +v 0.929367 0.293854 -0.284454 +v -0.929367 0.293854 -0.284454 +v -0.913548 0.279072 -0.277306 +v -0.898651 0.263351 -0.272522 +v 0.821039 0.259366 -0.100540 +v 0.813181 0.234149 -0.138187 +v 0.807175 0.214828 -0.169983 +v -0.807175 0.214828 -0.169983 +v -0.813181 0.234149 -0.138187 +v -0.821039 0.259366 -0.100540 +v 0.740529 0.046611 -0.107198 +v 0.739494 0.051340 -0.133993 +v 0.739990 0.052955 -0.162009 +v -0.739990 0.052955 -0.162009 +v -0.739494 0.051340 -0.133993 +v -0.740529 0.046611 -0.107198 +v 0.958176 0.287628 -0.335403 +v 0.953453 0.293823 -0.323025 +v 0.949875 0.300018 -0.309235 +v 0.888763 0.241379 -0.283569 +v 0.892212 0.236816 -0.296761 +v 0.896393 0.231766 -0.308990 +v 0.917145 0.239700 -0.323822 +v 0.932117 0.254009 -0.329296 +v 0.948044 0.267548 -0.336624 +v -0.948044 0.267548 -0.336624 +v -0.932117 0.254009 -0.329296 +v -0.917145 0.239700 -0.323822 +v -0.896393 0.231766 -0.308990 +v -0.892212 0.236816 -0.296761 +v -0.888763 0.241379 -0.283569 +v -0.949875 0.300018 -0.309235 +v -0.953453 0.293823 -0.323025 +v -0.958176 0.287628 -0.335403 +v 0.831818 0.171539 -0.283188 +v 0.835770 0.169498 -0.295410 +v 0.840118 0.166367 -0.305923 +v 0.861298 0.177597 -0.314896 +v 0.875488 0.193756 -0.315777 +v 0.889191 0.209488 -0.317352 +v -0.889191 0.209488 -0.317352 +v -0.875488 0.193756 -0.315777 +v -0.861298 0.177597 -0.314896 +v -0.840118 0.166367 -0.305923 +v -0.835770 0.169498 -0.295410 +v -0.831818 0.171539 -0.283188 +v 0.780396 0.101624 -0.283783 +v 0.784648 0.103930 -0.295408 +v 0.790812 0.105845 -0.304098 +v 0.803019 0.115519 -0.311330 +v 0.814120 0.128226 -0.313127 +v 0.829437 0.143967 -0.313980 +v -0.829437 0.143967 -0.313980 +v -0.814120 0.128226 -0.313127 +v -0.803019 0.115519 -0.311330 +v -0.790812 0.105845 -0.304098 +v -0.784648 0.103930 -0.295408 +v -0.780396 0.101624 -0.283783 +v 0.814067 0.071798 -0.288228 +v 0.818541 0.073453 -0.297033 +v 0.823868 0.075043 -0.306152 +v 0.819595 0.087189 -0.313904 +v 0.808394 0.094813 -0.313108 +v 0.800700 0.101878 -0.311330 +v -0.800700 0.101878 -0.311330 +v -0.808394 0.094813 -0.313108 +v -0.819595 0.087189 -0.313904 +v -0.823868 0.075043 -0.306152 +v -0.818541 0.073453 -0.297033 +v -0.814067 0.071798 -0.288228 +v 0.830368 0.022354 -0.283401 +v 0.838024 0.021950 -0.295757 +v 0.844727 0.023209 -0.305878 +v 0.852783 0.039688 -0.314362 +v 0.848652 0.053326 -0.314629 +v 0.841385 0.066437 -0.314514 +v -0.841385 0.066437 -0.314514 +v -0.848652 0.053326 -0.314629 +v -0.852783 0.039688 -0.314362 +v -0.844727 0.023209 -0.305878 +v -0.838024 0.021950 -0.295757 +v -0.830368 0.022354 -0.283401 +v 0.822968 -0.012146 -0.272736 +v 0.828807 -0.010943 -0.285507 +v 0.834300 -0.008921 -0.293854 +v 0.840647 -0.002360 -0.303558 +v 0.844348 0.004922 -0.308838 +v 0.849487 0.014725 -0.311920 +v -0.849487 0.014725 -0.311920 +v -0.844348 0.004922 -0.308838 +v -0.840647 -0.002360 -0.303558 +v -0.834300 -0.008921 -0.293854 +v -0.828807 -0.010943 -0.285507 +v -0.822968 -0.012146 -0.272736 +v 0.893280 -0.019348 -0.256866 +v 0.896584 -0.015266 -0.271233 +v 0.899353 -0.009979 -0.284363 +v 0.877808 -0.005035 -0.290649 +v 0.858332 -0.006922 -0.291718 +v 0.845561 -0.007273 -0.295166 +v -0.845561 -0.007273 -0.295166 +v -0.858332 -0.006922 -0.291718 +v -0.877808 -0.005035 -0.290649 +v -0.899353 -0.009979 -0.284363 +v -0.896584 -0.015266 -0.271233 +v -0.893280 -0.019348 -0.256866 +v 1.046138 0.326182 -0.396118 +v 1.041976 0.332564 -0.385979 +v 1.038513 0.339523 -0.373138 +v 0.983627 0.291595 -0.357925 +v 1.003871 0.301905 -0.371647 +v 1.026240 0.311045 -0.386719 +v -1.026240 0.311045 -0.386719 +v -1.003871 0.301905 -0.371647 +v -0.983627 0.291595 -0.357925 +v -1.038513 0.339523 -0.373138 +v -1.041976 0.332564 -0.385979 +v -1.046138 0.326182 -0.396118 +v 1.164851 0.326487 -0.454712 +v 1.165801 0.332884 -0.448574 +v 1.164612 0.339767 -0.438461 +v 1.078832 0.325734 -0.418823 +v 1.107404 0.329137 -0.433746 +v 1.134801 0.327077 -0.445801 +v -1.134801 0.327077 -0.445801 +v -1.107404 0.329137 -0.433746 +v -1.078832 0.325734 -0.418823 +v -1.164612 0.339767 -0.438461 +v -1.165801 0.332884 -0.448574 +v -1.164851 0.326487 -0.454712 +v 1.223663 0.235428 -0.469208 +v 1.226601 0.238472 -0.464722 +v 1.225952 0.242142 -0.455841 +v 1.180842 0.304576 -0.461060 +v 1.197409 0.284644 -0.464745 +v 1.208649 0.260269 -0.466522 +v -1.208649 0.260269 -0.466522 +v -1.197409 0.284644 -0.464745 +v -1.180842 0.304576 -0.461060 +v -1.225952 0.242142 -0.455841 +v -1.226601 0.238472 -0.464722 +v -1.223663 0.235428 -0.469208 +v 1.177444 0.104263 -0.456589 +v 1.180283 0.101372 -0.452328 +v 1.179428 0.100784 -0.443787 +v 1.212067 0.202225 -0.466522 +v 1.203686 0.170982 -0.464695 +v 1.188858 0.140152 -0.460861 +v -1.188858 0.140152 -0.460861 +v -1.203686 0.170982 -0.464695 +v -1.212067 0.202225 -0.466522 +v -1.179428 0.100784 -0.443787 +v -1.180283 0.101372 -0.452328 +v -1.177444 0.104263 -0.456589 +v 1.041046 0.018112 -0.388245 +v 1.041447 0.012196 -0.379482 +v 1.040024 0.008698 -0.367477 +v 1.140701 0.085403 -0.444809 +v 1.109051 0.063019 -0.431561 +v 1.074310 0.044113 -0.414246 +v -1.074310 0.044113 -0.414246 +v -1.109051 0.063019 -0.431561 +v -1.140701 0.085403 -0.444809 +v -1.040024 0.008698 -0.367477 +v -1.041447 0.012196 -0.379482 +v -1.041046 0.018112 -0.388245 +v 1.001434 0.017197 -0.365662 +v 0.965965 0.008656 -0.338028 +v 0.932739 0.002533 -0.313599 +v -0.932739 0.002533 -0.313599 +v -0.965965 0.008656 -0.338028 +v -1.001434 0.017197 -0.365662 +v 0.873276 0.102905 -0.326996 +v 0.857224 0.092369 -0.323814 +v 0.842972 0.083954 -0.319824 +v 0.866333 0.032181 -0.318451 +v 0.882854 0.039944 -0.322063 +v 0.901657 0.049469 -0.325760 +v 0.918442 0.075470 -0.333450 +v 0.911446 0.089241 -0.333691 +v 0.901657 0.102295 -0.332367 +v -0.901657 0.102295 -0.332367 +v -0.911446 0.089241 -0.333691 +v -0.918442 0.075470 -0.333450 +v -0.901657 0.049469 -0.325760 +v -0.882854 0.039944 -0.322063 +v -0.866333 0.032181 -0.318451 +v -0.842972 0.083954 -0.319824 +v -0.857224 0.092369 -0.323814 +v -0.873276 0.102905 -0.326996 +v 0.939102 0.158615 -0.343948 +v 0.923141 0.143383 -0.338848 +v 0.906784 0.128784 -0.334198 +v 0.941513 0.072540 -0.339005 +v 0.961300 0.085659 -0.348835 +v 0.980835 0.099686 -0.359314 +v 0.989197 0.132523 -0.363281 +v 0.978367 0.147614 -0.358387 +v 0.966995 0.161057 -0.353958 +v -0.966995 0.161057 -0.353958 +v -0.978367 0.147614 -0.358387 +v -0.989197 0.132523 -0.363281 +v -0.980835 0.099686 -0.359314 +v -0.961300 0.085659 -0.348835 +v -0.941513 0.072540 -0.339005 +v -0.906784 0.128784 -0.334198 +v -0.923141 0.143383 -0.338848 +v -0.939102 0.158615 -0.343948 +v 0.998886 0.218735 -0.365829 +v 0.984085 0.204552 -0.360260 +v 0.969437 0.189560 -0.354752 +v 1.018677 0.130020 -0.377747 +v 1.036739 0.145741 -0.384743 +v 1.054062 0.161194 -0.390472 +v 1.051865 0.191528 -0.385040 +v 1.037575 0.205608 -0.378803 +v 1.025681 0.218796 -0.374802 +v -1.025681 0.218796 -0.374802 +v -1.037575 0.205608 -0.378803 +v -1.051865 0.191528 -0.385040 +v -1.054062 0.161194 -0.390472 +v -1.036739 0.145741 -0.384743 +v -1.018677 0.130020 -0.377747 +v -0.969437 0.189560 -0.354752 +v -0.984085 0.204552 -0.360260 +v -0.998886 0.218735 -0.365829 +v 1.066752 0.263972 -0.392288 +v 1.047858 0.253749 -0.383759 +v 1.030441 0.243210 -0.377121 +v 1.085983 0.189392 -0.398773 +v 1.100493 0.202070 -0.402608 +v 1.114085 0.214345 -0.407705 +v 1.116801 0.233144 -0.408590 +v 1.107351 0.245968 -0.405114 +v 1.097758 0.260249 -0.403763 +v -1.097758 0.260249 -0.403763 +v -1.107351 0.245968 -0.405114 +v -1.116801 0.233144 -0.408590 +v -1.114085 0.214345 -0.407705 +v -1.100493 0.202070 -0.402608 +v -1.085983 0.189392 -0.398773 +v -1.030441 0.243210 -0.377121 +v -1.047858 0.253749 -0.383759 +v -1.066752 0.263972 -0.392288 +v 1.075704 0.287206 -0.403824 +v 1.064828 0.298784 -0.404179 +v 1.056982 0.309621 -0.404541 +v 0.975449 0.270050 -0.354752 +v 0.987877 0.258121 -0.361694 +v 1.001205 0.245102 -0.367172 +v -1.001205 0.245102 -0.367172 +v -0.987877 0.258121 -0.361694 +v -0.975449 0.270050 -0.354752 +v -1.056982 0.309621 -0.404541 +v -1.064828 0.298784 -0.404179 +v -1.075704 0.287206 -0.403824 +v 0.913361 0.214493 -0.329254 +v 0.926373 0.201904 -0.337139 +v 0.940567 0.188095 -0.343765 +v -0.940567 0.188095 -0.343765 +v -0.926373 0.201904 -0.337139 +v -0.913361 0.214493 -0.329254 +v 0.855011 0.152267 -0.320511 +v 0.865910 0.141094 -0.324810 +v 0.877914 0.128418 -0.327850 +v -0.877914 0.128418 -0.327850 +v -0.865910 0.141094 -0.324810 +v -0.855011 0.152267 -0.320511 +v 0.908752 0.010895 -0.307678 +v 0.914974 0.026566 -0.317524 +v 0.919846 0.043610 -0.325455 +v -0.919846 0.043610 -0.325455 +v -0.914974 0.026566 -0.317524 +v -0.908752 0.010895 -0.307678 +v 1.031281 0.046616 -0.390686 +v 1.022057 0.068726 -0.384964 +v 1.011292 0.092300 -0.377197 +v -1.011292 0.092300 -0.377197 +v -1.022057 0.068726 -0.384964 +v -1.031281 0.046616 -0.390686 +v 1.148209 0.123734 -0.443710 +v 1.122757 0.140034 -0.427921 +v 1.095505 0.158081 -0.410553 +v -1.095505 0.158081 -0.410553 +v -1.122757 0.140034 -0.427921 +v -1.148209 0.123734 -0.443710 +v 1.193634 0.229019 -0.455536 +v 1.168360 0.226022 -0.439657 +v 1.143138 0.224386 -0.423940 +v -1.143138 0.224386 -0.423940 +v -1.168360 0.226022 -0.439657 +v -1.193634 0.229019 -0.455536 +v 1.148351 0.309072 -0.447266 +v 1.131555 0.297654 -0.434330 +v 1.110128 0.285924 -0.418716 +v -1.110128 0.285924 -0.418716 +v -1.131555 0.297654 -0.434330 +v -1.148351 0.309072 -0.447266 +v 1.039841 -0.042694 -0.422882 +v 1.038792 -0.067886 -0.392254 +v 1.037201 -0.075531 -0.361221 +v 0.796005 -0.117615 -0.227646 +v 0.797813 -0.107777 -0.266869 +v 0.803452 -0.080826 -0.307358 +v 0.863327 -0.031082 -0.360336 +v 0.918953 -0.024364 -0.387272 +v 0.978256 -0.011688 -0.418488 +v -0.978256 -0.011688 -0.418488 +v -0.918953 -0.024364 -0.387272 +v -0.863327 -0.031082 -0.360336 +v -0.803452 -0.080826 -0.307358 +v -0.797813 -0.107777 -0.266869 +v -0.796005 -0.117615 -0.227646 +v -1.037201 -0.075531 -0.361221 +v -1.038792 -0.067886 -0.392254 +v -1.039841 -0.042694 -0.422882 +v 1.267446 0.093613 -0.487467 +v 1.264524 0.077164 -0.466296 +v 1.257935 0.072708 -0.444855 +v 1.103317 0.030182 -0.471710 +v 1.164407 0.058521 -0.488665 +v 1.219839 0.091110 -0.499797 +v -1.219839 0.091110 -0.499797 +v -1.164407 0.058521 -0.488665 +v -1.103317 0.030182 -0.471710 +v -1.257935 0.072708 -0.444855 +v -1.264524 0.077164 -0.466296 +v -1.267446 0.093613 -0.487467 +v 1.328525 0.291392 -0.483700 +v 1.328549 0.296187 -0.467185 +v 1.323074 0.299362 -0.449738 +v 1.299632 0.166977 -0.506429 +v 1.320008 0.209075 -0.503966 +v 1.326755 0.253062 -0.499753 +v -1.326755 0.253062 -0.499753 +v -1.320008 0.209075 -0.503966 +v -1.299632 0.166977 -0.506429 +v -1.323074 0.299362 -0.449738 +v -1.328549 0.296187 -0.467185 +v -1.328525 0.291392 -0.483700 +v 1.231069 0.425323 -0.490382 +v 1.226156 0.448555 -0.466769 +v 1.220917 0.458252 -0.442123 +v 1.318027 0.315898 -0.501248 +v 1.301194 0.348666 -0.507114 +v 1.273631 0.371735 -0.510442 +v -1.273631 0.371735 -0.510442 +v -1.301194 0.348666 -0.507114 +v -1.318027 0.315898 -0.501248 +v -1.220917 0.458252 -0.442123 +v -1.226156 0.448555 -0.466769 +v -1.231069 0.425323 -0.490382 +v 1.034561 0.413788 -0.427643 +v 1.034451 0.443317 -0.397373 +v 1.034393 0.455185 -0.366196 +v 1.191152 0.387970 -0.502162 +v 1.140443 0.383362 -0.489623 +v 1.087418 0.373505 -0.472687 +v -1.087418 0.373505 -0.472687 +v -1.140443 0.383362 -0.489623 +v -1.191152 0.387970 -0.502162 +v -1.034393 0.455185 -0.366196 +v -1.034451 0.443317 -0.397373 +v -1.034561 0.413788 -0.427643 +v 0.876419 0.353439 -0.353149 +v 0.885319 0.380886 -0.323669 +v 0.892883 0.392029 -0.292603 +v 0.986404 0.347748 -0.429230 +v 0.941734 0.334064 -0.406700 +v 0.901382 0.319565 -0.387756 +v -0.901382 0.319565 -0.387756 +v -0.941734 0.334064 -0.406700 +v -0.986404 0.347748 -0.429230 +v -0.892883 0.392029 -0.292603 +v -0.885319 0.380886 -0.323669 +v -0.876419 0.353439 -0.353149 +v 1.036331 0.278656 -0.466461 +v 1.037888 0.182251 -0.470444 +v 1.039291 0.086578 -0.464386 +v 0.824631 0.045456 -0.368454 +v 0.838425 0.136238 -0.382610 +v 0.852554 0.227036 -0.385132 +v -0.852554 0.227036 -0.385132 +v -0.838425 0.136238 -0.382610 +v -0.824631 0.045456 -0.368454 +v -1.039291 0.086578 -0.464386 +v -1.037888 0.182251 -0.470444 +v -1.036331 0.278656 -0.466461 +v 1.243479 0.323639 -0.520269 +v 1.251399 0.252888 -0.523042 +v 1.259837 0.184006 -0.517782 +v -1.259837 0.184006 -0.517782 +v -1.251399 0.252888 -0.523042 +v -1.243479 0.323639 -0.520269 +v 0.834915 0.287277 -0.372986 +v 0.807968 0.274511 -0.378812 +v 0.783674 0.270970 -0.391528 +v -0.783674 0.270970 -0.391528 +v -0.807968 0.274511 -0.378812 +v -0.834915 0.287277 -0.372986 +v 0.767685 -0.025284 -0.341354 +v 0.727104 -0.014782 -0.351410 +v 0.689072 -0.002350 -0.369949 +v -0.689072 -0.002350 -0.369949 +v -0.727104 -0.014782 -0.351410 +v -0.767685 -0.025284 -0.341354 +v 0.438988 0.160133 0.757462 +v -0.438988 0.160133 0.757462 +v 0.484131 0.111679 0.685059 +v -0.484131 0.111679 0.685059 +v 0.528564 0.071777 0.581787 +v -0.528564 0.071777 0.581787 +v 0.351562 0.000977 0.615967 +v -0.351562 0.000977 0.615967 +v 0.351753 0.057343 0.711617 +v -0.351753 0.057343 0.711617 +v 0.352592 0.126175 0.774765 +v -0.352592 0.126175 0.774765 +v 0.269508 0.160133 0.789360 +v -0.269508 0.160133 0.789360 +v 0.220024 0.111679 0.732613 +v -0.220024 0.111679 0.732613 +v 0.174561 0.071777 0.644775 +v -0.174561 0.071777 0.644775 +v 0.102539 0.243408 0.653809 +v -0.102539 0.243408 0.653809 +v 0.165497 0.242569 0.736923 +v -0.165497 0.242569 0.736923 +v 0.235550 0.243217 0.791992 +v -0.235550 0.243217 0.791992 +v 0.269508 0.329803 0.789360 +v -0.269508 0.329803 0.789360 +v 0.220024 0.374756 0.732613 +v -0.220024 0.374756 0.732613 +v 0.174561 0.419189 0.644775 +v -0.174561 0.419189 0.644775 +v 0.351562 0.491211 0.615967 +v -0.351562 0.491211 0.615967 +v 0.351753 0.429474 0.711617 +v -0.351753 0.429474 0.711617 +v 0.352592 0.364792 0.774765 +v -0.352592 0.364792 0.774765 +v 0.438988 0.329803 0.757462 +v -0.438988 0.329803 0.757462 +v 0.484131 0.374756 0.685059 +v -0.484131 0.374756 0.685059 +v 0.528564 0.419189 0.581787 +v -0.528564 0.419189 0.581787 +v 0.600586 0.243408 0.567383 +v -0.600586 0.243408 0.567383 +v 0.538849 0.242569 0.672714 +v -0.538849 0.242569 0.672714 +v 0.473328 0.243217 0.749649 +v -0.473328 0.243217 0.749649 +v 0.451131 0.243281 0.779948 +v -0.451131 0.243281 0.779948 +v 0.423937 0.315465 0.786603 +v -0.423937 0.315465 0.786603 +v 0.352656 0.345576 0.803022 +v -0.352656 0.345576 0.803022 +v 0.284037 0.315465 0.815430 +v -0.284037 0.315465 0.815430 +v 0.255020 0.243281 0.817617 +v -0.255020 0.243281 0.817617 +v 0.284037 0.174662 0.815430 +v -0.284037 0.174662 0.815430 +v 0.352135 0.243046 0.820114 +v -0.352135 0.243046 0.820114 +v 0.352656 0.145645 0.803022 +v -0.352656 0.145645 0.803022 +v 0.423937 0.174662 0.786603 +v -0.423937 0.174662 0.786603 +v 0.000000 0.433731 0.735626 +v 0.000000 0.353045 0.805537 +v 0.000000 -0.655884 0.736816 +v 0.000000 -0.302765 0.778809 +v 0.000000 -0.172394 0.789932 +v 0.000000 -0.762909 0.713486 +v 0.000000 0.445897 0.621857 +v 0.000000 0.566464 0.538140 +v 0.000000 0.843887 -0.478363 +v 0.000000 0.508942 -0.752380 +v 0.000000 0.098541 -0.758392 +v 0.000000 -0.344894 -0.293579 +v 0.232994 -0.174835 0.580162 +v -0.232994 -0.174835 0.580162 +v 0.282936 -0.444519 0.564590 +v -0.282936 -0.444519 0.564590 +v 0.317307 -0.657394 0.556541 +v -0.317307 -0.657394 0.556541 +v 0.332092 -0.835693 0.532547 +v -0.332092 -0.835693 0.532547 +v 0.295815 -0.926086 0.526527 +v -0.295815 -0.926086 0.526527 +v 0.169159 -0.958145 0.547554 +v -0.169159 -0.958145 0.547554 +v 0.000000 -0.971405 0.563019 +v 0.424149 -0.111252 0.549370 +v -0.424149 -0.111252 0.549370 +v 0.623009 -0.006218 0.523003 +v -0.623009 -0.006218 0.523003 +v 0.780174 0.171974 0.489716 +v -0.780174 0.171974 0.489716 +v 0.806847 0.385132 0.569221 +v -0.806847 0.385132 0.569221 +v 0.688164 0.478844 0.624237 +v -0.688164 0.478844 0.624237 +v 0.491653 0.589699 0.680824 +v -0.491653 0.589699 0.680824 +v 0.322945 0.700859 0.724800 +v -0.322945 0.700859 0.724800 +v 0.178757 0.665802 0.745796 +v -0.178757 0.665802 0.745796 +v 0.078712 0.501457 0.742294 +v -0.078712 0.501457 0.742294 +v 0.167419 0.406433 0.782555 +v -0.167419 0.406433 0.782555 +v 0.122101 0.310944 0.775848 +v -0.122101 0.310944 0.775848 +v 0.215141 0.067276 0.749245 +v -0.215141 0.067276 0.749245 +v 0.370308 0.029854 0.707855 +v -0.370308 0.029854 0.707855 +v 0.508286 0.078255 0.672905 +v -0.508286 0.078255 0.672905 +v 0.616379 0.190056 0.651566 +v -0.616379 0.190056 0.651566 +v 0.639595 0.300499 0.657822 +v -0.639595 0.300499 0.657822 +v 0.586990 0.377670 0.684624 +v -0.586990 0.377670 0.684624 +v 0.441628 0.444633 0.733093 +v -0.441628 0.444633 0.733093 +v 0.245689 0.474625 0.774139 +v -0.245689 0.474625 0.774139 +v 0.000000 -0.742203 0.732315 +v 0.113359 -0.742727 0.723766 +v -0.113359 -0.742727 0.723766 +v 0.123024 -0.835480 0.700142 +v -0.123024 -0.835480 0.700142 +v 0.072800 -0.882545 0.685928 +v -0.072800 -0.882545 0.685928 +v 0.000000 -0.893906 0.681473 +v 0.000000 -0.168823 0.764771 +v 0.000000 -0.132636 0.749864 +v 0.094810 -0.148132 0.755920 +v -0.094810 -0.148132 0.755920 +v 0.123512 -0.224464 0.751259 +v -0.123512 -0.224464 0.751259 +v 0.090695 -0.303336 0.744566 +v -0.090695 -0.303336 0.744566 +v 0.392715 -0.042336 0.666000 +v -0.392715 -0.042336 0.666000 +v 0.588730 0.050301 0.619125 +v -0.588730 0.050301 0.619125 +v 0.709625 0.196564 0.599350 +v -0.709625 0.196564 0.599350 +v 0.731354 0.350777 0.644463 +v -0.731354 0.350777 0.644463 +v 0.651794 0.430161 0.702644 +v -0.651794 0.430161 0.702644 +v 0.461113 0.534515 0.763077 +v -0.461113 0.534515 0.763077 +v 0.318604 0.614799 0.802139 +v -0.318604 0.614799 0.802139 +v 0.205490 0.586517 0.818985 +v -0.205490 0.586517 0.818985 +v 0.107132 0.451431 0.815865 +v -0.107132 0.451431 0.815865 +v 0.149190 -0.094742 0.763808 +v -0.149190 -0.094742 0.763808 +v 0.207657 -0.462860 0.691597 +v -0.207657 -0.462860 0.691597 +v 0.240883 -0.677681 0.675941 +v -0.240883 -0.677681 0.675941 +v 0.251091 -0.811577 0.653648 +v -0.251091 -0.811577 0.653648 +v 0.222321 -0.895866 0.632431 +v -0.222321 -0.895866 0.632431 +v 0.139610 -0.927322 0.631210 +v -0.139610 -0.927322 0.631210 +v 0.000000 -0.938370 0.635742 +v 0.000000 0.040098 0.742762 +v 0.000000 0.209564 0.764862 +v 0.330376 0.486023 0.758629 +v -0.330376 0.486023 0.758629 +v 0.146553 0.131699 0.753433 +v -0.146553 0.131699 0.753433 +v 0.121254 0.214485 0.762886 +v -0.121254 0.214485 0.762886 +v 0.114021 -0.659004 0.729111 +v -0.114021 -0.659004 0.729111 +v 0.091896 -0.460457 0.740234 +v -0.091896 -0.460457 0.740234 +v 0.000000 -0.462097 0.746338 +v 0.000000 -0.334534 0.749512 +v 0.081924 -0.272026 0.781250 +v -0.081924 -0.272026 0.781250 +v 0.118546 -0.220001 0.789742 +v -0.118546 -0.220001 0.789742 +v 0.098007 -0.151390 0.782280 +v -0.098007 -0.151390 0.782280 +v 0.040016 -0.141708 0.782280 +v -0.040016 -0.141708 0.782280 +v 0.000000 -0.201692 0.821461 +v 0.048647 -0.158945 0.808666 +v -0.048647 -0.158945 0.808666 +v 0.086842 -0.166142 0.808666 +v -0.086842 -0.166142 0.808666 +v 0.090530 -0.217484 0.817329 +v -0.090530 -0.217484 0.817329 +v 0.068920 -0.256510 0.804380 +v -0.068920 -0.256510 0.804380 +v 0.000000 -0.270981 0.804688 +v 0.243431 -0.316238 0.561630 +v -0.243431 -0.316238 0.561630 +v 0.164711 -0.233032 0.703346 +v -0.164711 -0.233032 0.703346 +v 0.180450 -0.319366 0.692169 +v -0.180450 -0.319366 0.692169 +v 0.218315 -0.243401 0.568802 +v -0.218315 -0.243401 0.568802 +v 0.000000 -0.872559 0.679337 +v 0.049126 -0.861275 0.683075 +v -0.049126 -0.861275 0.683075 +v 0.089706 -0.815887 0.699684 +v -0.089706 -0.815887 0.699684 +v 0.081352 -0.757790 0.714165 +v -0.081352 -0.757790 0.714165 +v 0.000000 -0.783615 0.664413 +v 0.073685 -0.776436 0.665334 +v -0.073685 -0.776436 0.665334 +v 0.083478 -0.808974 0.660379 +v -0.083478 -0.808974 0.660379 +v 0.051529 -0.835922 0.647016 +v -0.051529 -0.835922 0.647016 +v 0.000000 -0.846024 0.645401 +v 0.172714 0.221382 0.772057 +v -0.172714 0.221382 0.772057 +v 0.189751 0.158691 0.767982 +v -0.189751 0.158691 0.767982 +v 0.338379 0.427437 0.757698 +v -0.338379 0.427437 0.757698 +v 0.270996 0.419434 0.768364 +v -0.270996 0.419434 0.768364 +v 0.430420 0.400116 0.748680 +v -0.430420 0.400116 0.748680 +v 0.545601 0.350723 0.698021 +v -0.545601 0.350723 0.698021 +v 0.584145 0.284561 0.679527 +v -0.584145 0.284561 0.679527 +v 0.565727 0.195580 0.679955 +v -0.565727 0.195580 0.679955 +v 0.476944 0.107208 0.707306 +v -0.476944 0.107208 0.707306 +v 0.367485 0.072838 0.735703 +v -0.367485 0.072838 0.735703 +v 0.242622 0.108803 0.764732 +v -0.242622 0.108803 0.764732 +v 0.178200 0.298286 0.774956 +v -0.178200 0.298286 0.774956 +v 0.211967 0.373398 0.775146 +v -0.211967 0.373398 0.775146 +v 0.235596 0.355713 0.759033 +v -0.235596 0.355713 0.759033 +v 0.201416 0.295654 0.756592 +v -0.201416 0.295654 0.756592 +v 0.256836 0.126221 0.751709 +v -0.256836 0.126221 0.751709 +v 0.367676 0.096924 0.727783 +v -0.367676 0.096924 0.727783 +v 0.460938 0.126953 0.701904 +v -0.460938 0.126953 0.701904 +v 0.534668 0.207275 0.676758 +v -0.534668 0.207275 0.676758 +v 0.549805 0.278809 0.673096 +v -0.549805 0.278809 0.673096 +v 0.516602 0.335938 0.689453 +v -0.516602 0.335938 0.689453 +v 0.420166 0.384521 0.739014 +v -0.420166 0.384521 0.739014 +v 0.282471 0.393555 0.761963 +v -0.282471 0.393555 0.761963 +v 0.339600 0.402588 0.752441 +v -0.339600 0.402588 0.752441 +v 0.208008 0.173096 0.751221 +v -0.208008 0.173096 0.751221 +v 0.196533 0.229004 0.751221 +v -0.196533 0.229004 0.751221 +v 0.131133 0.498074 0.614004 +v -0.131133 0.498074 0.614004 +v 0.208984 0.635851 0.642795 +v -0.208984 0.635851 0.642795 +v 0.324797 0.662109 0.625344 +v -0.324797 0.662109 0.625344 +v 0.449656 0.553797 0.558480 +v -0.449656 0.553797 0.558480 +v 0.665230 0.465981 0.489059 +v -0.665230 0.465981 0.489059 +v 0.776924 0.380348 0.445221 +v -0.776924 0.380348 0.445221 +v 0.768326 0.180618 0.362526 +v -0.768326 0.180618 0.362526 +v 0.622887 0.000038 0.380608 +v -0.622887 0.000038 0.380608 +v 0.411328 -0.115797 0.443598 +v -0.411328 -0.115797 0.443598 +v 0.000000 0.845566 0.257401 +v 0.000000 0.941956 -0.086975 +v 0.000000 -0.170364 -0.605499 +v 0.000000 -0.430420 0.115936 +v 0.000000 -0.941162 0.455902 +v 0.000000 -0.781799 0.364197 +v 0.000000 -0.581070 0.326263 +v 0.000000 -0.474075 0.268433 +v 0.806168 0.225067 0.127434 +v -0.806168 0.225067 0.127434 +v 0.828965 0.295180 -0.050016 +v -0.828965 0.295180 -0.050016 +v 0.759227 0.288059 -0.412227 +v -0.759227 0.288059 -0.412227 +v 0.396393 0.442307 -0.657005 +v -0.396393 0.442307 -0.657005 +v 0.690477 -0.027453 0.073656 +v -0.690477 -0.027453 0.073656 +v 0.622414 -0.104750 -0.169336 +v -0.622414 -0.104750 -0.169336 +v 0.651878 0.009659 -0.393562 +v -0.651878 0.009659 -0.393562 +v 0.321798 0.083374 -0.660756 +v -0.321798 0.083374 -0.660756 +v 0.237083 -0.388870 0.409340 +v -0.237083 -0.388870 0.409340 +v 0.157639 -0.429626 0.284363 +v -0.157639 -0.429626 0.284363 +v 0.276932 -0.741302 0.408371 +v -0.276932 -0.741302 0.408371 +v 0.250000 -0.544098 0.411415 +v -0.250000 -0.544098 0.411415 +v 0.282588 -0.885706 0.433323 +v -0.282588 -0.885706 0.433323 +v 0.142685 -0.752739 0.375542 +v -0.142685 -0.752739 0.375542 +v 0.133125 -0.553062 0.347893 +v -0.133125 -0.553062 0.347893 +v 0.162270 -0.919487 0.442764 +v -0.162270 -0.919487 0.442764 +v 0.241273 -0.285879 0.398809 +v -0.241273 -0.285879 0.398809 +v 0.245295 -0.213097 0.473963 +v -0.245295 -0.213097 0.473963 +v 0.257119 -0.172110 0.502562 +v -0.257119 -0.172110 0.502562 +v 0.212559 -0.363246 0.152047 +v -0.212559 -0.363246 0.152047 +v 0.267807 -0.287361 -0.247810 +v -0.267807 -0.287361 -0.247810 +v 0.302450 -0.129288 -0.533773 +v -0.302450 -0.129288 -0.533773 +v 0.411888 0.790359 -0.412376 +v -0.411888 0.790359 -0.412376 +v 0.411621 0.887993 -0.094383 +v -0.411621 0.887993 -0.094383 +v 0.412270 0.797966 0.200424 +v -0.412270 0.797966 0.200424 +v 0.421455 0.569824 0.414619 +v -0.421455 0.569824 0.414619 +v 0.746170 0.409988 0.279175 +v -0.746170 0.409988 0.279175 +v 0.630295 0.496231 0.307495 +v -0.630295 0.496231 0.307495 +v 0.633377 0.673943 0.087799 +v -0.633377 0.673943 0.087799 +v 0.773338 0.528854 0.081894 +v -0.773338 0.528854 0.081894 +v 0.775932 0.573929 -0.144730 +v -0.775932 0.573929 -0.144730 +v 0.635742 0.738060 -0.163765 +v -0.635742 0.738060 -0.163765 +v 0.632843 0.643066 -0.406555 +v -0.632843 0.643066 -0.406555 +v 0.754630 0.521647 -0.342665 +v -0.754630 0.521647 -0.342665 +v 0.599525 0.341629 -0.550880 +v -0.599525 0.341629 -0.550880 +v 0.481832 0.019141 -0.514746 +v -0.481832 0.019141 -0.514746 +v 0.826098 0.353906 -0.205953 +v -0.826098 0.353906 -0.205953 +v 0.405434 -0.184277 0.161047 +v -0.405434 -0.184277 0.161047 +v 0.441551 -0.169754 -0.206589 +v -0.441551 -0.169754 -0.206589 +v 0.899628 0.392517 -0.265503 +v -0.899628 0.392517 -0.265503 +v 0.798424 -0.115601 -0.195610 +v -0.798424 -0.115601 -0.195610 +v 1.035324 -0.072189 -0.334419 +v -1.035324 -0.072189 -0.334419 +v 1.248291 0.074867 -0.425949 +v -1.248291 0.074867 -0.425949 +v 1.312996 0.300133 -0.434006 +v -1.312996 0.300133 -0.434006 +v 1.214462 0.458069 -0.420006 +v -1.214462 0.458069 -0.420006 +v 1.034042 0.455162 -0.338860 +v -1.034042 0.455162 -0.338860 +v 1.030083 0.407959 -0.308022 +v -1.030083 0.407959 -0.308022 +v 1.177925 0.409424 -0.389214 +v -1.177925 0.409424 -0.389214 +v 1.252655 0.278076 -0.413116 +v -1.252655 0.278076 -0.413116 +v 1.198158 0.091171 -0.400528 +v -1.198158 0.091171 -0.400528 +v 1.030411 -0.028915 -0.306175 +v -1.030411 -0.028915 -0.306175 +v 0.842392 -0.063484 -0.177711 +v -0.842392 -0.063484 -0.177711 +v 0.924431 0.355713 -0.241180 +v -0.924431 0.355713 -0.241180 +v 0.946342 0.307510 -0.294327 +v -0.946342 0.307510 -0.294327 +v 0.888077 -0.023895 -0.241074 +v -0.888077 -0.023895 -0.241074 +v 1.037651 0.005264 -0.353569 +v -1.037651 0.005264 -0.353569 +v 1.178001 0.100807 -0.433105 +v -1.178001 0.100807 -0.433105 +v 1.225067 0.246986 -0.444931 +v -1.225067 0.246986 -0.444931 +v 1.163330 0.348656 -0.426323 +v -1.163330 0.348656 -0.426323 +v 1.035645 0.348442 -0.358536 +v -1.035645 0.348442 -0.358536 +v 0.853439 0.287689 -0.216812 +v -0.853439 0.287689 -0.216812 +v 0.827011 0.173630 -0.269585 +v -0.827011 0.173630 -0.269585 +v 0.776428 0.097717 -0.270004 +v -0.776428 0.097717 -0.270004 +v 0.810185 0.069915 -0.281557 +v -0.810185 0.069915 -0.281557 +v 0.818993 0.024055 -0.269051 +v -0.818993 0.024055 -0.269051 +v 0.814102 -0.013184 -0.256271 +v -0.814102 -0.013184 -0.256271 +v 0.741389 0.038808 -0.080638 +v -0.741389 0.038808 -0.080638 +v 0.731082 -0.028688 -0.166641 +v -0.731082 -0.028688 -0.166641 +v 0.744770 0.052145 -0.193328 +v -0.744770 0.052145 -0.193328 +v 0.804619 0.200340 -0.196747 +v -0.804619 0.200340 -0.196747 +v 0.884445 0.246880 -0.269745 +v -0.884445 0.246880 -0.269745 +v 0.902908 0.224800 -0.319931 +v -0.902908 0.224800 -0.319931 +v 0.839645 -0.006420 -0.297852 +v -0.839645 -0.006420 -0.297852 +v 0.853241 0.026497 -0.313522 +v -0.853241 0.026497 -0.313522 +v 0.831520 0.078049 -0.314209 +v -0.831520 0.078049 -0.314209 +v 0.797598 0.107548 -0.309118 +v -0.797598 0.107548 -0.309118 +v 0.846115 0.161003 -0.314400 +v -0.846115 0.161003 -0.314400 +v 1.051104 0.318995 -0.402618 +v -1.051104 0.318995 -0.402618 +v 1.159716 0.319056 -0.454926 +v -1.159716 0.319056 -0.454926 +v 1.213791 0.232460 -0.466934 +v -1.213791 0.232460 -0.466934 +v 1.167793 0.111153 -0.454430 +v -1.167793 0.111153 -0.454430 +v 1.037949 0.028801 -0.392426 +v -1.037949 0.028801 -0.392426 +v 0.902954 -0.001816 -0.296448 +v -0.902954 -0.001816 -0.296448 +v 0.965141 0.280136 -0.346077 +v -0.965141 0.280136 -0.346077 +v 0.890129 0.115173 -0.330185 +v -0.890129 0.115173 -0.330185 +v 0.921593 0.060440 -0.330940 +v -0.921593 0.060440 -0.330940 +v 1.000000 0.114510 -0.369324 +v -1.000000 0.114510 -0.369324 +v 0.954567 0.174126 -0.349312 +v -0.954567 0.174126 -0.349312 +v 1.014214 0.231743 -0.371452 +v -1.014214 0.231743 -0.371452 +v 1.070518 0.175903 -0.395096 +v -1.070518 0.175903 -0.395096 +v 1.124476 0.223886 -0.413014 +v -1.124476 0.223886 -0.413014 +v 1.087413 0.274493 -0.403633 +v -1.087413 0.274493 -0.403633 +v 1.035072 0.360825 -0.452255 +v -1.035072 0.360825 -0.452255 +v 1.236547 0.384903 -0.509402 +v -1.236547 0.384903 -0.509402 +v 1.325075 0.286910 -0.495184 +v -1.325075 0.286910 -0.495184 +v 1.266088 0.127434 -0.505564 +v -1.266088 0.127434 -0.505564 +v 1.040092 0.006607 -0.448471 +v -1.040092 0.006607 -0.448471 +v 0.812523 -0.031502 -0.343193 +v -0.812523 -0.031502 -0.343193 +v 0.865669 0.304039 -0.375488 +v -0.865669 0.304039 -0.375488 +vt 0.342688 0.893823 +vt 0.338510 0.888579 +vt 0.344367 0.885604 +vt 0.347894 0.890000 +vt 0.350240 0.882562 +vt 0.335430 0.882629 +vt 0.341741 0.880505 +vt 0.348080 0.878334 +vt 0.352189 0.893542 +vt 0.347814 0.898276 +vt 0.357121 0.896084 +vt 0.353662 0.901502 +vt 0.353119 0.886183 +vt 0.356628 0.889076 +vt 0.360678 0.891120 +vt 0.343567 0.903543 +vt 0.337522 0.897659 +vt 0.339054 0.908371 +vt 0.350395 0.907831 +vt 0.347740 0.917186 +vt 0.332686 0.891422 +vt 0.329175 0.884661 +vt 0.332300 0.901206 +vt 0.326909 0.894065 +vt 0.323005 0.886554 +vt 0.659406 0.860314 +vt 0.664989 0.856086 +vt 0.667954 0.862670 +vt 0.661998 0.866074 +vt 0.670237 0.869725 +vt 0.670695 0.851957 +vt 0.674008 0.859291 +vt 0.676561 0.867140 +vt 0.656106 0.869493 +vt 0.653906 0.864608 +vt 0.650247 0.872919 +vt 0.648447 0.868934 +vt 0.663990 0.872263 +vt 0.657795 0.874767 +vt 0.651624 0.877256 +vt 0.651364 0.859983 +vt 0.656416 0.854841 +vt 0.648652 0.855487 +vt 0.646363 0.865184 +vt 0.644139 0.861550 +vt 0.661570 0.849816 +vt 0.666876 0.844968 +vt 0.653227 0.849510 +vt 0.657923 0.843703 +vt 0.662803 0.838151 +vt 0.321522 0.906768 +vt 0.315530 0.898619 +vt 0.321196 0.896442 +vt 0.326928 0.904158 +vt 0.310975 0.889830 +vt 0.316947 0.888262 +vt 0.333882 0.911784 +vt 0.328528 0.914545 +vt 0.341447 0.919364 +vt 0.336041 0.922136 +vt 0.323468 0.917417 +vt 0.316196 0.909285 +vt 0.318556 0.920345 +vt 0.331227 0.925304 +vt 0.326709 0.928670 +vt 0.309897 0.900663 +vt 0.305059 0.891305 +vt 0.310911 0.911757 +vt 0.304280 0.902641 +vt 0.299170 0.892734 +vt 0.682560 0.844062 +vt 0.688637 0.840230 +vt 0.692822 0.849308 +vt 0.686474 0.852617 +vt 0.696043 0.859058 +vt 0.694754 0.836430 +vt 0.699201 0.846006 +vt 0.702622 0.856310 +vt 0.680192 0.855942 +vt 0.676566 0.847960 +vt 0.689489 0.861790 +vt 0.682987 0.864489 +vt 0.672385 0.840355 +vt 0.678046 0.835919 +vt 0.667926 0.832939 +vt 0.683809 0.831600 +vt 0.689623 0.827341 +vt 0.673232 0.827982 +vt 0.678661 0.823195 +vt 0.684150 0.818493 +vt 0.232859 0.954478 +vt 0.232909 0.964756 +vt 0.227282 0.964432 +vt 0.227207 0.954814 +vt 0.221655 0.964107 +vt 0.232959 0.975034 +vt 0.227357 0.974050 +vt 0.221755 0.973066 +vt 0.227132 0.945196 +vt 0.232809 0.944200 +vt 0.227057 0.935578 +vt 0.232759 0.933923 +vt 0.221555 0.955149 +vt 0.221455 0.946191 +vt 0.221355 0.937233 +vt 0.238486 0.943205 +vt 0.238511 0.954143 +vt 0.244163 0.942210 +vt 0.238461 0.932268 +vt 0.244163 0.930613 +vt 0.238536 0.965080 +vt 0.238561 0.976017 +vt 0.244163 0.953807 +vt 0.244163 0.965404 +vt 0.244163 0.977001 +vt 0.691936 0.882184 +vt 0.698650 0.880797 +vt 0.698075 0.891878 +vt 0.691400 0.892560 +vt 0.696273 0.902497 +vt 0.705382 0.879350 +vt 0.704768 0.891118 +vt 0.702855 0.902406 +vt 0.684762 0.893082 +vt 0.685259 0.883451 +vt 0.678179 0.893367 +vt 0.678636 0.884539 +vt 0.689713 0.902490 +vt 0.683194 0.902290 +vt 0.676739 0.901798 +vt 0.684671 0.873790 +vt 0.691304 0.871786 +vt 0.678100 0.875676 +vt 0.697979 0.869706 +vt 0.704674 0.867586 +vt 0.366751 0.903542 +vt 0.360004 0.903066 +vt 0.362560 0.897475 +vt 0.368377 0.898003 +vt 0.365179 0.892195 +vt 0.374447 0.897956 +vt 0.373809 0.903508 +vt 0.380644 0.897621 +vt 0.381023 0.903220 +vt 0.370020 0.892541 +vt 0.375086 0.892403 +vt 0.380265 0.892022 +vt 0.373171 0.909061 +vt 0.365156 0.909237 +vt 0.372532 0.914614 +vt 0.381403 0.908818 +vt 0.381782 0.914417 +vt 0.357578 0.909278 +vt 0.363578 0.915009 +vt 0.355215 0.915800 +vt 0.665590 0.886058 +vt 0.672086 0.885388 +vt 0.671668 0.893335 +vt 0.665213 0.893065 +vt 0.670368 0.900918 +vt 0.658794 0.892637 +vt 0.659131 0.886607 +vt 0.652393 0.892129 +vt 0.652691 0.887097 +vt 0.664060 0.899746 +vt 0.657795 0.898381 +vt 0.651550 0.896918 +vt 0.658801 0.880562 +vt 0.665185 0.879024 +vt 0.652437 0.882061 +vt 0.671612 0.877408 +vt 0.243926 0.874978 +vt 0.244991 0.867961 +vt 0.250387 0.871199 +vt 0.249522 0.877139 +vt 0.255732 0.874496 +vt 0.246198 0.860896 +vt 0.251370 0.865218 +vt 0.256486 0.869625 +vt 0.248893 0.882999 +vt 0.243143 0.881903 +vt 0.248617 0.888738 +vt 0.242783 0.888687 +vt 0.255072 0.879333 +vt 0.254601 0.884102 +vt 0.254412 0.888770 +vt 0.237307 0.880820 +vt 0.238234 0.872883 +vt 0.231343 0.879757 +vt 0.236872 0.888596 +vt 0.230845 0.888446 +vt 0.239490 0.864840 +vt 0.240910 0.856744 +vt 0.232401 0.870887 +vt 0.233833 0.861896 +vt 0.235452 0.852845 +vt 0.659646 0.911388 +vt 0.665365 0.914173 +vt 0.662098 0.920193 +vt 0.656768 0.916662 +vt 0.658613 0.926039 +vt 0.671187 0.916573 +vt 0.667561 0.923297 +vt 0.663691 0.929832 +vt 0.651526 0.912847 +vt 0.653997 0.908348 +vt 0.646328 0.908891 +vt 0.648383 0.905179 +vt 0.653697 0.921779 +vt 0.648891 0.917209 +vt 0.644139 0.912482 +vt 0.656141 0.903572 +vt 0.662141 0.905802 +vt 0.650168 0.901228 +vt 0.668194 0.907806 +vt 0.674328 0.909470 +vt 0.220215 0.867257 +vt 0.221945 0.856655 +vt 0.227967 0.859187 +vt 0.226379 0.869023 +vt 0.223905 0.845976 +vt 0.229764 0.849284 +vt 0.225208 0.878722 +vt 0.218945 0.877707 +vt 0.224664 0.888216 +vt 0.218367 0.887927 +vt 0.212597 0.876705 +vt 0.213956 0.865557 +vt 0.206207 0.875710 +vt 0.211993 0.887599 +vt 0.205581 0.887250 +vt 0.215818 0.854240 +vt 0.217932 0.842838 +vt 0.207650 0.863890 +vt 0.209640 0.851884 +vt 0.211901 0.839785 +vt 0.683215 0.919966 +vt 0.689351 0.921214 +vt 0.684834 0.929771 +vt 0.678973 0.927944 +vt 0.680015 0.938099 +vt 0.695522 0.922334 +vt 0.690739 0.931455 +vt 0.685638 0.940336 +vt 0.673201 0.925834 +vt 0.677149 0.918462 +vt 0.674447 0.935706 +vt 0.668988 0.933002 +vt 0.680569 0.910681 +vt 0.686890 0.911553 +vt 0.693265 0.912199 +vt 0.699666 0.912731 +vt 0.219745 0.907530 +vt 0.218711 0.897843 +vt 0.224956 0.897439 +vt 0.225874 0.906457 +vt 0.231095 0.896893 +vt 0.227210 0.915339 +vt 0.221241 0.917065 +vt 0.228755 0.924152 +vt 0.231906 0.905159 +vt 0.233091 0.913305 +vt 0.234462 0.921389 +vt 0.215213 0.918586 +vt 0.213552 0.908455 +vt 0.209155 0.920005 +vt 0.222966 0.926523 +vt 0.217124 0.928633 +vt 0.211255 0.930613 +vt 0.212395 0.898154 +vt 0.207327 0.909304 +vt 0.206045 0.898417 +vt 0.725060 0.917714 +vt 0.719806 0.908658 +vt 0.724997 0.907009 +vt 0.715832 0.898968 +vt 0.721249 0.897907 +vt 0.720159 0.919804 +vt 0.714646 0.910232 +vt 0.710456 0.899975 +vt 0.729974 0.915528 +vt 0.730250 0.905208 +vt 0.734916 0.913146 +vt 0.735595 0.903181 +vt 0.726749 0.896737 +vt 0.732373 0.895402 +vt 0.740358 0.920728 +vt 0.735770 0.923658 +vt 0.746188 0.928133 +vt 0.739897 0.910472 +vt 0.744920 0.917440 +vt 0.750304 0.924245 +vt 0.731167 0.926349 +vt 0.726555 0.928919 +vt 0.741976 0.931595 +vt 0.737700 0.934772 +vt 0.733392 0.937807 +vt 0.243618 0.901744 +vt 0.242989 0.895285 +vt 0.248812 0.894317 +vt 0.249361 0.899775 +vt 0.254601 0.893301 +vt 0.250145 0.905152 +vt 0.244529 0.908109 +vt 0.251047 0.910490 +vt 0.245582 0.914428 +vt 0.255072 0.897731 +vt 0.255732 0.902093 +vt 0.256486 0.906421 +vt 0.238854 0.910861 +vt 0.240062 0.918104 +vt 0.237810 0.903563 +vt 0.237095 0.896160 +vt 0.750009 0.904053 +vt 0.744933 0.907409 +vt 0.741063 0.900850 +vt 0.738163 0.893849 +vt 0.744078 0.892131 +vt 0.746623 0.898293 +vt 0.752245 0.895584 +vt 0.755111 0.900503 +vt 0.757897 0.892800 +vt 0.760228 0.896855 +vt 0.750076 0.890305 +vt 0.756115 0.888423 +vt 0.758443 0.905181 +vt 0.753955 0.909546 +vt 0.762008 0.909739 +vt 0.762924 0.900696 +vt 0.765802 0.904430 +vt 0.749450 0.913673 +vt 0.758181 0.914906 +vt 0.754291 0.919789 +vt 0.962451 0.274468 +vt 0.962757 0.281791 +vt 0.958971 0.279988 +vt 0.958777 0.273393 +vt 0.955854 0.278664 +vt 0.963024 0.289021 +vt 0.959208 0.286444 +vt 0.956064 0.284546 +vt 0.958668 0.266518 +vt 0.962068 0.266956 +vt 0.958602 0.259503 +vt 0.955711 0.272577 +vt 0.955699 0.266082 +vt 0.956077 0.258220 +vt 0.965757 0.266637 +vt 0.970208 0.266355 +vt 0.971373 0.276407 +vt 0.961648 0.259350 +vt 0.964688 0.257580 +vt 0.967191 0.254013 +vt 0.966631 0.275458 +vt 0.967115 0.283805 +vt 0.967405 0.291917 +vt 0.971952 0.285763 +vt 0.972240 0.294770 +vt 0.742315 0.878497 +vt 0.736202 0.878397 +vt 0.737049 0.870592 +vt 0.739055 0.863165 +vt 0.744744 0.865072 +vt 0.730205 0.878251 +vt 0.731162 0.869585 +vt 0.733415 0.861329 +vt 0.743024 0.871611 +vt 0.749057 0.872639 +vt 0.748505 0.878566 +vt 0.755118 0.873670 +vt 0.750468 0.867025 +vt 0.756209 0.869002 +vt 0.748837 0.884544 +vt 0.742654 0.885435 +vt 0.754734 0.878621 +vt 0.755063 0.883618 +vt 0.736558 0.886257 +vt 0.730593 0.886975 +vt 0.982679 0.288786 +vt 0.977172 0.287396 +vt 0.976729 0.277361 +vt 0.982859 0.299370 +vt 0.977420 0.297221 +vt 0.982448 0.278191 +vt 0.975897 0.266905 +vt 0.982118 0.267571 +vt 0.975218 0.256843 +vt 0.988163 0.267638 +vt 0.988276 0.278769 +vt 0.994121 0.267406 +vt 0.994158 0.279222 +vt 0.981823 0.257172 +vt 0.988034 0.256679 +vt 0.994048 0.255775 +vt 0.988377 0.290015 +vt 0.988472 0.301317 +vt 0.994170 0.291162 +vt 0.994170 0.303164 +vt 0.712986 0.877509 +vt 0.714083 0.866643 +vt 0.719708 0.867615 +vt 0.716837 0.856290 +vt 0.722324 0.857915 +vt 0.707375 0.877211 +vt 0.708487 0.865676 +vt 0.711368 0.854689 +vt 0.718636 0.877792 +vt 0.725391 0.868594 +vt 0.724363 0.878044 +vt 0.727844 0.859586 +vt 0.724801 0.887554 +vt 0.719139 0.888029 +vt 0.713564 0.888435 +vt 0.708032 0.888806 +vt 0.981697 0.247235 +vt 0.975233 0.247985 +vt 0.975462 0.239510 +vt 0.968628 0.248466 +vt 0.969267 0.241437 +vt 0.981622 0.237516 +vt 0.975425 0.230592 +vt 0.981478 0.227767 +vt 0.975256 0.221453 +vt 0.969373 0.233417 +vt 0.969213 0.224904 +vt 0.987530 0.224942 +vt 0.987712 0.235388 +vt 0.993583 0.222117 +vt 0.993766 0.233192 +vt 0.981300 0.218003 +vt 0.987343 0.214553 +vt 0.993387 0.211103 +vt 0.987881 0.245947 +vt 0.993926 0.244391 +vt 0.726862 0.838236 +vt 0.733312 0.830052 +vt 0.737732 0.833294 +vt 0.740172 0.822110 +vt 0.744129 0.825833 +vt 0.722002 0.835557 +vt 0.728868 0.826896 +vt 0.736178 0.818493 +vt 0.731714 0.840978 +vt 0.742108 0.836706 +vt 0.736548 0.843850 +vt 0.746415 0.840374 +vt 0.748013 0.829770 +vt 0.751789 0.834025 +vt 0.731678 0.851407 +vt 0.726452 0.849111 +vt 0.741354 0.846914 +vt 0.736917 0.853835 +vt 0.721234 0.846901 +vt 0.716020 0.844735 +vt 0.374031 0.831951 +vt 0.374668 0.836997 +vt 0.368888 0.837464 +vt 0.375305 0.842043 +vt 0.370556 0.842460 +vt 0.380948 0.831760 +vt 0.380531 0.836805 +vt 0.380114 0.841850 +vt 0.367238 0.832387 +vt 0.363275 0.838481 +vt 0.360695 0.833313 +vt 0.357913 0.840323 +vt 0.365927 0.843327 +vt 0.361481 0.844868 +vt 0.358260 0.827499 +vt 0.365624 0.827149 +vt 0.355898 0.821364 +vt 0.364029 0.821830 +vt 0.354454 0.835304 +vt 0.351209 0.829335 +vt 0.348663 0.820242 +vt 0.373394 0.826905 +vt 0.381365 0.826715 +vt 0.372756 0.821859 +vt 0.381782 0.821670 +vt 0.746124 0.850235 +vt 0.750633 0.844383 +vt 0.754782 0.848649 +vt 0.755419 0.838706 +vt 0.758941 0.843707 +vt 0.750867 0.853750 +vt 0.758886 0.853084 +vt 0.755592 0.857393 +vt 0.762968 0.857606 +vt 0.762390 0.848921 +vt 0.765802 0.854241 +vt 0.752717 0.861992 +vt 0.747440 0.859171 +vt 0.760308 0.861101 +vt 0.757997 0.864856 +vt 0.742172 0.856438 +vt 0.348564 0.838938 +vt 0.352886 0.843266 +vt 0.348396 0.847153 +vt 0.357276 0.847307 +vt 0.353502 0.850524 +vt 0.343303 0.843757 +vt 0.344644 0.851826 +vt 0.338944 0.849305 +vt 0.341833 0.857129 +vt 0.350345 0.854398 +vt 0.347996 0.858807 +vt 0.333246 0.846886 +vt 0.338236 0.840314 +vt 0.327551 0.844620 +vt 0.333097 0.837110 +vt 0.335681 0.855483 +vt 0.329551 0.853901 +vt 0.323455 0.852413 +vt 0.344379 0.834035 +vt 0.339952 0.829515 +vt 0.185405 0.902876 +vt 0.184024 0.895185 +vt 0.189537 0.894354 +vt 0.183007 0.887397 +vt 0.188625 0.887558 +vt 0.179749 0.904616 +vt 0.178212 0.896072 +vt 0.177082 0.887423 +vt 0.190730 0.901008 +vt 0.194877 0.893781 +vt 0.195840 0.899108 +vt 0.200130 0.893336 +vt 0.194027 0.888152 +vt 0.199764 0.890398 +vt 0.197027 0.904710 +vt 0.192146 0.907685 +vt 0.198326 0.910452 +vt 0.193673 0.914374 +vt 0.200844 0.897192 +vt 0.201791 0.901658 +vt 0.202854 0.906431 +vt 0.187028 0.910504 +vt 0.181557 0.913090 +vt 0.188773 0.918099 +vt 0.183501 0.921529 +vt 0.329693 0.823746 +vt 0.334905 0.826333 +vt 0.327786 0.834432 +vt 0.337474 0.815596 +vt 0.342621 0.818247 +vt 0.322422 0.832046 +vt 0.321862 0.842558 +vt 0.316176 0.840649 +vt 0.317402 0.851053 +vt 0.310492 0.838842 +vt 0.317123 0.829719 +vt 0.304809 0.837085 +vt 0.311856 0.827422 +vt 0.311382 0.849787 +vt 0.305384 0.848584 +vt 0.299398 0.847413 +vt 0.324770 0.821011 +vt 0.332925 0.812509 +vt 0.319992 0.818201 +vt 0.328673 0.809202 +vt 0.160548 0.908702 +vt 0.158665 0.898019 +vt 0.165427 0.897454 +vt 0.157317 0.887203 +vt 0.164138 0.887311 +vt 0.153776 0.909875 +vt 0.151797 0.898549 +vt 0.150396 0.887081 +vt 0.167209 0.907472 +vt 0.171978 0.896816 +vt 0.173647 0.906129 +vt 0.170759 0.887389 +vt 0.175615 0.915366 +vt 0.169320 0.917409 +vt 0.177733 0.924564 +vt 0.171594 0.927303 +vt 0.162788 0.919296 +vt 0.156139 0.921106 +vt 0.165207 0.929846 +vt 0.158696 0.932289 +vt 0.308472 0.859693 +vt 0.314677 0.860256 +vt 0.313692 0.869783 +vt 0.320945 0.860857 +vt 0.320029 0.869597 +vt 0.307439 0.869948 +vt 0.314447 0.879247 +vt 0.308275 0.880133 +vt 0.320714 0.878281 +vt 0.302167 0.880966 +vt 0.301243 0.870098 +vt 0.296090 0.881773 +vt 0.295076 0.870242 +vt 0.302309 0.859154 +vt 0.296166 0.858627 +vt 0.156582 0.865083 +vt 0.156839 0.853868 +vt 0.163550 0.856057 +vt 0.157275 0.842608 +vt 0.163901 0.845501 +vt 0.149714 0.863622 +vt 0.150047 0.851727 +vt 0.150573 0.839785 +vt 0.163363 0.866571 +vt 0.170098 0.858341 +vt 0.169969 0.868113 +vt 0.176403 0.860770 +vt 0.170378 0.848531 +vt 0.176628 0.851769 +vt 0.170139 0.877808 +vt 0.163504 0.877003 +vt 0.176313 0.869736 +vt 0.176494 0.878632 +vt 0.156682 0.876210 +vt 0.149766 0.875423 +vt 0.333710 0.862194 +vt 0.340165 0.862906 +vt 0.339612 0.868899 +vt 0.346641 0.863630 +vt 0.346235 0.868642 +vt 0.333018 0.869148 +vt 0.340147 0.874851 +vt 0.333595 0.876056 +vt 0.346730 0.873619 +vt 0.327107 0.877208 +vt 0.326481 0.869383 +vt 0.327296 0.861507 +vt 0.182307 0.871468 +vt 0.182382 0.863391 +vt 0.187827 0.865964 +vt 0.182578 0.855282 +vt 0.188012 0.858741 +vt 0.187790 0.873197 +vt 0.192529 0.868252 +vt 0.192598 0.874816 +vt 0.196281 0.870014 +vt 0.192718 0.861818 +vt 0.196481 0.864184 +vt 0.193183 0.881639 +vt 0.188049 0.880450 +vt 0.196400 0.876058 +vt 0.197159 0.882529 +vt 0.182475 0.879480 +vt 0.570370 0.864351 +vt 0.571396 0.864438 +vt 0.570497 0.869073 +vt 0.572423 0.864526 +vt 0.571559 0.869087 +vt 0.571422 0.859706 +vt 0.572416 0.859860 +vt 0.573410 0.860015 +vt 0.569436 0.869060 +vt 0.569839 0.873823 +vt 0.568737 0.873899 +vt 0.569543 0.878745 +vt 0.570942 0.873748 +vt 0.570695 0.878558 +vt 0.567634 0.873975 +vt 0.568374 0.869047 +vt 0.566531 0.874050 +vt 0.567312 0.869033 +vt 0.568390 0.878932 +vt 0.567238 0.879118 +vt 0.566085 0.879305 +vt 0.569343 0.864263 +vt 0.570428 0.859552 +vt 0.568317 0.864176 +vt 0.569434 0.859397 +vt 0.201488 0.876886 +vt 0.201239 0.882291 +vt 0.200337 0.882354 +vt 0.200991 0.887697 +vt 0.200311 0.887884 +vt 0.202321 0.876827 +vt 0.202055 0.882321 +vt 0.201788 0.887816 +vt 0.200415 0.876859 +vt 0.199261 0.882600 +vt 0.198862 0.876661 +vt 0.199863 0.888683 +vt 0.198872 0.871012 +vt 0.200594 0.871437 +vt 0.199087 0.865508 +vt 0.200825 0.866051 +vt 0.201736 0.871480 +vt 0.202588 0.871332 +vt 0.201984 0.866075 +vt 0.202854 0.865837 +vt 0.568514 0.884223 +vt 0.569728 0.883896 +vt 0.570275 0.889218 +vt 0.570942 0.883568 +vt 0.571559 0.888728 +vt 0.568992 0.889708 +vt 0.571063 0.894656 +vt 0.569704 0.895323 +vt 0.571972 0.900150 +vt 0.572423 0.893988 +vt 0.573410 0.899298 +vt 0.568344 0.895991 +vt 0.567708 0.890198 +vt 0.566985 0.896658 +vt 0.566424 0.890689 +vt 0.570534 0.901003 +vt 0.569096 0.901855 +vt 0.567658 0.902707 +vt 0.567301 0.884550 +vt 0.566087 0.884878 +vt 0.370927 0.977132 +vt 0.368777 0.972651 +vt 0.370217 0.972426 +vt 0.367097 0.967923 +vt 0.368624 0.967802 +vt 0.369613 0.977447 +vt 0.367336 0.972876 +vt 0.365571 0.968044 +vt 0.372241 0.976817 +vt 0.371658 0.972200 +vt 0.373555 0.976502 +vt 0.373098 0.971975 +vt 0.370151 0.967681 +vt 0.371677 0.967560 +vt 0.375714 0.980659 +vt 0.374553 0.981054 +vt 0.378003 0.984743 +vt 0.377008 0.985213 +vt 0.374870 0.976187 +vt 0.376875 0.980264 +vt 0.378997 0.984272 +vt 0.373392 0.981449 +vt 0.372231 0.981843 +vt 0.376014 0.985683 +vt 0.375020 0.986153 +vt 0.951134 0.247218 +vt 0.952447 0.247655 +vt 0.952501 0.252106 +vt 0.953759 0.248092 +vt 0.953919 0.252200 +vt 0.951202 0.242470 +vt 0.952533 0.243070 +vt 0.953863 0.243670 +vt 0.951113 0.251967 +vt 0.952834 0.256288 +vt 0.951188 0.256716 +vt 0.953188 0.260842 +vt 0.954604 0.255681 +vt 0.949787 0.256785 +vt 0.949788 0.251738 +vt 0.948510 0.256674 +vt 0.948492 0.251464 +vt 0.951305 0.261660 +vt 0.949838 0.261967 +vt 0.948578 0.262019 +vt 0.949821 0.246782 +vt 0.949872 0.241871 +vt 0.948509 0.246345 +vt 0.948542 0.241271 +vt 0.365632 0.957647 +vt 0.365859 0.952431 +vt 0.367376 0.952719 +vt 0.366737 0.947385 +vt 0.368205 0.947825 +vt 0.364081 0.957507 +vt 0.364343 0.952144 +vt 0.365268 0.946946 +vt 0.367183 0.957787 +vt 0.368893 0.953006 +vt 0.368734 0.957927 +vt 0.370409 0.953293 +vt 0.369673 0.948264 +vt 0.371142 0.948703 +vt 0.369165 0.962871 +vt 0.367606 0.962869 +vt 0.370286 0.958067 +vt 0.370724 0.962873 +vt 0.366047 0.962866 +vt 0.364488 0.962864 +vt 0.951413 0.266993 +vt 0.953304 0.266409 +vt 0.953352 0.272364 +vt 0.951542 0.272525 +vt 0.953501 0.278085 +vt 0.953702 0.283689 +vt 0.951926 0.283613 +vt 0.951722 0.278067 +vt 0.950324 0.278421 +vt 0.950122 0.272830 +vt 0.949117 0.278961 +vt 0.948898 0.273208 +vt 0.950542 0.284056 +vt 0.949353 0.284759 +vt 0.949955 0.267331 +vt 0.948713 0.267546 +vt 0.370296 0.938216 +vt 0.372651 0.933925 +vt 0.373975 0.934821 +vt 0.375169 0.929718 +vt 0.376445 0.930766 +vt 0.368924 0.937472 +vt 0.371326 0.933030 +vt 0.373893 0.928670 +vt 0.371668 0.938960 +vt 0.375299 0.935717 +vt 0.376623 0.936612 +vt 0.374413 0.940447 +vt 0.377721 0.931814 +vt 0.378997 0.932862 +vt 0.373041 0.939703 +vt 0.371111 0.943858 +vt 0.369691 0.943266 +vt 0.372531 0.944449 +vt 0.368270 0.942675 +vt 0.366850 0.942083 +vt 0.745247 0.270406 +vt 0.746967 0.265468 +vt 0.748179 0.266188 +vt 0.746467 0.270965 +vt 0.749391 0.266909 +vt 0.748929 0.260528 +vt 0.750133 0.261410 +vt 0.751338 0.262292 +vt 0.745243 0.275740 +vt 0.744015 0.275342 +vt 0.744748 0.280510 +vt 0.743513 0.280273 +vt 0.747687 0.271525 +vt 0.746471 0.276138 +vt 0.745984 0.280747 +vt 0.742787 0.274944 +vt 0.744027 0.269847 +vt 0.741559 0.274546 +vt 0.742277 0.280037 +vt 0.741041 0.279800 +vt 0.745755 0.264748 +vt 0.747725 0.259647 +vt 0.742808 0.269288 +vt 0.744542 0.264027 +vt 0.746521 0.258765 +vt 0.569750 0.848024 +vt 0.568301 0.848499 +vt 0.567736 0.843426 +vt 0.569202 0.843115 +vt 0.567481 0.838323 +vt 0.566851 0.848974 +vt 0.566270 0.843737 +vt 0.565999 0.838470 +vt 0.570667 0.842804 +vt 0.571200 0.847549 +vt 0.572133 0.842493 +vt 0.572649 0.847074 +vt 0.568963 0.838176 +vt 0.570445 0.838028 +vt 0.571926 0.837881 +vt 0.571939 0.852274 +vt 0.570505 0.852913 +vt 0.572780 0.856989 +vt 0.573372 0.851635 +vt 0.574198 0.856187 +vt 0.569072 0.853552 +vt 0.567638 0.854191 +vt 0.571363 0.857792 +vt 0.569946 0.858595 +vt 0.568529 0.859397 +vt 0.745185 0.290122 +vt 0.743984 0.285200 +vt 0.745227 0.285275 +vt 0.746436 0.290036 +vt 0.746471 0.285350 +vt 0.748132 0.294794 +vt 0.746872 0.295041 +vt 0.750070 0.299550 +vt 0.748803 0.299959 +vt 0.747687 0.289950 +vt 0.749391 0.294546 +vt 0.751338 0.299142 +vt 0.745613 0.295288 +vt 0.743933 0.290208 +vt 0.744354 0.295536 +vt 0.747536 0.300368 +vt 0.746269 0.300776 +vt 0.742740 0.285124 +vt 0.742682 0.290294 +vt 0.741497 0.285049 +vt 0.569621 0.828186 +vt 0.568107 0.828005 +vt 0.568781 0.822811 +vt 0.570311 0.823155 +vt 0.569558 0.817607 +vt 0.566592 0.827825 +vt 0.567250 0.822467 +vt 0.568012 0.817099 +vt 0.571841 0.823499 +vt 0.571135 0.828366 +vt 0.573372 0.823843 +vt 0.572649 0.828546 +vt 0.571105 0.818115 +vt 0.572651 0.818622 +vt 0.574198 0.819130 +vt 0.570635 0.833212 +vt 0.569137 0.833196 +vt 0.572133 0.833229 +vt 0.567639 0.833179 +vt 0.566141 0.833163 +vt 0.770682 0.829218 +vt 0.770527 0.834500 +vt 0.769251 0.833785 +vt 0.769486 0.828679 +vt 0.767975 0.833071 +vt 0.770356 0.839809 +vt 0.768995 0.838914 +vt 0.767634 0.838018 +vt 0.769681 0.823618 +vt 0.770808 0.823994 +vt 0.769816 0.818624 +vt 0.770888 0.818856 +vt 0.768290 0.828139 +vt 0.768555 0.823241 +vt 0.768744 0.818392 +vt 0.771934 0.824371 +vt 0.771878 0.829758 +vt 0.773061 0.824747 +vt 0.771961 0.819088 +vt 0.773033 0.819319 +vt 0.771803 0.835214 +vt 0.771718 0.840705 +vt 0.773074 0.830297 +vt 0.773079 0.835928 +vt 0.773079 0.841600 +vt 0.941205 0.178059 +vt 0.940081 0.178298 +vt 0.939150 0.173439 +vt 0.940220 0.173334 +vt 0.938884 0.168551 +vt 0.938958 0.178537 +vt 0.938080 0.173543 +vt 0.937870 0.168532 +vt 0.941291 0.173230 +vt 0.942329 0.177821 +vt 0.942361 0.173125 +vt 0.943452 0.177582 +vt 0.939897 0.168571 +vt 0.940911 0.168591 +vt 0.941924 0.168611 +vt 0.943805 0.182380 +vt 0.942631 0.182759 +vt 0.945501 0.186922 +vt 0.944980 0.182000 +vt 0.946726 0.186399 +vt 0.941456 0.183138 +vt 0.940281 0.183518 +vt 0.944277 0.187446 +vt 0.943052 0.187969 +vt 0.941827 0.188492 +vt 0.770884 0.808893 +vt 0.770909 0.813832 +vt 0.769870 0.813720 +vt 0.769863 0.808884 +vt 0.768831 0.813608 +vt 0.769816 0.804092 +vt 0.770830 0.804012 +vt 0.769748 0.799323 +vt 0.770761 0.799159 +vt 0.768841 0.808874 +vt 0.768801 0.804172 +vt 0.768736 0.799487 +vt 0.771845 0.803931 +vt 0.771906 0.808903 +vt 0.772859 0.803851 +vt 0.771773 0.798994 +vt 0.772786 0.798830 +vt 0.771948 0.813943 +vt 0.772928 0.808912 +vt 0.772987 0.814055 +vt 0.941676 0.158905 +vt 0.940788 0.158673 +vt 0.942516 0.153701 +vt 0.943337 0.154028 +vt 0.944466 0.148720 +vt 0.939900 0.158441 +vt 0.941695 0.153374 +vt 0.943712 0.148301 +vt 0.944159 0.154355 +vt 0.942564 0.159138 +vt 0.944980 0.154682 +vt 0.943452 0.159370 +vt 0.945219 0.149138 +vt 0.945973 0.149556 +vt 0.946726 0.149975 +vt 0.941408 0.163888 +vt 0.940456 0.163757 +vt 0.942361 0.164019 +vt 0.939504 0.163626 +vt 0.938551 0.163496 +vt 0.767113 0.293637 +vt 0.772577 0.292115 +vt 0.770376 0.297276 +vt 0.777431 0.292431 +vt 0.774175 0.298355 +vt 0.766333 0.289718 +vt 0.772671 0.287247 +vt 0.781884 0.283554 +vt 0.767163 0.296826 +vt 0.767273 0.301881 +vt 0.764087 0.299438 +vt 0.763720 0.306210 +vt 0.771093 0.303685 +vt 0.768099 0.308718 +vt 0.761989 0.295975 +vt 0.760463 0.291929 +vt 0.760016 0.302832 +vt 0.757035 0.298721 +vt 0.754826 0.294009 +vt 0.182862 0.958548 +vt 0.185078 0.959126 +vt 0.182398 0.963742 +vt 0.180373 0.961165 +vt 0.179291 0.968222 +vt 0.187564 0.960462 +vt 0.184861 0.965789 +vt 0.182060 0.971030 +vt 0.179211 0.957608 +vt 0.183116 0.955340 +vt 0.178530 0.953463 +vt 0.176994 0.964677 +vt 0.175249 0.960460 +vt 0.174135 0.955637 +vt 0.186905 0.954239 +vt 0.190070 0.954965 +vt 0.182928 0.951391 +vt 0.187335 0.949525 +vt 0.192772 0.947803 +vt 0.779815 0.302243 +vt 0.782680 0.300308 +vt 0.783527 0.305203 +vt 0.779244 0.305925 +vt 0.783819 0.310196 +vt 0.786344 0.298312 +vt 0.787726 0.303629 +vt 0.788949 0.308652 +vt 0.775091 0.305226 +vt 0.778439 0.310718 +vt 0.773102 0.310223 +vt 0.777494 0.300322 +vt 0.780723 0.295606 +vt 0.784645 0.292403 +vt 0.182840 0.942194 +vt 0.185824 0.945431 +vt 0.182025 0.947620 +vt 0.185212 0.938871 +vt 0.189485 0.942896 +vt 0.180941 0.944798 +vt 0.177948 0.949119 +vt 0.173733 0.950273 +vt 0.173866 0.944504 +vt 0.177908 0.944632 +vt 0.178849 0.940055 +vt 0.180282 0.935433 +vt 0.174355 0.938464 +vt 0.175023 0.932290 +vt 0.790045 0.297185 +vt 0.789123 0.292039 +vt 0.793547 0.293506 +vt 0.789691 0.287208 +vt 0.794706 0.289617 +vt 0.792889 0.296696 +vt 0.797305 0.295794 +vt 0.794923 0.299276 +vt 0.800731 0.298493 +vt 0.799086 0.291776 +vt 0.803148 0.293810 +vt 0.791627 0.301771 +vt 0.797497 0.302636 +vt 0.793539 0.306077 +vt 0.829456 0.302361 +vt 0.831695 0.300489 +vt 0.832497 0.305911 +vt 0.833053 0.311645 +vt 0.828680 0.311051 +vt 0.834575 0.298087 +vt 0.835934 0.305059 +vt 0.837487 0.312013 +vt 0.829108 0.306148 +vt 0.825813 0.305155 +vt 0.827650 0.300364 +vt 0.822550 0.303446 +vt 0.824433 0.310006 +vt 0.820374 0.308284 +vt 0.830399 0.295693 +vt 0.833604 0.291080 +vt 0.824984 0.298305 +vt 0.827933 0.292556 +vt 0.833215 0.284021 +vt 0.796539 0.286110 +vt 0.792579 0.283566 +vt 0.796648 0.280992 +vt 0.789837 0.279898 +vt 0.794953 0.277454 +vt 0.798225 0.283546 +vt 0.800759 0.279368 +vt 0.801297 0.283528 +vt 0.804890 0.278220 +vt 0.799426 0.275226 +vt 0.803578 0.273106 +vt 0.800578 0.287676 +vt 0.805245 0.283495 +vt 0.804658 0.288751 +vt 0.818868 0.296567 +vt 0.821693 0.297116 +vt 0.819256 0.301533 +vt 0.816381 0.299032 +vt 0.816564 0.305660 +vt 0.814375 0.295556 +vt 0.818692 0.293429 +vt 0.812922 0.291485 +vt 0.813172 0.302229 +vt 0.810363 0.298088 +vt 0.808305 0.293334 +vt 0.823621 0.292116 +vt 0.817941 0.289512 +vt 0.823762 0.287290 +vt 0.793918 0.273485 +vt 0.789451 0.274966 +vt 0.790561 0.269677 +vt 0.785080 0.274533 +vt 0.787010 0.268459 +vt 0.793369 0.270198 +vt 0.792308 0.264940 +vt 0.794374 0.260480 +vt 0.798177 0.264065 +vt 0.788604 0.262959 +vt 0.790029 0.257745 +vt 0.795483 0.267542 +vt 0.797761 0.271127 +vt 0.801292 0.268331 +vt 0.814549 0.283035 +vt 0.816263 0.285818 +vt 0.811708 0.287200 +vt 0.811208 0.282825 +vt 0.807163 0.288062 +vt 0.811900 0.278483 +vt 0.816376 0.280455 +vt 0.813226 0.274229 +vt 0.806885 0.282530 +vt 0.807417 0.276997 +vt 0.808704 0.271720 +vt 0.820838 0.283397 +vt 0.818140 0.276866 +vt 0.823838 0.279756 +vt 0.783656 0.266314 +vt 0.781524 0.271150 +vt 0.778771 0.266191 +vt 0.778095 0.274398 +vt 0.775389 0.268195 +vt 0.781083 0.264239 +vt 0.776808 0.261030 +vt 0.775239 0.255770 +vt 0.780302 0.255366 +vt 0.772796 0.262584 +vt 0.770258 0.257269 +vt 0.780776 0.260386 +vt 0.784734 0.261234 +vt 0.785285 0.256033 +vt 0.818954 0.269526 +vt 0.818858 0.272846 +vt 0.814631 0.270117 +vt 0.816492 0.266627 +vt 0.810694 0.266957 +vt 0.819187 0.264242 +vt 0.821667 0.269227 +vt 0.822333 0.262534 +vt 0.813308 0.262842 +vt 0.816466 0.259507 +vt 0.820090 0.257087 +vt 0.823666 0.274715 +vt 0.824840 0.268292 +vt 0.827874 0.274673 +vt 0.771406 0.269314 +vt 0.773124 0.274716 +vt 0.767603 0.273130 +vt 0.772820 0.279756 +vt 0.766478 0.277196 +vt 0.768008 0.269785 +vt 0.762520 0.270666 +vt 0.757657 0.267762 +vt 0.761244 0.263447 +vt 0.760610 0.274867 +vt 0.754980 0.272653 +vt 0.765097 0.267034 +vt 0.768707 0.264463 +vt 0.765519 0.259889 +vt 0.829276 0.264516 +vt 0.827461 0.266411 +vt 0.825549 0.261080 +vt 0.828879 0.260427 +vt 0.824100 0.255714 +vt 0.832367 0.261123 +vt 0.831586 0.266791 +vt 0.835934 0.262494 +vt 0.828402 0.255126 +vt 0.832896 0.255062 +vt 0.837487 0.255260 +vt 0.830301 0.271669 +vt 0.834575 0.269710 +vt 0.833604 0.276892 +vt 0.764398 0.280842 +vt 0.769449 0.283516 +vt 0.764382 0.286131 +vt 0.762308 0.283478 +vt 0.758991 0.287734 +vt 0.758272 0.283452 +vt 0.753438 0.288831 +vt 0.759003 0.279146 +vt 0.752949 0.283403 +vt 0.753437 0.277939 +vt 0.190895 0.964949 +vt 0.193042 0.963122 +vt 0.192939 0.969039 +vt 0.189979 0.969038 +vt 0.192315 0.975443 +vt 0.195731 0.960849 +vt 0.196059 0.968400 +vt 0.196059 0.976246 +vt 0.187337 0.967754 +vt 0.189587 0.962719 +vt 0.188669 0.974440 +vt 0.185218 0.973036 +vt 0.192100 0.958178 +vt 0.194745 0.953885 +vt 0.930703 0.105883 +vt 0.932613 0.113681 +vt 0.927708 0.114187 +vt 0.934624 0.121116 +vt 0.929404 0.121238 +vt 0.935538 0.105138 +vt 0.937488 0.112958 +vt 0.939807 0.120749 +vt 0.925899 0.106516 +vt 0.922744 0.114260 +vt 0.921153 0.106921 +vt 0.917691 0.113684 +vt 0.924107 0.120870 +vt 0.918698 0.119767 +vt 0.919389 0.099013 +vt 0.924081 0.098403 +vt 0.917505 0.090698 +vt 0.922166 0.089802 +vt 0.916398 0.106878 +vt 0.914871 0.099524 +vt 0.913162 0.091801 +vt 0.929155 0.098015 +vt 0.934418 0.097739 +vt 0.927433 0.089467 +vt 0.934590 0.091212 +vt 0.920060 0.080665 +vt 0.915557 0.082134 +vt 0.913592 0.073608 +vt 0.911322 0.083886 +vt 0.909403 0.076059 +vt 0.917965 0.071348 +vt 0.911656 0.065407 +vt 0.916078 0.062205 +vt 0.909796 0.057818 +vt 0.907459 0.068602 +vt 0.905539 0.061793 +vt 0.920742 0.058421 +vt 0.922544 0.069086 +vt 0.925302 0.055069 +vt 0.927065 0.066761 +vt 0.914325 0.053723 +vt 0.919180 0.048741 +vt 0.924782 0.040199 +vt 0.925002 0.079629 +vt 0.929765 0.078117 +vt 0.938216 0.134194 +vt 0.939874 0.140162 +vt 0.933380 0.138411 +vt 0.941493 0.145968 +vt 0.934521 0.143471 +vt 0.944199 0.135006 +vt 0.946316 0.141714 +vt 0.948410 0.148301 +vt 0.932184 0.133151 +vt 0.926782 0.136258 +vt 0.926055 0.131647 +vt 0.920028 0.133503 +vt 0.927438 0.140646 +vt 0.920190 0.137330 +vt 0.925187 0.126592 +vt 0.930877 0.127494 +vt 0.919782 0.129451 +vt 0.919366 0.124947 +vt 0.936479 0.127900 +vt 0.942037 0.128058 +vt 0.912631 0.046387 +vt 0.908059 0.051127 +vt 0.906216 0.045317 +vt 0.903698 0.055913 +vt 0.901703 0.050899 +vt 0.910846 0.039978 +vt 0.904040 0.040374 +vt 0.908820 0.034279 +vt 0.901301 0.036281 +vt 0.899322 0.046689 +vt 0.896326 0.043218 +vt 0.913643 0.028329 +vt 0.915577 0.034668 +vt 0.918487 0.022452 +vt 0.920359 0.029373 +vt 0.906331 0.029459 +vt 0.911397 0.022713 +vt 0.916482 0.016006 +vt 0.917440 0.041148 +vt 0.922368 0.035659 +vt 0.411406 0.622446 +vt 0.419416 0.618842 +vt 0.421054 0.623342 +vt 0.412879 0.627185 +vt 0.422251 0.629156 +vt 0.427406 0.615427 +vt 0.429203 0.619681 +vt 0.430535 0.625562 +vt 0.404651 0.631393 +vt 0.403357 0.626427 +vt 0.396342 0.636147 +vt 0.395248 0.630973 +vt 0.413940 0.632924 +vt 0.405575 0.637042 +vt 0.397128 0.641685 +vt 0.401816 0.621917 +vt 0.400152 0.617634 +vt 0.407772 0.614634 +vt 0.393948 0.626042 +vt 0.392546 0.621233 +vt 0.409658 0.618373 +vt 0.417482 0.615218 +vt 0.425299 0.612257 +vt 0.415402 0.612032 +vt 0.423037 0.609629 +vt 0.899261 0.022829 +vt 0.903155 0.025690 +vt 0.897772 0.033022 +vt 0.893539 0.030501 +vt 0.892481 0.040425 +vt 0.888690 0.028621 +vt 0.894618 0.020732 +vt 0.883311 0.027288 +vt 0.889192 0.019257 +vt 0.887977 0.038238 +vt 0.883001 0.036587 +vt 0.877743 0.035401 +vt 0.900704 0.012893 +vt 0.906871 0.005079 +vt 0.910972 0.007593 +vt 0.895282 0.011279 +vt 0.901475 0.003329 +vt 0.905090 0.015200 +vt 0.908600 0.018405 +vt 0.914076 0.011144 +vt 0.414564 0.648461 +vt 0.423045 0.645926 +vt 0.422978 0.656658 +vt 0.422825 0.668806 +vt 0.414194 0.669809 +vt 0.431521 0.643546 +vt 0.431540 0.655083 +vt 0.431486 0.667937 +vt 0.414427 0.658378 +vt 0.405902 0.660389 +vt 0.406073 0.651308 +vt 0.397414 0.662835 +vt 0.397567 0.654622 +vt 0.405622 0.671080 +vt 0.397141 0.672752 +vt 0.406006 0.643603 +vt 0.414452 0.639996 +vt 0.397504 0.647707 +vt 0.422859 0.636721 +vt 0.431247 0.633612 +vt 0.876106 0.017622 +vt 0.882954 0.018260 +vt 0.877490 0.026405 +vt 0.871337 0.025860 +vt 0.872390 0.034610 +vt 0.864963 0.025539 +vt 0.868851 0.017223 +vt 0.858478 0.025331 +vt 0.861393 0.016943 +vt 0.866967 0.034115 +vt 0.861496 0.033817 +vt 0.856001 0.033618 +vt 0.873020 0.008881 +vt 0.877329 0.000526 +vt 0.886306 0.001175 +vt 0.864600 0.008488 +vt 0.867953 0.000000 +vt 0.881140 0.009395 +vt 0.888660 0.010154 +vt 0.894488 0.002068 +vt 0.422674 0.696227 +vt 0.422492 0.709928 +vt 0.413570 0.710145 +vt 0.422118 0.722575 +vt 0.413187 0.722775 +vt 0.431582 0.696026 +vt 0.431489 0.709818 +vt 0.431140 0.722478 +vt 0.404796 0.710573 +vt 0.405095 0.697080 +vt 0.396245 0.711319 +vt 0.396544 0.697958 +vt 0.404438 0.723183 +vt 0.395962 0.723901 +vt 0.413825 0.696541 +vt 0.405362 0.683613 +vt 0.414013 0.682813 +vt 0.396847 0.684781 +vt 0.422754 0.682258 +vt 0.431540 0.681825 +vt 0.044277 0.778293 +vt 0.033345 0.786053 +vt 0.030915 0.777843 +vt 0.041179 0.769956 +vt 0.028568 0.769377 +vt 0.021961 0.794103 +vt 0.020382 0.785985 +vt 0.018251 0.777787 +vt 0.050907 0.762580 +vt 0.054306 0.771114 +vt 0.059829 0.755968 +vt 0.063347 0.764706 +vt 0.038618 0.761236 +vt 0.048132 0.753632 +vt 0.056842 0.746834 +vt 0.058953 0.778823 +vt 0.048448 0.785862 +vt 0.065474 0.785293 +vt 0.054970 0.792086 +vt 0.067927 0.772655 +vt 0.074099 0.779419 +vt 0.035943 0.793751 +vt 0.022438 0.802063 +vt 0.041751 0.799900 +vt 0.021259 0.809785 +vt 0.187718 0.349248 +vt 0.177448 0.357820 +vt 0.172717 0.348690 +vt 0.183471 0.341391 +vt 0.168912 0.340766 +vt 0.167270 0.366395 +vt 0.162328 0.355999 +vt 0.158300 0.347147 +vt 0.194956 0.334114 +vt 0.198171 0.340682 +vt 0.206806 0.326848 +vt 0.208715 0.332118 +vt 0.180067 0.334401 +vt 0.192304 0.328071 +vt 0.208062 0.321837 +vt 0.202755 0.347626 +vt 0.192777 0.357690 +vt 0.208024 0.354758 +vt 0.212734 0.337562 +vt 0.217806 0.343091 +vt 0.182799 0.367754 +vt 0.172821 0.377819 +vt 0.198241 0.366424 +vt 0.188458 0.378091 +vt 0.178676 0.389757 +vt 0.711102 0.782665 +vt 0.697854 0.784513 +vt 0.703331 0.770342 +vt 0.715696 0.768151 +vt 0.707997 0.756156 +vt 0.684563 0.786471 +vt 0.690791 0.772973 +vt 0.694714 0.762492 +vt 0.727712 0.766842 +vt 0.724262 0.781037 +vt 0.739554 0.765974 +vt 0.737379 0.779520 +vt 0.719357 0.753804 +vt 0.730271 0.752832 +vt 0.740962 0.752550 +vt 0.720219 0.795355 +vt 0.705746 0.797563 +vt 0.715879 0.809735 +vt 0.734693 0.793147 +vt 0.731751 0.806816 +vt 0.691272 0.799771 +vt 0.676798 0.801979 +vt 0.700008 0.812655 +vt 0.684136 0.815574 +vt 0.668265 0.818493 +vt 0.628187 0.281692 +vt 0.619292 0.280407 +vt 0.619204 0.270871 +vt 0.628130 0.272797 +vt 0.619116 0.261334 +vt 0.610398 0.279123 +vt 0.610278 0.268944 +vt 0.610159 0.258765 +vt 0.637056 0.274724 +vt 0.637082 0.282976 +vt 0.645981 0.276651 +vt 0.645976 0.284261 +vt 0.628073 0.263903 +vt 0.637029 0.266472 +vt 0.645986 0.269040 +vt 0.637108 0.291228 +vt 0.628244 0.290586 +vt 0.637134 0.299481 +vt 0.645972 0.291871 +vt 0.645967 0.299481 +vt 0.619381 0.289944 +vt 0.610517 0.289302 +vt 0.628302 0.299481 +vt 0.619469 0.299481 +vt 0.610636 0.299481 +vt 0.723453 0.725203 +vt 0.715170 0.725654 +vt 0.716438 0.711778 +vt 0.724034 0.711452 +vt 0.716591 0.698766 +vt 0.707983 0.726625 +vt 0.709693 0.713021 +vt 0.709960 0.700599 +vt 0.732198 0.711739 +vt 0.732171 0.725415 +vt 0.740645 0.712332 +vt 0.741108 0.725960 +vt 0.724052 0.697985 +vt 0.732066 0.697904 +vt 0.740358 0.698174 +vt 0.731643 0.739068 +vt 0.721918 0.739404 +vt 0.741347 0.739289 +vt 0.712150 0.740854 +vt 0.704080 0.742067 +vt 0.748517 0.555746 +vt 0.756396 0.544730 +vt 0.761002 0.549002 +vt 0.752865 0.561357 +vt 0.765958 0.552864 +vt 0.764015 0.533005 +vt 0.768776 0.535876 +vt 0.774029 0.538026 +vt 0.744002 0.572173 +vt 0.740122 0.565345 +vt 0.734050 0.580679 +vt 0.757455 0.566815 +vt 0.748087 0.578994 +vt 0.737422 0.588514 +vt 0.736651 0.558503 +vt 0.744655 0.549827 +vt 0.733795 0.551640 +vt 0.730875 0.572914 +vt 0.728093 0.565290 +vt 0.725900 0.557875 +vt 0.752489 0.539638 +vt 0.760238 0.528693 +vt 0.741337 0.543620 +vt 0.748899 0.534009 +vt 0.757936 0.522219 +vt 0.722892 0.671739 +vt 0.715023 0.674211 +vt 0.712425 0.663151 +vt 0.720383 0.659642 +vt 0.708030 0.653226 +vt 0.708167 0.677532 +vt 0.705592 0.667287 +vt 0.701557 0.658171 +vt 0.729091 0.656553 +vt 0.731435 0.669833 +vt 0.738174 0.653673 +vt 0.740315 0.668211 +vt 0.715699 0.648686 +vt 0.724165 0.644414 +vt 0.733030 0.640276 +vt 0.732119 0.683776 +vt 0.723893 0.684633 +vt 0.740645 0.683256 +vt 0.716266 0.686163 +vt 0.709537 0.688702 +vt 0.720443 0.577840 +vt 0.722647 0.586105 +vt 0.710712 0.588738 +vt 0.725027 0.594489 +vt 0.711942 0.597276 +vt 0.709571 0.580287 +vt 0.699166 0.588861 +vt 0.699001 0.580422 +vt 0.688929 0.586761 +vt 0.699204 0.597231 +vt 0.687853 0.594712 +vt 0.698582 0.571845 +vt 0.708607 0.572012 +vt 0.698601 0.564085 +vt 0.689719 0.578612 +vt 0.689938 0.570068 +vt 0.688097 0.560022 +vt 0.718595 0.569810 +vt 0.708115 0.564255 +vt 0.717277 0.562133 +vt 0.692880 0.636998 +vt 0.682810 0.629668 +vt 0.687234 0.623051 +vt 0.671966 0.622514 +vt 0.675235 0.615627 +vt 0.688447 0.643381 +vt 0.679601 0.636628 +vt 0.668222 0.628293 +vt 0.698469 0.630802 +vt 0.691872 0.616093 +vt 0.704680 0.624589 +vt 0.696617 0.608965 +vt 0.678188 0.608002 +vt 0.680982 0.600007 +vt 0.715737 0.633894 +vt 0.708176 0.639210 +vt 0.711202 0.618366 +vt 0.723689 0.628655 +vt 0.701401 0.644680 +vt 0.695804 0.650382 +vt 0.682711 0.575227 +vt 0.680921 0.582723 +vt 0.674242 0.576918 +vt 0.678928 0.590075 +vt 0.671539 0.583616 +vt 0.676895 0.570184 +vt 0.667992 0.569517 +vt 0.671188 0.563404 +vt 0.661270 0.560690 +vt 0.664796 0.575630 +vt 0.657810 0.566413 +vt 0.674384 0.557291 +vt 0.679446 0.563379 +vt 0.677580 0.551178 +vt 0.681946 0.556537 +vt 0.664730 0.554968 +vt 0.668189 0.549245 +vt 0.671649 0.543522 +vt 0.684095 0.567442 +vt 0.685276 0.559513 +vt 0.096060 0.408725 +vt 0.103654 0.406393 +vt 0.102982 0.412605 +vt 0.113140 0.404309 +vt 0.112814 0.410916 +vt 0.097329 0.403117 +vt 0.104261 0.400242 +vt 0.113352 0.397731 +vt 0.094792 0.414430 +vt 0.102181 0.418938 +vt 0.093526 0.420330 +vt 0.101186 0.425451 +vt 0.112261 0.417583 +vt 0.111368 0.424340 +vt 0.085821 0.421748 +vt 0.087698 0.416345 +vt 0.078591 0.423178 +vt 0.081151 0.418304 +vt 0.092262 0.426523 +vt 0.084176 0.427570 +vt 0.076509 0.428603 +vt 0.089729 0.411220 +vt 0.091837 0.406235 +vt 0.084028 0.413797 +vt 0.087065 0.409474 +vt 0.656660 0.544957 +vt 0.653176 0.550609 +vt 0.644043 0.539847 +vt 0.649693 0.556260 +vt 0.640627 0.545613 +vt 0.647458 0.534082 +vt 0.634202 0.528980 +vt 0.637604 0.523051 +vt 0.623987 0.518581 +vt 0.630800 0.534909 +vt 0.620395 0.524589 +vt 0.641006 0.517122 +vt 0.650873 0.528317 +vt 0.644408 0.511193 +vt 0.654288 0.522552 +vt 0.627578 0.512573 +vt 0.631170 0.506566 +vt 0.634762 0.500558 +vt 0.660143 0.539306 +vt 0.663627 0.533654 +vt 0.138723 0.401107 +vt 0.152899 0.399957 +vt 0.152325 0.407044 +vt 0.166717 0.399086 +vt 0.166229 0.406251 +vt 0.139022 0.394104 +vt 0.153296 0.392856 +vt 0.166993 0.391899 +vt 0.138267 0.408105 +vt 0.151397 0.414101 +vt 0.137496 0.415094 +vt 0.149938 0.421112 +vt 0.165318 0.413369 +vt 0.163772 0.420417 +vt 0.124242 0.416278 +vt 0.124836 0.409406 +vt 0.136253 0.422067 +vt 0.123227 0.423177 +vt 0.125150 0.402552 +vt 0.125323 0.395707 +vt 0.617860 0.503358 +vt 0.613729 0.509226 +vt 0.604070 0.500596 +vt 0.609597 0.515093 +vt 0.599204 0.506138 +vt 0.609011 0.495115 +vt 0.595652 0.492375 +vt 0.589117 0.484247 +vt 0.595879 0.480184 +vt 0.590012 0.497442 +vt 0.582819 0.488720 +vt 0.601590 0.487554 +vt 0.608124 0.483223 +vt 0.614100 0.489756 +vt 0.614954 0.479137 +vt 0.603565 0.476942 +vt 0.613965 0.475727 +vt 0.621992 0.497490 +vt 0.619264 0.484459 +vt 0.626123 0.491623 +vt 0.191467 0.405485 +vt 0.190727 0.398065 +vt 0.201579 0.397784 +vt 0.203267 0.405363 +vt 0.212101 0.397569 +vt 0.189631 0.390603 +vt 0.199438 0.390152 +vt 0.208812 0.389757 +vt 0.204049 0.412833 +vt 0.191493 0.412818 +vt 0.203472 0.420141 +vt 0.214834 0.405315 +vt 0.216454 0.412931 +vt 0.216406 0.420351 +vt 0.178633 0.412969 +vt 0.179199 0.405756 +vt 0.190449 0.420021 +vt 0.177246 0.420083 +vt 0.179216 0.398478 +vt 0.178961 0.391166 +vt 0.592155 0.472512 +vt 0.585106 0.475893 +vt 0.582988 0.467371 +vt 0.578420 0.479692 +vt 0.576119 0.470432 +vt 0.590047 0.464584 +vt 0.582132 0.458736 +vt 0.589183 0.456447 +vt 0.581906 0.450044 +vt 0.575215 0.461020 +vt 0.575010 0.451530 +vt 0.596504 0.454148 +vt 0.597485 0.462349 +vt 0.604380 0.453117 +vt 0.605268 0.461205 +vt 0.588941 0.448204 +vt 0.596253 0.445655 +vt 0.603980 0.442043 +vt 0.599929 0.469966 +vt 0.607739 0.468446 +vt 0.089189 0.864583 +vt 0.087229 0.871578 +vt 0.079692 0.868875 +vt 0.086479 0.879903 +vt 0.078904 0.877757 +vt 0.096547 0.867899 +vt 0.094766 0.874281 +vt 0.094054 0.882048 +vt 0.081831 0.861267 +vt 0.072155 0.866172 +vt 0.074473 0.857952 +vt 0.064618 0.863470 +vt 0.071330 0.875612 +vt 0.063755 0.873466 +vt 0.077786 0.850543 +vt 0.084871 0.854509 +vt 0.081596 0.843540 +vt 0.088361 0.848175 +vt 0.067115 0.854636 +vt 0.070701 0.846577 +vt 0.074831 0.838905 +vt 0.091956 0.858475 +vt 0.099041 0.862441 +vt 0.095126 0.852810 +vt 0.101890 0.857445 +vt 0.014458 0.857595 +vt 0.021163 0.855946 +vt 0.020545 0.868894 +vt 0.028064 0.854910 +vt 0.027396 0.867942 +vt 0.015094 0.844426 +vt 0.021867 0.842538 +vt 0.028812 0.841398 +vt 0.013916 0.870344 +vt 0.020099 0.880922 +vt 0.013562 0.882251 +vt 0.019911 0.891571 +vt 0.026890 0.880015 +vt 0.026627 0.890648 +vt 0.007194 0.883862 +vt 0.007436 0.872127 +vt 0.000912 0.885613 +vt 0.001030 0.874076 +vt 0.013488 0.892897 +vt 0.007262 0.894491 +vt 0.001133 0.896220 +vt 0.007884 0.859654 +vt 0.008435 0.846812 +vt 0.001376 0.861917 +vt 0.001834 0.849448 +vt 0.089131 0.901069 +vt 0.091157 0.912302 +vt 0.084386 0.911218 +vt 0.092734 0.922897 +vt 0.086357 0.921827 +vt 0.096269 0.902365 +vt 0.097929 0.913386 +vt 0.099110 0.923967 +vt 0.081992 0.899773 +vt 0.077614 0.910134 +vt 0.074854 0.898478 +vt 0.070842 0.909050 +vt 0.079980 0.920757 +vt 0.073604 0.919687 +vt 0.072494 0.886675 +vt 0.079918 0.888338 +vt 0.067715 0.897182 +vt 0.065070 0.885013 +vt 0.087342 0.890001 +vt 0.094766 0.891664 +vt 0.013790 0.901859 +vt 0.020066 0.900380 +vt 0.020480 0.907810 +vt 0.026686 0.899361 +vt 0.026989 0.906635 +vt 0.014372 0.909560 +vt 0.021066 0.914320 +vt 0.015142 0.916419 +vt 0.021738 0.920370 +vt 0.027453 0.912948 +vt 0.027998 0.918782 +vt 0.009526 0.919003 +vt 0.008531 0.911693 +vt 0.004065 0.921830 +vt 0.002823 0.914017 +vt 0.016006 0.922857 +vt 0.010625 0.925945 +vt 0.005420 0.929331 +vt 0.007742 0.903645 +vt 0.001808 0.905584 +vt 0.092759 0.940119 +vt 0.091776 0.947467 +vt 0.086456 0.945252 +vt 0.090509 0.954455 +vt 0.085520 0.951661 +vt 0.098416 0.941813 +vt 0.097095 0.949682 +vt 0.095497 0.957250 +vt 0.087102 0.938424 +vt 0.081137 0.943037 +vt 0.081445 0.936730 +vt 0.075817 0.940822 +vt 0.080532 0.948866 +vt 0.075543 0.946071 +vt 0.081158 0.929463 +vt 0.087165 0.930756 +vt 0.075788 0.935035 +vt 0.075151 0.928169 +vt 0.093173 0.932049 +vt 0.099180 0.933342 +vt 0.636319 0.433601 +vt 0.640130 0.437602 +vt 0.636207 0.443048 +vt 0.643735 0.440972 +vt 0.640619 0.446013 +vt 0.641369 0.428054 +vt 0.644442 0.432503 +vt 0.647440 0.436284 +vt 0.631319 0.439440 +vt 0.632931 0.448652 +vt 0.626784 0.445348 +vt 0.630564 0.454225 +vt 0.638191 0.451221 +vt 0.636554 0.456409 +vt 0.619745 0.441353 +vt 0.626039 0.435353 +vt 0.612261 0.437013 +vt 0.620563 0.431027 +vt 0.623370 0.451297 +vt 0.614946 0.447629 +vt 0.632370 0.429180 +vt 0.638246 0.423159 +vt 0.628353 0.424549 +vt 0.635099 0.418041 +vt 0.592439 0.740323 +vt 0.589766 0.731649 +vt 0.597965 0.731725 +vt 0.587980 0.723484 +vt 0.596983 0.724461 +vt 0.585184 0.741229 +vt 0.581568 0.731574 +vt 0.578976 0.722506 +vt 0.599694 0.739417 +vt 0.606163 0.731800 +vt 0.606949 0.738511 +vt 0.614362 0.731876 +vt 0.605986 0.725438 +vt 0.614989 0.726416 +vt 0.608299 0.745327 +vt 0.601954 0.747220 +vt 0.610170 0.752002 +vt 0.604529 0.754813 +vt 0.614204 0.737605 +vt 0.614644 0.743434 +vt 0.615811 0.749190 +vt 0.595609 0.749112 +vt 0.589264 0.751005 +vt 0.598888 0.757624 +vt 0.593246 0.760435 +vt 0.621409 0.463157 +vt 0.621732 0.457259 +vt 0.629365 0.459579 +vt 0.629068 0.464956 +vt 0.635810 0.461390 +vt 0.629408 0.470596 +vt 0.621936 0.468914 +vt 0.630066 0.476370 +vt 0.622889 0.474599 +vt 0.635797 0.466501 +vt 0.636354 0.472075 +vt 0.637228 0.479356 +vt 0.614894 0.468146 +vt 0.613359 0.461690 +vt 0.617066 0.474300 +vt 0.613097 0.454629 +vt 0.597891 0.712071 +vt 0.588180 0.709749 +vt 0.590063 0.703962 +vt 0.599744 0.706730 +vt 0.593066 0.698751 +vt 0.578469 0.707427 +vt 0.580382 0.701194 +vt 0.583627 0.695687 +vt 0.609425 0.709497 +vt 0.607602 0.714392 +vt 0.619106 0.712265 +vt 0.617313 0.716714 +vt 0.602505 0.701816 +vt 0.611944 0.704880 +vt 0.621383 0.707945 +vt 0.606461 0.719671 +vt 0.615957 0.721397 +vt 0.596964 0.717945 +vt 0.587468 0.716220 +vt 0.577972 0.714494 +vt 0.752026 0.687222 +vt 0.752509 0.679129 +vt 0.759211 0.684155 +vt 0.753023 0.670070 +vt 0.759511 0.673997 +vt 0.745057 0.681242 +vt 0.745807 0.674103 +vt 0.746536 0.666143 +vt 0.758996 0.693202 +vt 0.765913 0.689180 +vt 0.765965 0.699182 +vt 0.772614 0.694206 +vt 0.765999 0.677923 +vt 0.772486 0.681850 +vt 0.766110 0.708347 +vt 0.758837 0.701509 +vt 0.766302 0.717094 +vt 0.758708 0.709446 +vt 0.772935 0.705162 +vt 0.773383 0.715185 +vt 0.773896 0.724742 +vt 0.751565 0.694671 +vt 0.744292 0.687833 +vt 0.751114 0.701798 +vt 0.743520 0.694151 +vt 0.606154 0.697222 +vt 0.597136 0.694007 +vt 0.602256 0.689307 +vt 0.588117 0.690793 +vt 0.593810 0.686016 +vt 0.610702 0.692597 +vt 0.608408 0.684225 +vt 0.616156 0.687592 +vt 0.615575 0.678337 +vt 0.600660 0.680857 +vt 0.608623 0.674818 +vt 0.623904 0.690960 +vt 0.619148 0.695888 +vt 0.631652 0.694328 +vt 0.627594 0.699178 +vt 0.622526 0.681857 +vt 0.629478 0.685376 +vt 0.636429 0.688896 +vt 0.615173 0.700436 +vt 0.624191 0.703650 +vt 0.760259 0.650180 +vt 0.754023 0.648880 +vt 0.754196 0.638331 +vt 0.760319 0.638406 +vt 0.753942 0.628869 +vt 0.747787 0.647579 +vt 0.748072 0.638256 +vt 0.747972 0.629762 +vt 0.766442 0.638480 +vt 0.766495 0.651481 +vt 0.772566 0.638555 +vt 0.772730 0.652782 +vt 0.759912 0.627976 +vt 0.765882 0.627083 +vt 0.771852 0.626190 +vt 0.766270 0.664993 +vt 0.759925 0.662358 +vt 0.772614 0.667628 +vt 0.753580 0.659723 +vt 0.747235 0.657088 +vt 0.629821 0.675040 +vt 0.623738 0.671220 +vt 0.632353 0.663646 +vt 0.617656 0.667400 +vt 0.627158 0.659412 +vt 0.640872 0.656387 +vt 0.645214 0.661114 +vt 0.648752 0.650214 +vt 0.652327 0.655479 +vt 0.636531 0.651659 +vt 0.645177 0.644949 +vt 0.637547 0.667881 +vt 0.649555 0.665842 +vt 0.642742 0.672115 +vt 0.653896 0.670570 +vt 0.655903 0.660744 +vt 0.659478 0.666009 +vt 0.635903 0.678861 +vt 0.647937 0.676350 +vt 0.641986 0.682681 +vt 0.751832 0.615110 +vt 0.750268 0.609873 +vt 0.755386 0.608120 +vt 0.748558 0.605106 +vt 0.753327 0.603406 +vt 0.746385 0.616815 +vt 0.745150 0.611627 +vt 0.743789 0.606806 +vt 0.757280 0.613405 +vt 0.760504 0.606366 +vt 0.765622 0.604613 +vt 0.768176 0.609994 +vt 0.758096 0.601707 +vt 0.762864 0.600007 +vt 0.762728 0.611699 +vt 0.764582 0.618380 +vt 0.758844 0.619833 +vt 0.770320 0.616928 +vt 0.753106 0.621285 +vt 0.747367 0.622738 +vt 0.663661 0.649592 +vt 0.658396 0.651712 +vt 0.655445 0.645901 +vt 0.661202 0.642975 +vt 0.652495 0.640090 +vt 0.666271 0.640966 +vt 0.668367 0.648896 +vt 0.671717 0.641102 +vt 0.658659 0.636248 +vt 0.663844 0.632593 +vt 0.669801 0.655938 +vt 0.665955 0.655986 +vt 0.670904 0.662537 +vt 0.668167 0.662269 +vt 0.673119 0.649819 +vt 0.673596 0.657153 +vt 0.672323 0.663861 +vt 0.661346 0.657523 +vt 0.664297 0.663334 +vt 0.709642 0.545366 +vt 0.716537 0.542249 +vt 0.716509 0.548274 +vt 0.723028 0.537943 +vt 0.723619 0.544045 +vt 0.710092 0.540111 +vt 0.716495 0.536933 +vt 0.722468 0.532593 +vt 0.709028 0.551131 +vt 0.716668 0.554928 +vt 0.708387 0.557362 +vt 0.724492 0.550742 +vt 0.699750 0.558095 +vt 0.701239 0.552968 +vt 0.690935 0.557979 +vt 0.693295 0.554294 +vt 0.702278 0.547794 +vt 0.703117 0.542641 +vt 0.694680 0.549877 +vt 0.694595 0.545638 +vt 0.678525 0.652558 +vt 0.678607 0.644612 +vt 0.685704 0.650156 +vt 0.683980 0.656838 +vt 0.691776 0.656393 +vt 0.688879 0.662388 +vt 0.696677 0.663653 +vt 0.686672 0.668041 +vt 0.682604 0.662944 +vt 0.684716 0.673025 +vt 0.681232 0.668198 +vt 0.692987 0.669278 +vt 0.690087 0.674653 +vt 0.687572 0.679383 +vt 0.678054 0.659401 +vt 0.677207 0.664907 +vt 0.734813 0.524641 +vt 0.739758 0.516390 +vt 0.742027 0.522186 +vt 0.743840 0.507572 +vt 0.746600 0.512890 +vt 0.733374 0.519325 +vt 0.737943 0.511273 +vt 0.741683 0.502860 +vt 0.736463 0.530713 +vt 0.745243 0.528126 +vt 0.738622 0.537144 +vt 0.750556 0.518229 +vt 0.731758 0.544748 +vt 0.730299 0.538095 +vt 0.729178 0.531949 +vt 0.728155 0.526578 +vt 0.696073 0.677579 +vt 0.700260 0.672261 +vt 0.702659 0.681985 +vt 0.698193 0.686975 +vt 0.704007 0.692587 +vt 0.699406 0.697152 +vt 0.704437 0.703834 +vt 0.695463 0.701730 +vt 0.694458 0.691905 +vt 0.691909 0.705655 +vt 0.691149 0.696181 +vt 0.699771 0.707796 +vt 0.695711 0.711809 +vt 0.692008 0.715200 +vt 0.692674 0.682744 +vt 0.689709 0.687263 +vt 0.748638 0.488978 +vt 0.748855 0.478453 +vt 0.752644 0.481594 +vt 0.747572 0.467762 +vt 0.752330 0.469631 +vt 0.745833 0.485737 +vt 0.745653 0.475971 +vt 0.742329 0.463340 +vt 0.751755 0.492849 +vt 0.756374 0.484446 +vt 0.755093 0.496898 +vt 0.760076 0.487153 +vt 0.756954 0.470804 +vt 0.761511 0.471629 +vt 0.753497 0.507990 +vt 0.749791 0.503196 +vt 0.758543 0.501036 +vt 0.757600 0.512862 +vt 0.746883 0.498559 +vt 0.744411 0.494524 +vt 0.699346 0.718591 +vt 0.704082 0.715489 +vt 0.702549 0.727970 +vt 0.698158 0.729692 +vt 0.699448 0.741691 +vt 0.696233 0.741253 +vt 0.695563 0.756033 +vt 0.692410 0.742281 +vt 0.694098 0.731797 +vt 0.688384 0.743258 +vt 0.690264 0.733526 +vt 0.693939 0.753045 +vt 0.690443 0.752978 +vt 0.685674 0.755281 +vt 0.695227 0.721735 +vt 0.691466 0.724331 +vt 0.747815 0.444921 +vt 0.740903 0.448111 +vt 0.736065 0.438940 +vt 0.743819 0.433633 +vt 0.730801 0.431165 +vt 0.733980 0.451240 +vt 0.728311 0.444246 +vt 0.722746 0.438026 +vt 0.751574 0.428326 +vt 0.754702 0.441605 +vt 0.759328 0.423019 +vt 0.738855 0.424304 +vt 0.746910 0.417444 +vt 0.754965 0.410583 +vt 0.756446 0.456144 +vt 0.750686 0.457164 +vt 0.761578 0.438228 +vt 0.762159 0.454876 +vt 0.744830 0.457684 +vt 0.738927 0.457955 +vt 0.711289 0.323261 +vt 0.716761 0.322403 +vt 0.722007 0.331106 +vt 0.722234 0.321544 +vt 0.730079 0.330189 +vt 0.708167 0.314345 +vt 0.711251 0.313309 +vt 0.714334 0.312273 +vt 0.726724 0.339029 +vt 0.716099 0.339983 +vt 0.728800 0.347077 +vt 0.716651 0.347711 +vt 0.737814 0.337583 +vt 0.745385 0.343099 +vt 0.714052 0.331900 +vt 0.706403 0.339952 +vt 0.706329 0.332447 +vt 0.697171 0.339428 +vt 0.705983 0.346838 +vt 0.695724 0.343981 +vt 0.705816 0.324120 +vt 0.705084 0.315381 +vt 0.698723 0.332871 +vt 0.700344 0.324978 +vt 0.702000 0.316418 +vt 0.726856 0.413890 +vt 0.733078 0.417941 +vt 0.725593 0.425785 +vt 0.720394 0.422278 +vt 0.718107 0.433628 +vt 0.715157 0.420125 +vt 0.720560 0.411495 +vt 0.709836 0.418806 +vt 0.713892 0.410216 +vt 0.714088 0.430640 +vt 0.710379 0.428648 +vt 0.706674 0.427238 +vt 0.727211 0.402647 +vt 0.733631 0.405447 +vt 0.734488 0.393691 +vt 0.719740 0.401311 +vt 0.731810 0.391342 +vt 0.740563 0.410098 +vt 0.740562 0.396977 +vt 0.748048 0.402255 +vt 0.711742 0.362334 +vt 0.721634 0.365428 +vt 0.718273 0.374056 +vt 0.707902 0.368492 +vt 0.713451 0.380765 +vt 0.731228 0.367982 +vt 0.728524 0.378318 +vt 0.699783 0.363694 +vt 0.703086 0.359037 +vt 0.693767 0.359702 +vt 0.696295 0.355963 +vt 0.703057 0.373395 +vt 0.695578 0.367413 +vt 0.690374 0.362691 +vt 0.705015 0.353309 +vt 0.697481 0.351184 +vt 0.714929 0.355285 +vt 0.726123 0.356156 +vt 0.736409 0.357359 +vt 0.653801 0.424116 +vt 0.648882 0.427939 +vt 0.646055 0.423018 +vt 0.656664 0.428746 +vt 0.651630 0.432136 +vt 0.650696 0.418647 +vt 0.643174 0.417613 +vt 0.647623 0.412715 +vt 0.640267 0.411967 +vt 0.652062 0.408635 +vt 0.655614 0.415094 +vt 0.656960 0.405544 +vt 0.660857 0.412424 +vt 0.644566 0.406551 +vt 0.648702 0.402019 +vt 0.653383 0.398594 +vt 0.659550 0.421239 +vt 0.662900 0.426335 +vt 0.665394 0.419162 +vt 0.672044 0.425891 +vt 0.613345 0.774139 +vt 0.608211 0.778946 +vt 0.604896 0.772588 +vt 0.603076 0.783753 +vt 0.599693 0.776832 +vt 0.617066 0.779193 +vt 0.612123 0.784492 +vt 0.607180 0.789791 +vt 0.610099 0.768344 +vt 0.601886 0.765465 +vt 0.607202 0.761878 +vt 0.596569 0.769052 +vt 0.612518 0.758291 +vt 0.615301 0.764100 +vt 0.617834 0.754704 +vt 0.620504 0.759856 +vt 0.618479 0.769332 +vt 0.622010 0.773894 +vt 0.623613 0.764526 +vt 0.626953 0.768595 +vt 0.672412 0.409671 +vt 0.675747 0.416874 +vt 0.670598 0.417737 +vt 0.666475 0.410701 +vt 0.679047 0.424027 +vt 0.674939 0.424747 +vt 0.662788 0.403615 +vt 0.669009 0.402366 +vt 0.659318 0.396503 +vt 0.665573 0.395009 +vt 0.675090 0.401317 +vt 0.678609 0.409080 +vt 0.681742 0.400726 +vt 0.671219 0.393375 +vt 0.675322 0.390864 +vt 0.681423 0.416485 +vt 0.683883 0.423711 +vt 0.685099 0.408850 +vt 0.687315 0.416447 +vt 0.688960 0.423781 +vt 0.626161 0.787066 +vt 0.631234 0.790292 +vt 0.628311 0.796837 +vt 0.622396 0.793223 +vt 0.625387 0.803382 +vt 0.636458 0.793314 +vt 0.634449 0.800236 +vt 0.632440 0.807158 +vt 0.616925 0.789180 +vt 0.621388 0.783434 +vt 0.618630 0.799380 +vt 0.612463 0.794925 +vt 0.625851 0.777689 +vt 0.629927 0.780909 +vt 0.630314 0.771944 +vt 0.634158 0.783746 +vt 0.638467 0.786392 +vt 0.633692 0.774752 +vt 0.637081 0.777201 +vt 0.640476 0.779470 +vt 0.698958 0.401138 +vt 0.709651 0.401051 +vt 0.706557 0.409512 +vt 0.697655 0.393273 +vt 0.710158 0.392583 +vt 0.699061 0.409152 +vt 0.704381 0.417798 +vt 0.698804 0.417083 +vt 0.702665 0.425997 +vt 0.693112 0.416640 +vt 0.691913 0.408902 +vt 0.698366 0.424973 +vt 0.693793 0.424218 +vt 0.689677 0.400848 +vt 0.687209 0.393147 +vt 0.683255 0.380527 +vt 0.680293 0.375030 +vt 0.685859 0.372760 +vt 0.677412 0.370058 +vt 0.682310 0.367581 +vt 0.676534 0.381894 +vt 0.674488 0.377219 +vt 0.671237 0.373608 +vt 0.690015 0.378882 +vt 0.690946 0.370328 +vt 0.696855 0.376682 +vt 0.686593 0.365221 +vt 0.704577 0.384280 +vt 0.694310 0.385787 +vt 0.685314 0.386469 +vt 0.676953 0.386739 +vt 0.906682 0.104917 +vt 0.907301 0.110032 +vt 0.902112 0.107145 +vt 0.901782 0.103274 +vt 0.896938 0.103920 +vt 0.907577 0.114706 +vt 0.902168 0.110925 +vt 0.896811 0.105565 +vt 0.901168 0.098947 +vt 0.905753 0.099385 +vt 0.900272 0.094389 +vt 0.904552 0.093610 +vt 0.896874 0.101433 +vt 0.896575 0.098404 +vt 0.895999 0.095131 +vt 0.910323 0.099613 +vt 0.911564 0.106164 +vt 0.908846 0.092759 +vt 0.912518 0.112244 +vt 0.913137 0.117683 +vt 0.901467 0.082020 +vt 0.905375 0.078896 +vt 0.907183 0.085774 +vt 0.903116 0.087765 +vt 0.899097 0.089823 +vt 0.897639 0.085337 +vt 0.895103 0.091916 +vt 0.893851 0.088751 +vt 0.895895 0.081020 +vt 0.899629 0.076550 +vt 0.893862 0.076957 +vt 0.892212 0.085631 +vt 0.890151 0.082552 +vt 0.903467 0.072363 +vt 0.897627 0.071524 +vt 0.901501 0.066416 +vt 0.907012 0.122672 +vt 0.906198 0.126008 +vt 0.899727 0.122106 +vt 0.901232 0.118854 +vt 0.894486 0.118527 +vt 0.905209 0.129132 +vt 0.897834 0.125045 +vt 0.890774 0.121497 +vt 0.901960 0.114978 +vt 0.907476 0.118912 +vt 0.896602 0.115431 +vt 0.897645 0.111553 +vt 0.913370 0.122311 +vt 0.913314 0.126332 +vt 0.913066 0.129947 +vt 0.912721 0.133360 +vt 0.893034 0.063318 +vt 0.897319 0.056938 +vt 0.899521 0.061293 +vt 0.895482 0.067117 +vt 0.891536 0.073238 +vt 0.888814 0.069927 +vt 0.887636 0.079507 +vt 0.884626 0.076650 +vt 0.885590 0.067091 +vt 0.881762 0.064796 +vt 0.886574 0.057514 +vt 0.881083 0.074135 +vt 0.876968 0.072116 +vt 0.890119 0.060120 +vt 0.894689 0.053295 +vt 0.891423 0.050309 +vt 0.379221 0.647564 +vt 0.370043 0.653673 +vt 0.370317 0.648287 +vt 0.360629 0.659869 +vt 0.361801 0.654623 +vt 0.379642 0.652713 +vt 0.370068 0.658393 +vt 0.358336 0.663600 +vt 0.378761 0.642115 +vt 0.370177 0.642300 +vt 0.378121 0.636428 +vt 0.369830 0.636014 +vt 0.362225 0.648366 +vt 0.362275 0.641605 +vt 0.386047 0.630944 +vt 0.387060 0.636274 +vt 0.377391 0.630622 +vt 0.384961 0.625628 +vt 0.387926 0.641630 +vt 0.388573 0.647026 +vt 0.882627 0.046062 +vt 0.887313 0.047922 +vt 0.882238 0.055491 +vt 0.877439 0.053980 +vt 0.877224 0.063106 +vt 0.872360 0.062001 +vt 0.872241 0.070745 +vt 0.867551 0.061458 +vt 0.872506 0.052913 +vt 0.863158 0.060985 +vt 0.867762 0.052100 +vt 0.867335 0.070073 +vt 0.862682 0.070146 +vt 0.858714 0.071014 +vt 0.877631 0.044653 +vt 0.872592 0.043622 +vt 0.380282 0.700914 +vt 0.388232 0.699286 +vt 0.387991 0.712488 +vt 0.380157 0.713935 +vt 0.387850 0.725034 +vt 0.372863 0.715512 +vt 0.372821 0.702690 +vt 0.366231 0.717075 +vt 0.365973 0.704463 +vt 0.380206 0.726418 +vt 0.373135 0.727892 +vt 0.366742 0.729291 +vt 0.372884 0.690499 +vt 0.380485 0.688406 +vt 0.372927 0.680016 +vt 0.365836 0.692537 +vt 0.365688 0.682381 +vt 0.388514 0.686439 +vt 0.380667 0.677462 +vt 0.388780 0.674960 +vt 0.312292 0.716983 +vt 0.308668 0.703423 +vt 0.313759 0.702122 +vt 0.317708 0.715598 +vt 0.319092 0.701205 +vt 0.304896 0.689500 +vt 0.309509 0.688290 +vt 0.313087 0.686918 +vt 0.321055 0.728361 +vt 0.315624 0.729815 +vt 0.323871 0.740109 +vt 0.318610 0.741568 +vt 0.323081 0.714369 +vt 0.326311 0.726975 +vt 0.328922 0.738670 +vt 0.310077 0.731314 +vt 0.306712 0.718402 +vt 0.304471 0.732836 +vt 0.313210 0.743040 +vt 0.307739 0.744519 +vt 0.303192 0.704699 +vt 0.299595 0.690601 +vt 0.301049 0.719838 +vt 0.297525 0.705963 +vt 0.293948 0.691649 +vt 0.380590 0.662714 +vt 0.372304 0.666679 +vt 0.371104 0.662383 +vt 0.380163 0.657502 +vt 0.364566 0.669933 +vt 0.362801 0.666260 +vt 0.388926 0.652475 +vt 0.389042 0.658560 +vt 0.388975 0.665861 +vt 0.380730 0.669133 +vt 0.372823 0.672316 +vt 0.365397 0.675076 +vt 0.859686 0.050719 +vt 0.856104 0.050234 +vt 0.858590 0.042045 +vt 0.863127 0.042393 +vt 0.852653 0.049824 +vt 0.854108 0.041772 +vt 0.867776 0.042894 +vt 0.863531 0.051355 +vt 0.859540 0.060090 +vt 0.856514 0.059114 +vt 0.855676 0.068963 +vt 0.853899 0.058397 +vt 0.851490 0.057810 +vt 0.853476 0.067545 +vt 0.851835 0.066548 +vt 0.850473 0.065762 +vt 0.363453 0.750497 +vt 0.364660 0.759646 +vt 0.360127 0.759241 +vt 0.365852 0.768595 +vt 0.361591 0.767352 +vt 0.368680 0.749875 +vt 0.369621 0.759418 +vt 0.369700 0.770480 +vt 0.358696 0.750796 +vt 0.355652 0.759255 +vt 0.354215 0.751213 +vt 0.351268 0.759902 +vt 0.357111 0.766914 +vt 0.352606 0.767444 +vt 0.352821 0.742404 +vt 0.357330 0.741680 +vt 0.351494 0.732446 +vt 0.356112 0.731474 +vt 0.349914 0.751979 +vt 0.348526 0.743294 +vt 0.347088 0.733465 +vt 0.362215 0.740946 +vt 0.367639 0.740028 +vt 0.361131 0.730453 +vt 0.340007 0.745976 +vt 0.344281 0.744522 +vt 0.345699 0.753325 +vt 0.338263 0.735900 +vt 0.342702 0.734626 +vt 0.341498 0.755025 +vt 0.347011 0.761398 +vt 0.342819 0.763388 +vt 0.348270 0.769106 +vt 0.338631 0.765517 +vt 0.337237 0.756852 +vt 0.334386 0.767430 +vt 0.332842 0.758581 +vt 0.344056 0.771408 +vt 0.339919 0.773858 +vt 0.335812 0.775962 +vt 0.335627 0.747540 +vt 0.333695 0.737258 +vt 0.331060 0.749100 +vt 0.353596 0.695948 +vt 0.359464 0.694339 +vt 0.359863 0.706081 +vt 0.352940 0.685941 +vt 0.359078 0.684315 +vt 0.354304 0.707567 +vt 0.360383 0.718476 +vt 0.355123 0.719755 +vt 0.350255 0.720955 +vt 0.349109 0.708940 +vt 0.345582 0.722113 +vt 0.344090 0.710221 +vt 0.348063 0.697411 +vt 0.347121 0.687379 +vt 0.342691 0.698773 +vt 0.341465 0.688753 +vt 0.339061 0.711432 +vt 0.340910 0.723272 +vt 0.336180 0.724453 +vt 0.333912 0.712516 +vt 0.331333 0.725680 +vt 0.328534 0.713418 +vt 0.325293 0.701081 +vt 0.331615 0.700967 +vt 0.322774 0.690845 +vt 0.329735 0.691077 +vt 0.337312 0.700078 +vt 0.335817 0.690183 +vt 0.345541 0.674207 +vt 0.344848 0.669813 +vt 0.350648 0.669199 +vt 0.344182 0.666046 +vt 0.349688 0.665843 +vt 0.339707 0.675346 +vt 0.339049 0.670753 +vt 0.338455 0.666762 +vt 0.351535 0.673223 +vt 0.356308 0.668273 +vt 0.354548 0.665310 +vt 0.357760 0.671957 +vt 0.358597 0.677079 +vt 0.352276 0.678585 +vt 0.346290 0.679853 +vt 0.340491 0.681145 +vt 0.328210 0.679160 +vt 0.328720 0.684118 +vt 0.322143 0.684883 +vt 0.322357 0.681346 +vt 0.315287 0.685331 +vt 0.322370 0.678381 +vt 0.327846 0.675277 +vt 0.322283 0.675703 +vt 0.316453 0.683582 +vt 0.316930 0.681726 +vt 0.317062 0.679815 +vt 0.333394 0.672654 +vt 0.333961 0.677079 +vt 0.327556 0.671930 +vt 0.332929 0.668837 +vt 0.334735 0.682719 +vt 0.569292 0.807915 +vt 0.571697 0.807194 +vt 0.571570 0.811410 +vt 0.569031 0.812006 +vt 0.571436 0.815545 +vt 0.574159 0.806387 +vt 0.574177 0.810738 +vt 0.574198 0.815027 +vt 0.566627 0.812448 +vt 0.566999 0.808463 +vt 0.564426 0.812661 +vt 0.564931 0.808854 +vt 0.568740 0.815976 +vt 0.566178 0.816233 +vt 0.563817 0.816229 +vt 0.567218 0.804075 +vt 0.569493 0.803580 +vt 0.567364 0.799415 +vt 0.565228 0.804572 +vt 0.564900 0.798954 +vt 0.571809 0.802813 +vt 0.574146 0.801912 +vt 0.569642 0.798963 +vt 0.571897 0.798188 +vt 0.574140 0.797251 +vt 0.310713 0.945797 +vt 0.313489 0.949121 +vt 0.311163 0.949743 +vt 0.308557 0.946538 +vt 0.309086 0.950348 +vt 0.316087 0.952982 +vt 0.313650 0.953495 +vt 0.311521 0.953997 +vt 0.305874 0.943696 +vt 0.307925 0.942857 +vt 0.303151 0.941037 +vt 0.305132 0.940110 +vt 0.306554 0.947274 +vt 0.303958 0.944585 +vt 0.301329 0.942087 +vt 0.310060 0.941817 +vt 0.313006 0.944786 +vt 0.312224 0.940325 +vt 0.307224 0.939094 +vt 0.309376 0.937778 +vt 0.316172 0.948246 +vt 0.319028 0.952287 +vt 0.315267 0.943135 +vt 0.318698 0.946469 +vt 0.323441 0.951182 +vt 0.406972 0.951138 +vt 0.406202 0.955008 +vt 0.403640 0.954813 +vt 0.405862 0.959167 +vt 0.403263 0.958888 +vt 0.409518 0.951348 +vt 0.408763 0.955204 +vt 0.408462 0.959445 +vt 0.404427 0.950928 +vt 0.401078 0.954617 +vt 0.401882 0.950718 +vt 0.398516 0.954422 +vt 0.400664 0.958610 +vt 0.398065 0.958332 +vt 0.402945 0.946880 +vt 0.405488 0.947170 +vt 0.404137 0.943073 +vt 0.399336 0.950507 +vt 0.400401 0.946590 +vt 0.401589 0.942671 +vt 0.408031 0.947459 +vt 0.410574 0.947749 +vt 0.406685 0.943475 +vt 0.409233 0.943877 +vt 0.411781 0.944279 +vt 0.684411 0.946745 +vt 0.686827 0.946374 +vt 0.686410 0.951504 +vt 0.689167 0.946140 +vt 0.688665 0.951306 +vt 0.684700 0.941829 +vt 0.687211 0.941361 +vt 0.689632 0.941130 +vt 0.684113 0.951690 +vt 0.685928 0.956865 +vt 0.683800 0.956693 +vt 0.685413 0.962343 +vt 0.688086 0.956787 +vt 0.687122 0.963489 +vt 0.681576 0.956708 +vt 0.681760 0.951958 +vt 0.679304 0.956817 +vt 0.679378 0.952266 +vt 0.683478 0.961725 +vt 0.681391 0.961458 +vt 0.679229 0.961368 +vt 0.681945 0.947207 +vt 0.682129 0.942457 +vt 0.679453 0.947715 +vt 0.679528 0.943164 +vt 0.406767 0.968539 +vt 0.407722 0.973560 +vt 0.404870 0.972384 +vt 0.408822 0.978678 +vt 0.405862 0.977094 +vt 0.409518 0.969340 +vt 0.410574 0.974737 +vt 0.411781 0.980262 +vt 0.404015 0.967737 +vt 0.402018 0.971207 +vt 0.401264 0.966936 +vt 0.399166 0.970031 +vt 0.402902 0.975510 +vt 0.399942 0.973925 +vt 0.400769 0.962727 +vt 0.403434 0.963218 +vt 0.398513 0.966135 +vt 0.398105 0.962236 +vt 0.406099 0.963709 +vt 0.408763 0.964200 +vt 0.894872 0.586194 +vt 0.892389 0.586738 +vt 0.892331 0.582679 +vt 0.889905 0.587281 +vt 0.889828 0.583644 +vt 0.894911 0.590675 +vt 0.892447 0.590796 +vt 0.889983 0.590918 +vt 0.894834 0.581714 +vt 0.892272 0.578620 +vt 0.894795 0.577234 +vt 0.892214 0.574562 +vt 0.889750 0.580007 +vt 0.889673 0.576369 +vt 0.897317 0.575848 +vt 0.897337 0.580750 +vt 0.899839 0.574462 +vt 0.899839 0.579785 +vt 0.894756 0.572754 +vt 0.897298 0.570947 +vt 0.899839 0.569139 +vt 0.897356 0.585651 +vt 0.897375 0.590553 +vt 0.899839 0.585108 +vt 0.899839 0.590431 +vt 0.278033 0.666753 +vt 0.275302 0.666153 +vt 0.275529 0.663062 +vt 0.278217 0.663231 +vt 0.275757 0.659970 +vt 0.272508 0.665437 +vt 0.272780 0.662806 +vt 0.273051 0.660174 +vt 0.280782 0.663229 +vt 0.280637 0.667120 +vt 0.283160 0.662967 +vt 0.278402 0.659710 +vt 0.280926 0.659337 +vt 0.283268 0.658796 +vt 0.280493 0.671011 +vt 0.277848 0.670274 +vt 0.280349 0.674902 +vt 0.277664 0.673796 +vt 0.283053 0.667138 +vt 0.282945 0.671308 +vt 0.282838 0.675479 +vt 0.275074 0.669245 +vt 0.272236 0.668069 +vt 0.274847 0.672336 +vt 0.271964 0.670700 +vt 0.650296 0.969850 +vt 0.649802 0.967510 +vt 0.653652 0.968058 +vt 0.649732 0.965395 +vt 0.653702 0.966258 +vt 0.646627 0.969677 +vt 0.645951 0.966962 +vt 0.645762 0.964532 +vt 0.653964 0.970022 +vt 0.657503 0.968606 +vt 0.661354 0.969153 +vt 0.661301 0.970368 +vt 0.657672 0.967122 +vt 0.661643 0.967985 +vt 0.657633 0.970195 +vt 0.657963 0.971856 +vt 0.654518 0.972097 +vt 0.658392 0.973551 +vt 0.661407 0.971614 +vt 0.661592 0.972875 +vt 0.651073 0.972339 +vt 0.647628 0.972580 +vt 0.655192 0.974227 +vt 0.651991 0.974903 +vt 0.648791 0.975579 +vt 0.569832 0.783317 +vt 0.572004 0.782674 +vt 0.571987 0.788036 +vt 0.574177 0.782031 +vt 0.574159 0.787249 +vt 0.569830 0.777709 +vt 0.572014 0.777230 +vt 0.574198 0.776751 +vt 0.569811 0.788798 +vt 0.571953 0.793235 +vt 0.569748 0.794025 +vt 0.574146 0.792343 +vt 0.567516 0.794610 +vt 0.567629 0.789509 +vt 0.565270 0.795094 +vt 0.565444 0.790195 +vt 0.567659 0.783961 +vt 0.567647 0.778188 +vt 0.565486 0.784604 +vt 0.565463 0.778667 +vt 0.318221 0.963134 +vt 0.315981 0.957976 +vt 0.318342 0.957533 +vt 0.316052 0.963593 +vt 0.313827 0.958415 +vt 0.320422 0.962615 +vt 0.321043 0.956997 +vt 0.322731 0.962128 +vt 0.323913 0.956414 +vt 0.324608 0.967432 +vt 0.322497 0.968067 +vt 0.326579 0.972824 +vt 0.324569 0.973705 +vt 0.325092 0.961658 +vt 0.326744 0.966907 +vt 0.328632 0.972161 +vt 0.320437 0.968920 +vt 0.318241 0.969527 +vt 0.322640 0.975020 +vt 0.320836 0.976988 +vt 0.313864 0.978242 +vt 0.316504 0.978953 +vt 0.315420 0.982738 +vt 0.319198 0.979825 +vt 0.317684 0.983313 +vt 0.313953 0.973912 +vt 0.316809 0.974708 +vt 0.313134 0.982252 +vt 0.314171 0.986646 +vt 0.312066 0.986156 +vt 0.312838 0.990614 +vt 0.316252 0.987237 +vt 0.314862 0.991378 +vt 0.309915 0.985868 +vt 0.310805 0.981945 +vt 0.307694 0.985885 +vt 0.308344 0.981826 +vt 0.310828 0.990005 +vt 0.308843 0.989708 +vt 0.306898 0.989876 +vt 0.311334 0.977855 +vt 0.311318 0.973515 +vt 0.308699 0.977632 +vt 0.308614 0.973234 +vt 0.309365 0.964229 +vt 0.307928 0.960076 +vt 0.309775 0.959507 +vt 0.306216 0.956554 +vt 0.306961 0.964065 +vt 0.305961 0.960174 +vt 0.305212 0.957555 +vt 0.311605 0.964096 +vt 0.311750 0.958939 +vt 0.313840 0.963899 +vt 0.307700 0.955502 +vt 0.309503 0.954658 +vt 0.315724 0.969423 +vt 0.313098 0.969047 +vt 0.310578 0.968840 +vt 0.307940 0.968565 +vt 0.287190 0.665894 +vt 0.285216 0.666690 +vt 0.285292 0.662360 +vt 0.287238 0.661495 +vt 0.285368 0.658031 +vt 0.289061 0.660457 +vt 0.289037 0.664864 +vt 0.290821 0.659333 +vt 0.287287 0.657097 +vt 0.289084 0.656050 +vt 0.290821 0.654948 +vt 0.289013 0.669271 +vt 0.287141 0.670292 +vt 0.288990 0.673679 +vt 0.290821 0.663719 +vt 0.290821 0.668104 +vt 0.290821 0.672490 +vt 0.285140 0.671020 +vt 0.287092 0.674690 +vt 0.285063 0.675349 +vt 0.655046 0.963082 +vt 0.651058 0.961866 +vt 0.651991 0.960056 +vt 0.655934 0.961229 +vt 0.652795 0.957951 +vt 0.647070 0.960649 +vt 0.648048 0.958882 +vt 0.648896 0.957020 +vt 0.659877 0.962403 +vt 0.659034 0.964298 +vt 0.663820 0.963577 +vt 0.663022 0.965515 +vt 0.656695 0.958883 +vt 0.660595 0.959814 +vt 0.664494 0.960746 +vt 0.658241 0.965779 +vt 0.654235 0.964679 +vt 0.662248 0.966878 +vt 0.650228 0.963580 +vt 0.646221 0.962480 +vt 0.734261 0.951483 +vt 0.734267 0.948063 +vt 0.736221 0.947838 +vt 0.734599 0.944574 +vt 0.732390 0.951961 +vt 0.732442 0.948243 +vt 0.732844 0.944450 +vt 0.738432 0.947522 +vt 0.738445 0.950567 +vt 0.741031 0.947071 +vt 0.741006 0.950208 +vt 0.736486 0.944578 +vt 0.738635 0.944344 +vt 0.741179 0.943753 +vt 0.738887 0.953345 +vt 0.736838 0.954026 +vt 0.740133 0.955774 +vt 0.738333 0.956802 +vt 0.741227 0.952981 +vt 0.741618 0.954895 +vt 0.736242 0.951015 +vt 0.734907 0.954763 +vt 0.733036 0.955528 +vt 0.736532 0.957834 +vt 0.734730 0.958868 +vt 0.692924 0.962215 +vt 0.694913 0.961064 +vt 0.695382 0.963718 +vt 0.696819 0.960077 +vt 0.697217 0.962495 +vt 0.692599 0.958999 +vt 0.694693 0.958025 +vt 0.696660 0.957299 +vt 0.696015 0.966117 +vt 0.694255 0.967560 +vt 0.696731 0.968387 +vt 0.695050 0.970043 +vt 0.697775 0.964673 +vt 0.698412 0.966732 +vt 0.692496 0.969004 +vt 0.691627 0.966329 +vt 0.690736 0.970447 +vt 0.689707 0.967717 +vt 0.693369 0.971698 +vt 0.691688 0.973353 +vt 0.693525 0.964983 +vt 0.690768 0.963695 +vt 0.690249 0.960468 +vt 0.688528 0.965339 +vt 0.743368 0.963210 +vt 0.739461 0.960626 +vt 0.741054 0.959273 +vt 0.741961 0.964715 +vt 0.737822 0.961905 +vt 0.744704 0.961543 +vt 0.742553 0.957770 +vt 0.745897 0.959552 +vt 0.743648 0.955837 +vt 0.749912 0.961337 +vt 0.748989 0.963715 +vt 0.754263 0.963125 +vt 0.753590 0.965837 +vt 0.746809 0.957023 +vt 0.750630 0.958303 +vt 0.754781 0.959629 +vt 0.747928 0.965654 +vt 0.746798 0.967373 +vt 0.752813 0.968028 +vt 0.751985 0.969957 +vt 0.693572 0.946172 +vt 0.695686 0.946348 +vt 0.695162 0.950539 +vt 0.697775 0.946570 +vt 0.694142 0.941544 +vt 0.696292 0.942029 +vt 0.698412 0.942594 +vt 0.693067 0.950712 +vt 0.694804 0.954474 +vt 0.692695 0.955076 +vt 0.697217 0.950426 +vt 0.696819 0.954042 +vt 0.690398 0.956019 +vt 0.690893 0.951005 +vt 0.691407 0.946088 +vt 0.691932 0.941218 +vt 0.561684 0.809180 +vt 0.563198 0.809104 +vt 0.562494 0.812566 +vt 0.560765 0.812241 +vt 0.561723 0.815877 +vt 0.559170 0.811763 +vt 0.560272 0.809048 +vt 0.557643 0.811208 +vt 0.559829 0.815264 +vt 0.558068 0.814478 +vt 0.556374 0.813604 +vt 0.561374 0.806333 +vt 0.562569 0.806044 +vt 0.562476 0.803618 +vt 0.563437 0.802871 +vt 0.558912 0.808811 +vt 0.560180 0.806415 +vt 0.561449 0.804019 +vt 0.563766 0.805343 +vt 0.564267 0.801432 +vt 0.305474 0.952299 +vt 0.304176 0.953836 +vt 0.301918 0.951631 +vt 0.303483 0.956080 +vt 0.301171 0.954333 +vt 0.303140 0.949676 +vt 0.299553 0.949650 +vt 0.300745 0.947411 +vt 0.297134 0.947782 +vt 0.298707 0.952642 +vt 0.296167 0.950980 +vt 0.302231 0.945772 +vt 0.304717 0.948272 +vt 0.298319 0.945324 +vt 0.299718 0.943470 +vt 0.307150 0.951168 +vt 0.747649 0.945674 +vt 0.751409 0.944817 +vt 0.751309 0.949824 +vt 0.755298 0.943915 +vt 0.755247 0.949763 +vt 0.747713 0.941257 +vt 0.751440 0.939592 +vt 0.755298 0.937807 +vt 0.747530 0.949900 +vt 0.751073 0.954392 +vt 0.755091 0.955087 +vt 0.747301 0.953745 +vt 0.744025 0.953194 +vt 0.744065 0.950007 +vt 0.744146 0.946440 +vt 0.744249 0.942684 +vt 0.657309 0.952177 +vt 0.653413 0.952390 +vt 0.653406 0.949178 +vt 0.657327 0.948184 +vt 0.653310 0.945844 +vt 0.649517 0.952602 +vt 0.649486 0.950172 +vt 0.649357 0.947680 +vt 0.661248 0.947189 +vt 0.661205 0.951964 +vt 0.665169 0.946195 +vt 0.657263 0.944008 +vt 0.661216 0.942172 +vt 0.665169 0.940336 +vt 0.661012 0.956254 +vt 0.657126 0.955804 +vt 0.665101 0.951752 +vt 0.664899 0.956703 +vt 0.653239 0.955355 +vt 0.649353 0.954906 +vt 0.302813 0.982130 +vt 0.299854 0.982454 +vt 0.299215 0.976979 +vt 0.302561 0.977194 +vt 0.298688 0.971556 +vt 0.296840 0.982827 +vt 0.295790 0.976764 +vt 0.295078 0.970857 +vt 0.305749 0.977412 +vt 0.305661 0.981904 +vt 0.302208 0.972213 +vt 0.305547 0.972786 +vt 0.305381 0.986307 +vt 0.302998 0.987034 +vt 0.305004 0.990666 +vt 0.300569 0.987963 +vt 0.298117 0.988994 +vt 0.303150 0.991923 +vt 0.301322 0.993490 +vt 0.299507 0.995213 +vt 0.301269 0.962334 +vt 0.301020 0.957978 +vt 0.303622 0.959333 +vt 0.304234 0.963374 +vt 0.304959 0.967982 +vt 0.301721 0.967169 +vt 0.298312 0.966202 +vt 0.298149 0.961127 +vt 0.294818 0.965159 +vt 0.298259 0.956537 +vt 0.294951 0.959835 +vt 0.295420 0.955053 +vt 0.976897 0.434845 +vt 0.979421 0.427097 +vt 0.983436 0.432255 +vt 0.981151 0.438640 +vt 0.987451 0.437412 +vt 0.982388 0.419209 +vt 0.986041 0.425759 +vt 0.989693 0.432309 +vt 0.979505 0.444807 +vt 0.975260 0.442313 +vt 0.978818 0.450645 +vt 0.974951 0.449362 +vt 0.985404 0.442436 +vt 0.983751 0.447301 +vt 0.982686 0.451928 +vt 0.971014 0.439820 +vt 0.972644 0.431050 +vt 0.966768 0.437326 +vt 0.971083 0.448079 +vt 0.967215 0.446796 +vt 0.975407 0.421939 +vt 0.978736 0.412659 +vt 0.968390 0.427254 +vt 0.971392 0.416782 +vt 0.975083 0.406109 +vt 0.745842 0.370564 +vt 0.738993 0.369660 +vt 0.743596 0.360327 +vt 0.749095 0.363439 +vt 0.749276 0.353237 +vt 0.754316 0.365073 +vt 0.752688 0.370793 +vt 0.759397 0.365968 +vt 0.759532 0.370686 +vt 0.753056 0.357691 +vt 0.756689 0.360225 +vt 0.760247 0.361800 +vt 0.751556 0.377096 +vt 0.743353 0.378288 +vt 0.750673 0.383689 +vt 0.760324 0.375770 +vt 0.761443 0.381037 +vt 0.736280 0.379212 +vt 0.741247 0.386311 +vt 0.734511 0.388872 +vt 0.979206 0.461921 +vt 0.976414 0.455851 +vt 0.979410 0.456045 +vt 0.980961 0.461116 +vt 0.982406 0.456239 +vt 0.983151 0.465968 +vt 0.982884 0.467710 +vt 0.985660 0.470710 +vt 0.982716 0.460311 +vt 0.983418 0.464225 +vt 0.984316 0.468060 +vt 0.982616 0.469453 +vt 0.977450 0.462725 +vt 0.982349 0.471195 +vt 0.975695 0.463530 +vt 0.987005 0.473360 +vt 0.988349 0.476010 +vt 0.989693 0.478660 +vt 0.973417 0.455657 +vt 0.970421 0.455464 +vt 0.761349 0.349616 +vt 0.767593 0.348589 +vt 0.768182 0.350046 +vt 0.773894 0.347449 +vt 0.773760 0.348507 +vt 0.759926 0.347248 +vt 0.767005 0.347133 +vt 0.774029 0.346391 +vt 0.762765 0.351828 +vt 0.768771 0.351502 +vt 0.764164 0.353731 +vt 0.769359 0.352959 +vt 0.773625 0.349564 +vt 0.773491 0.350622 +vt 0.760054 0.356542 +vt 0.757668 0.354099 +vt 0.765556 0.355478 +vt 0.762409 0.358364 +vt 0.755218 0.350416 +vt 0.752737 0.346113 +vt 0.385335 0.775889 +vt 0.381812 0.777039 +vt 0.380048 0.773568 +vt 0.378205 0.778526 +vt 0.375724 0.775565 +vt 0.386071 0.779810 +vt 0.383510 0.780418 +vt 0.380535 0.781606 +vt 0.384507 0.771789 +vt 0.378152 0.769912 +vt 0.383493 0.767332 +vt 0.376478 0.764936 +vt 0.372940 0.772843 +vt 0.389094 0.765456 +vt 0.389235 0.770446 +vt 0.395256 0.764146 +vt 0.394758 0.769382 +vt 0.382489 0.761945 +vt 0.388649 0.759892 +vt 0.395257 0.758541 +vt 0.388690 0.775410 +vt 0.387803 0.780361 +vt 0.393182 0.774928 +vt 0.388291 0.782651 +vt 0.321316 0.933409 +vt 0.324030 0.934480 +vt 0.322180 0.938918 +vt 0.326744 0.935551 +vt 0.325092 0.940753 +vt 0.323350 0.929511 +vt 0.325991 0.929931 +vt 0.328632 0.930351 +vt 0.319302 0.937124 +vt 0.320552 0.943138 +vt 0.317325 0.940474 +vt 0.323913 0.945962 +vt 0.314366 0.938131 +vt 0.316490 0.935410 +vt 0.311540 0.935949 +vt 0.313711 0.933736 +vt 0.318602 0.932338 +vt 0.320709 0.929091 +vt 0.315888 0.931267 +vt 0.318068 0.928670 +vt 0.381687 0.755056 +vt 0.375381 0.757508 +vt 0.374573 0.748491 +vt 0.381047 0.746799 +vt 0.373765 0.738752 +vt 0.380528 0.737313 +vt 0.387864 0.735911 +vt 0.388015 0.745255 +vt 0.395708 0.734741 +vt 0.395497 0.744012 +vt 0.388283 0.753203 +vt 0.395342 0.751888 +vt 0.318623 0.762145 +vt 0.316032 0.753185 +vt 0.321196 0.751893 +vt 0.313711 0.763062 +vt 0.310802 0.754448 +vt 0.323483 0.761146 +vt 0.326228 0.750541 +vt 0.328240 0.759985 +vt 0.330023 0.768771 +vt 0.325570 0.769685 +vt 0.331691 0.777228 +vt 0.327557 0.777867 +vt 0.321059 0.770313 +vt 0.316517 0.770799 +vt 0.323417 0.778086 +vt 0.319272 0.778097 +vt 0.411934 0.742775 +vt 0.412617 0.733583 +vt 0.421460 0.733381 +vt 0.420621 0.742554 +vt 0.430413 0.733286 +vt 0.419708 0.750303 +vt 0.411214 0.750555 +vt 0.418824 0.756834 +vt 0.429443 0.742447 +vt 0.428361 0.750168 +vt 0.427303 0.756656 +vt 0.403039 0.751044 +vt 0.403515 0.743223 +vt 0.410532 0.757130 +vt 0.402614 0.757660 +vt 0.403996 0.734001 +vt 0.074529 0.745669 +vt 0.080455 0.741709 +vt 0.084295 0.750746 +vt 0.085532 0.738360 +vt 0.089427 0.747423 +vt 0.071135 0.736315 +vt 0.076897 0.732303 +vt 0.081854 0.728927 +vt 0.078280 0.754650 +vt 0.088699 0.759047 +vt 0.093950 0.766244 +vt 0.088282 0.770006 +vt 0.093757 0.755747 +vt 0.098741 0.762961 +vt 0.082745 0.762886 +vt 0.075839 0.767383 +vt 0.071318 0.759261 +vt 0.081684 0.774361 +vt 0.067678 0.750376 +vt 0.064480 0.741111 +vt 0.409963 0.762704 +vt 0.418075 0.762357 +vt 0.417342 0.767186 +vt 0.426402 0.762118 +vt 0.425580 0.766813 +vt 0.409288 0.767686 +vt 0.416511 0.771634 +vt 0.408287 0.772484 +vt 0.415463 0.776017 +vt 0.424758 0.771001 +vt 0.423859 0.774943 +vt 0.400112 0.773767 +vt 0.401601 0.768440 +vt 0.406949 0.777358 +vt 0.398199 0.779234 +vt 0.402285 0.763265 +vt 0.093536 0.732630 +vt 0.096814 0.729335 +vt 0.100659 0.738748 +vt 0.099842 0.725143 +vt 0.103652 0.735098 +vt 0.089780 0.723085 +vt 0.093060 0.719495 +vt 0.096092 0.714711 +vt 0.097418 0.741781 +vt 0.104686 0.747307 +vt 0.101550 0.750140 +vt 0.108987 0.754586 +vt 0.107583 0.744102 +vt 0.111695 0.751678 +vt 0.097974 0.752866 +vt 0.093737 0.744554 +vt 0.106059 0.757314 +vt 0.102710 0.760045 +vt 0.089834 0.735486 +vt 0.086096 0.726041 +vt 0.403245 0.788109 +vt 0.412452 0.785467 +vt 0.410655 0.790410 +vt 0.400925 0.793978 +vt 0.408774 0.795415 +vt 0.421633 0.782867 +vt 0.420384 0.786841 +vt 0.419096 0.790819 +vt 0.391195 0.797547 +vt 0.393989 0.790837 +vt 0.381465 0.801115 +vt 0.384708 0.793608 +vt 0.398452 0.800011 +vt 0.388130 0.804606 +vt 0.377808 0.809202 +vt 0.396240 0.784829 +vt 0.405260 0.782568 +vt 0.387121 0.787260 +vt 0.414082 0.780648 +vt 0.422805 0.778900 +vt 0.109480 0.725123 +vt 0.106588 0.730475 +vt 0.102795 0.719596 +vt 0.105693 0.713035 +vt 0.099033 0.708171 +vt 0.108553 0.705796 +vt 0.112344 0.719283 +vt 0.111394 0.698218 +vt 0.115194 0.713200 +vt 0.101905 0.700313 +vt 0.104731 0.691578 +vt 0.107535 0.682403 +vt 0.116077 0.731309 +vt 0.113268 0.735944 +vt 0.119721 0.741144 +vt 0.118875 0.726516 +vt 0.122379 0.737331 +vt 0.110440 0.740261 +vt 0.117057 0.744866 +vt 0.114383 0.748408 +vt 0.870313 0.106949 +vt 0.869385 0.107596 +vt 0.868633 0.103277 +vt 0.868457 0.108243 +vt 0.867627 0.103796 +vt 0.871777 0.110339 +vt 0.871017 0.111113 +vt 0.870257 0.111888 +vt 0.869683 0.102785 +vt 0.868163 0.098345 +vt 0.869375 0.098079 +vt 0.867377 0.092988 +vt 0.867126 0.098719 +vt 0.866314 0.093185 +vt 0.870938 0.098029 +vt 0.870821 0.102347 +vt 0.872677 0.098089 +vt 0.872002 0.101937 +vt 0.868717 0.092939 +vt 0.870610 0.093187 +vt 0.874075 0.094566 +vt 0.871241 0.106303 +vt 0.872537 0.109564 +vt 0.872169 0.105656 +vt 0.873297 0.108790 +vt 0.866280 0.081903 +vt 0.868850 0.087660 +vt 0.867035 0.087477 +vt 0.868220 0.081577 +vt 0.870981 0.087402 +vt 0.864687 0.081870 +vt 0.865678 0.087396 +vt 0.863433 0.081732 +vt 0.864549 0.087365 +vt 0.861010 0.076162 +vt 0.862027 0.076297 +vt 0.858499 0.070638 +vt 0.859213 0.070740 +vt 0.862349 0.081541 +vt 0.860232 0.075997 +vt 0.863521 0.076373 +vt 0.865449 0.076150 +vt 0.860667 0.070956 +vt 0.862673 0.070923 +vt 0.882400 0.114845 +vt 0.883211 0.116746 +vt 0.878323 0.115431 +vt 0.884206 0.118884 +vt 0.878411 0.116731 +vt 0.886350 0.114565 +vt 0.887853 0.116986 +vt 0.878280 0.114191 +vt 0.874127 0.113639 +vt 0.874586 0.112721 +vt 0.873668 0.114558 +vt 0.875045 0.111803 +vt 0.878329 0.113071 +vt 0.875503 0.110884 +vt 0.881957 0.113423 +vt 0.885493 0.112911 +vt 0.878425 0.112010 +vt 0.881698 0.112238 +vt 0.884960 0.111640 +vt 0.214037 0.975412 +vt 0.217160 0.979129 +vt 0.217016 0.980493 +vt 0.220501 0.982812 +vt 0.220786 0.984514 +vt 0.214581 0.974366 +vt 0.217303 0.977764 +vt 0.220217 0.981111 +vt 0.213493 0.976458 +vt 0.216873 0.981858 +vt 0.212950 0.977503 +vt 0.216730 0.983223 +vt 0.221070 0.986215 +vt 0.221355 0.987916 +vt 0.209573 0.973152 +vt 0.210462 0.972390 +vt 0.207018 0.968806 +vt 0.208169 0.968275 +vt 0.212406 0.978549 +vt 0.208685 0.973914 +vt 0.205867 0.969336 +vt 0.211351 0.971628 +vt 0.212240 0.970866 +vt 0.209320 0.967744 +vt 0.210471 0.967214 +vt 0.892110 0.111132 +vt 0.894243 0.108545 +vt 0.895353 0.109681 +vt 0.893749 0.112956 +vt 0.896153 0.105715 +vt 0.896538 0.106068 +vt 0.891307 0.115557 +vt 0.889533 0.113233 +vt 0.888504 0.111590 +vt 0.891120 0.109661 +vt 0.887848 0.110287 +vt 0.893474 0.107327 +vt 0.895696 0.104791 +vt 0.890455 0.108366 +vt 0.892875 0.106068 +vt 0.895201 0.103582 +vt 0.206377 0.959971 +vt 0.207738 0.959719 +vt 0.208163 0.963727 +vt 0.209100 0.959467 +vt 0.209466 0.963358 +vt 0.206861 0.964097 +vt 0.205558 0.964466 +vt 0.205015 0.960223 +vt 0.204255 0.964835 +vt 0.203653 0.960476 +vt 0.205213 0.956169 +vt 0.206558 0.956016 +vt 0.205975 0.952394 +vt 0.203868 0.956323 +vt 0.204706 0.952441 +vt 0.207903 0.955863 +vt 0.209248 0.955710 +vt 0.207245 0.952347 +vt 0.208515 0.952300 +vt 0.209784 0.952254 +vt 0.216648 0.936881 +vt 0.214720 0.938948 +vt 0.214110 0.938195 +vt 0.216134 0.935980 +vt 0.213500 0.937441 +vt 0.212878 0.941153 +vt 0.212153 0.940556 +vt 0.211428 0.939960 +vt 0.218204 0.933863 +vt 0.218634 0.934906 +vt 0.220297 0.931794 +vt 0.220650 0.932976 +vt 0.215621 0.935078 +vt 0.217774 0.932819 +vt 0.219944 0.930613 +vt 0.219065 0.935949 +vt 0.217162 0.937783 +vt 0.219495 0.936992 +vt 0.221002 0.934157 +vt 0.221355 0.935339 +vt 0.215330 0.939702 +vt 0.213603 0.941749 +vt 0.217676 0.938685 +vt 0.215940 0.940456 +vt 0.214328 0.942345 +vt 0.209600 0.946164 +vt 0.210613 0.946418 +vt 0.209432 0.949174 +vt 0.208278 0.949082 +vt 0.211626 0.946672 +vt 0.210585 0.949267 +vt 0.207124 0.948989 +vt 0.208587 0.945910 +vt 0.205971 0.948896 +vt 0.207573 0.945656 +vt 0.210287 0.943114 +vt 0.211152 0.943539 +vt 0.209423 0.942688 +vt 0.212017 0.943965 +vt 0.212882 0.944391 +vt 0.763896 0.975275 +vt 0.760524 0.975275 +vt 0.760635 0.973007 +vt 0.757152 0.975275 +vt 0.763752 0.977426 +vt 0.760380 0.977426 +vt 0.757008 0.977426 +vt 0.764019 0.973016 +vt 0.760681 0.970507 +vt 0.764101 0.970541 +vt 0.760629 0.967657 +vt 0.757251 0.972999 +vt 0.757261 0.970474 +vt 0.757137 0.967574 +vt 0.767521 0.970574 +vt 0.767403 0.973024 +vt 0.770941 0.970608 +vt 0.770787 0.973033 +vt 0.764121 0.967741 +vt 0.767613 0.967825 +vt 0.771104 0.967908 +vt 0.767268 0.975275 +vt 0.767124 0.977426 +vt 0.770640 0.975275 +vt 0.770496 0.977426 +vt 0.894881 0.967560 +vt 0.894146 0.964800 +vt 0.896703 0.964725 +vt 0.893640 0.961812 +vt 0.896285 0.961697 +vt 0.892368 0.967615 +vt 0.891589 0.964875 +vt 0.890994 0.961927 +vt 0.897394 0.967504 +vt 0.899260 0.964650 +vt 0.901817 0.964575 +vt 0.902420 0.967393 +vt 0.898931 0.961583 +vt 0.901576 0.961468 +vt 0.899907 0.967449 +vt 0.900765 0.970069 +vt 0.898266 0.970118 +vt 0.901728 0.972600 +vt 0.899230 0.972649 +vt 0.903263 0.970020 +vt 0.904226 0.972551 +vt 0.895768 0.970167 +vt 0.893270 0.970216 +vt 0.896732 0.972697 +vt 0.894234 0.972746 +vt 0.763944 0.960988 +vt 0.764057 0.964509 +vt 0.760445 0.964342 +vt 0.760199 0.960711 +vt 0.756834 0.964174 +vt 0.759957 0.956917 +vt 0.763814 0.957322 +vt 0.759788 0.953111 +vt 0.763699 0.953655 +vt 0.756454 0.960434 +vt 0.756101 0.956512 +vt 0.755877 0.952568 +vt 0.767670 0.957727 +vt 0.771526 0.958132 +vt 0.771434 0.961542 +vt 0.767610 0.954198 +vt 0.771521 0.954742 +vt 0.767689 0.961265 +vt 0.767669 0.964676 +vt 0.771281 0.964844 +vt 0.896450 0.954731 +vt 0.893465 0.954997 +vt 0.893645 0.951322 +vt 0.896851 0.950958 +vt 0.893901 0.947571 +vt 0.890481 0.955262 +vt 0.890439 0.951686 +vt 0.890459 0.948040 +vt 0.900057 0.950594 +vt 0.899435 0.954465 +vt 0.903263 0.950230 +vt 0.902420 0.954200 +vt 0.897343 0.947102 +vt 0.900785 0.946633 +vt 0.904226 0.946164 +vt 0.899024 0.958158 +vt 0.901817 0.957978 +vt 0.896231 0.958338 +vt 0.893438 0.958519 +vt 0.890645 0.958699 +vt 0.763602 0.946710 +vt 0.763632 0.950129 +vt 0.759758 0.949444 +vt 0.759833 0.945881 +vt 0.755884 0.948759 +vt 0.759978 0.942387 +vt 0.763596 0.943361 +vt 0.760158 0.938928 +vt 0.763603 0.940048 +vt 0.756064 0.945052 +vt 0.756360 0.941412 +vt 0.756714 0.937807 +vt 0.767214 0.944336 +vt 0.767371 0.947538 +vt 0.770833 0.945310 +vt 0.767047 0.941169 +vt 0.770491 0.942289 +vt 0.767507 0.950814 +vt 0.771140 0.948367 +vt 0.771381 0.951499 +vt 0.750708 0.806628 +vt 0.751012 0.810501 +vt 0.748178 0.810000 +vt 0.747722 0.806286 +vt 0.745344 0.809500 +vt 0.751316 0.814374 +vt 0.748634 0.813715 +vt 0.745952 0.813056 +vt 0.747266 0.802571 +vt 0.750404 0.802755 +vt 0.746810 0.798856 +vt 0.750100 0.798882 +vt 0.744736 0.805943 +vt 0.744128 0.802387 +vt 0.743520 0.798830 +vt 0.753542 0.802940 +vt 0.753694 0.806971 +vt 0.756681 0.803124 +vt 0.753390 0.798908 +vt 0.756681 0.798935 +vt 0.753846 0.811002 +vt 0.753998 0.815033 +vt 0.756681 0.807313 +vt 0.756681 0.811502 +vt 0.756681 0.815692 +vt 0.911143 0.964159 +vt 0.907685 0.965129 +vt 0.907685 0.959776 +vt 0.904226 0.966098 +vt 0.904226 0.960745 +vt 0.911143 0.969512 +vt 0.907685 0.970481 +vt 0.904226 0.971450 +vt 0.911143 0.958807 +vt 0.907685 0.954423 +vt 0.911143 0.953454 +vt 0.907685 0.949071 +vt 0.904226 0.955393 +vt 0.904226 0.950040 +vt 0.914601 0.952485 +vt 0.914601 0.957838 +vt 0.918059 0.951516 +vt 0.918059 0.956869 +vt 0.911143 0.948102 +vt 0.914601 0.947133 +vt 0.918059 0.946164 +vt 0.914601 0.963190 +vt 0.914601 0.968543 +vt 0.918059 0.962221 +vt 0.918059 0.967574 +vt 0.875903 0.077827 +vt 0.878602 0.082866 +vt 0.875817 0.084475 +vt 0.881301 0.087905 +vt 0.878539 0.089669 +vt 0.878816 0.076258 +vt 0.881515 0.081296 +vt 0.884214 0.086335 +vt 0.873099 0.079315 +vt 0.873291 0.086164 +vt 0.870516 0.080640 +vt 0.876080 0.091825 +vt 0.867771 0.075389 +vt 0.870389 0.074224 +vt 0.865041 0.070275 +vt 0.867682 0.069166 +vt 0.873204 0.072789 +vt 0.876117 0.071219 +vt 0.870505 0.067750 +vt 0.873418 0.066181 +vt 0.159186 0.945605 +vt 0.164001 0.945616 +vt 0.163329 0.949610 +vt 0.158603 0.949070 +vt 0.162978 0.953906 +vt 0.168813 0.945620 +vt 0.168055 0.950150 +vt 0.167625 0.955049 +vt 0.153876 0.948530 +vt 0.154365 0.945578 +vt 0.149150 0.947990 +vt 0.149541 0.945543 +vt 0.158331 0.952763 +vt 0.153684 0.951619 +vt 0.149037 0.950476 +vt 0.155062 0.942839 +vt 0.159851 0.942492 +vt 0.155877 0.940388 +vt 0.150260 0.943154 +vt 0.151359 0.940841 +vt 0.164616 0.942083 +vt 0.169368 0.941641 +vt 0.160365 0.939856 +vt 0.164792 0.939166 +vt 0.169189 0.938397 +vt 0.159282 0.960435 +vt 0.160232 0.964363 +vt 0.155031 0.961202 +vt 0.154358 0.957971 +vt 0.149830 0.958040 +vt 0.161319 0.968317 +vt 0.155801 0.964448 +vt 0.150284 0.960580 +vt 0.153876 0.954771 +vt 0.158603 0.956560 +vt 0.149434 0.955506 +vt 0.149150 0.952983 +vt 0.163329 0.958348 +vt 0.164206 0.962900 +vt 0.168055 0.960136 +vt 0.165433 0.967524 +vt 0.166836 0.972185 +vt 0.169130 0.965364 +vt 0.170634 0.970685 +vt 0.172354 0.976054 +vt 0.162576 0.935593 +vt 0.164151 0.937023 +vt 0.160498 0.937819 +vt 0.165348 0.934427 +vt 0.167743 0.936069 +vt 0.156723 0.938299 +vt 0.157961 0.936475 +vt 0.152887 0.938621 +vt 0.154737 0.936471 +vt 0.160368 0.936601 +vt 0.159952 0.934817 +vt 0.162320 0.933244 +vt 0.156803 0.934369 +vt 0.158976 0.932290 +vt 0.428615 0.822586 +vt 0.428214 0.825514 +vt 0.427238 0.822657 +vt 0.428214 0.819700 +vt 0.426320 0.819381 +vt 0.427697 0.829278 +vt 0.426320 0.826026 +vt 0.424943 0.822774 +vt 0.429304 0.817567 +vt 0.429419 0.822545 +vt 0.430452 0.815847 +vt 0.430452 0.822492 +vt 0.427697 0.815988 +vt 0.429074 0.812595 +vt 0.430452 0.809202 +vt 0.429304 0.827535 +vt 0.429074 0.832530 +vt 0.430452 0.829137 +vt 0.430452 0.835782 +vt 0.277982 0.629149 +vt 0.278905 0.632767 +vt 0.276566 0.633616 +vt 0.275689 0.629746 +vt 0.274228 0.634465 +vt 0.280068 0.636338 +vt 0.277692 0.637470 +vt 0.275316 0.638602 +vt 0.275055 0.625898 +vt 0.277321 0.625515 +vt 0.274658 0.622106 +vt 0.276940 0.621897 +vt 0.273396 0.630343 +vt 0.272788 0.626280 +vt 0.272376 0.622316 +vt 0.279587 0.625133 +vt 0.280275 0.628552 +vt 0.281853 0.624751 +vt 0.279222 0.621687 +vt 0.281504 0.621478 +vt 0.281243 0.631918 +vt 0.282445 0.635207 +vt 0.282569 0.627955 +vt 0.283581 0.631069 +vt 0.284821 0.634075 +vt 0.678126 0.360321 +vt 0.676857 0.357832 +vt 0.680056 0.356143 +vt 0.681475 0.358441 +vt 0.682933 0.354191 +vt 0.675810 0.355689 +vt 0.678964 0.354157 +vt 0.681815 0.352302 +vt 0.683545 0.361363 +vt 0.679838 0.363503 +vt 0.684460 0.356364 +vt 0.686804 0.359103 +vt 0.675594 0.365687 +vt 0.674383 0.362050 +vt 0.671091 0.367396 +vt 0.673326 0.359279 +vt 0.672346 0.356940 +vt 0.670311 0.363499 +vt 0.669457 0.360501 +vt 0.668567 0.357953 +vt 0.284669 0.646662 +vt 0.282338 0.648770 +vt 0.280641 0.645034 +vt 0.280007 0.650878 +vt 0.278277 0.646801 +vt 0.286388 0.650039 +vt 0.284098 0.652493 +vt 0.281808 0.654948 +vt 0.283006 0.643266 +vt 0.279072 0.641272 +vt 0.281454 0.639832 +vt 0.276689 0.642712 +vt 0.283836 0.638392 +vt 0.285371 0.641499 +vt 0.286219 0.636952 +vt 0.287000 0.644555 +vt 0.288677 0.647584 +vt 0.287736 0.639731 +vt 0.289331 0.642447 +vt 0.290967 0.645130 +vt 0.661329 0.365289 +vt 0.660721 0.362292 +vt 0.665240 0.361519 +vt 0.665970 0.364541 +vt 0.660102 0.359363 +vt 0.664466 0.358769 +vt 0.666610 0.368110 +vt 0.661914 0.368423 +vt 0.667206 0.371952 +vt 0.656767 0.368928 +vt 0.656356 0.365855 +vt 0.651231 0.369284 +vt 0.651093 0.366116 +vt 0.662489 0.371625 +vt 0.657178 0.372000 +vt 0.651370 0.372451 +vt 0.655944 0.362782 +vt 0.655533 0.359709 +vt 0.650954 0.362949 +vt 0.650815 0.359782 +vt 0.656731 0.437857 +vt 0.654271 0.435367 +vt 0.659008 0.432162 +vt 0.653054 0.441391 +vt 0.650337 0.439175 +vt 0.661048 0.434710 +vt 0.664691 0.429647 +vt 0.665937 0.431865 +vt 0.670847 0.427477 +vt 0.667648 0.433683 +vt 0.662994 0.436741 +vt 0.669182 0.435451 +vt 0.664646 0.438518 +vt 0.671112 0.429171 +vt 0.672599 0.430640 +vt 0.675072 0.431552 +vt 0.658935 0.439832 +vt 0.655507 0.443146 +vt 0.660809 0.441519 +vt 0.657615 0.444658 +vt 0.640605 0.362171 +vt 0.645795 0.362755 +vt 0.645583 0.365953 +vt 0.641211 0.358998 +vt 0.646007 0.359556 +vt 0.639999 0.365343 +vt 0.645370 0.369152 +vt 0.645158 0.372350 +vt 0.638788 0.371689 +vt 0.639393 0.368516 +vt 0.633510 0.367361 +vt 0.634516 0.364265 +vt 0.627929 0.365670 +vt 0.632504 0.370456 +vt 0.626551 0.368643 +vt 0.635522 0.361169 +vt 0.636528 0.358073 +vt 0.629306 0.362696 +vt 0.630684 0.359722 +vt 0.632061 0.356748 +vt 0.277105 0.614390 +vt 0.276860 0.618325 +vt 0.274494 0.618409 +vt 0.274620 0.614354 +vt 0.272129 0.618494 +vt 0.275091 0.609485 +vt 0.277702 0.609681 +vt 0.275965 0.603351 +vt 0.272134 0.614317 +vt 0.272480 0.609290 +vt 0.273254 0.602914 +vt 0.280313 0.609876 +vt 0.279591 0.614426 +vt 0.282924 0.610072 +vt 0.282076 0.614462 +vt 0.278676 0.603788 +vt 0.281387 0.604225 +vt 0.284099 0.604662 +vt 0.279225 0.618241 +vt 0.281590 0.618156 +vt 0.687822 0.349516 +vt 0.689991 0.346933 +vt 0.691406 0.348674 +vt 0.692081 0.344279 +vt 0.693214 0.345738 +vt 0.686697 0.347592 +vt 0.688877 0.344917 +vt 0.690983 0.342152 +vt 0.689428 0.351484 +vt 0.693423 0.349865 +vt 0.694417 0.345862 +vt 0.691998 0.353540 +vt 0.689707 0.356557 +vt 0.687112 0.354046 +vt 0.685495 0.351959 +vt 0.684367 0.350084 +vt 0.281732 0.587620 +vt 0.280053 0.596302 +vt 0.277299 0.595498 +vt 0.278977 0.586355 +vt 0.274544 0.594693 +vt 0.280885 0.576352 +vt 0.283613 0.578142 +vt 0.282907 0.565920 +vt 0.285594 0.568266 +vt 0.276222 0.585089 +vt 0.278157 0.574563 +vt 0.280221 0.563575 +vt 0.286341 0.579932 +vt 0.284488 0.588886 +vt 0.289069 0.581722 +vt 0.288280 0.570611 +vt 0.290967 0.572956 +vt 0.282808 0.597106 +vt 0.287243 0.590152 +vt 0.285562 0.597910 +vt 0.668712 0.761877 +vt 0.665968 0.760527 +vt 0.670676 0.753875 +vt 0.673744 0.755300 +vt 0.674587 0.747191 +vt 0.663241 0.759240 +vt 0.667678 0.752701 +vt 0.671362 0.746166 +vt 0.676954 0.757226 +vt 0.671492 0.763353 +vt 0.680233 0.759404 +vt 0.674289 0.764891 +vt 0.677922 0.748563 +vt 0.681474 0.750631 +vt 0.665583 0.769912 +vt 0.663156 0.768535 +vt 0.659450 0.776687 +vt 0.668009 0.771289 +vt 0.661562 0.778141 +vt 0.660730 0.767159 +vt 0.658303 0.765782 +vt 0.657338 0.775233 +vt 0.655226 0.773780 +vt 0.653115 0.772326 +vt 0.741221 0.482103 +vt 0.742018 0.474636 +vt 0.743685 0.475095 +vt 0.743429 0.483579 +vt 0.742474 0.467093 +vt 0.743362 0.466346 +vt 0.742015 0.491533 +vt 0.739740 0.489418 +vt 0.739537 0.499338 +vt 0.737455 0.496804 +vt 0.737628 0.488009 +vt 0.739002 0.480909 +vt 0.735725 0.487137 +vt 0.735487 0.495058 +vt 0.733684 0.493901 +vt 0.739722 0.473408 +vt 0.740116 0.465705 +vt 0.736927 0.480156 +vt 0.737304 0.472459 +vt 0.736736 0.462303 +vt 0.682942 0.734003 +vt 0.679408 0.733581 +vt 0.680678 0.726425 +vt 0.684216 0.726392 +vt 0.681428 0.718882 +vt 0.675913 0.733008 +vt 0.677164 0.726173 +vt 0.677990 0.719027 +vt 0.687804 0.725789 +vt 0.686553 0.734123 +vt 0.684893 0.718411 +vt 0.688411 0.717292 +vt 0.684558 0.742667 +vt 0.680916 0.741429 +vt 0.677438 0.740464 +vt 0.674043 0.739637 +vt 0.730239 0.512013 +vt 0.734247 0.504483 +vt 0.736090 0.507371 +vt 0.731848 0.515164 +vt 0.726988 0.522248 +vt 0.725558 0.518948 +vt 0.721687 0.528151 +vt 0.720492 0.524695 +vt 0.723745 0.516667 +vt 0.728552 0.509725 +vt 0.722098 0.515048 +vt 0.718691 0.522303 +vt 0.715227 0.521885 +vt 0.732464 0.502409 +vt 0.726958 0.508068 +vt 0.730791 0.500948 +vt 0.684849 0.701295 +vt 0.681801 0.702757 +vt 0.681205 0.695114 +vt 0.683994 0.693162 +vt 0.679941 0.688376 +vt 0.678781 0.703907 +vt 0.678434 0.696780 +vt 0.677384 0.690507 +vt 0.686823 0.690641 +vt 0.687955 0.699207 +vt 0.682495 0.685984 +vt 0.685039 0.683073 +vt 0.688473 0.708261 +vt 0.685126 0.709878 +vt 0.681839 0.710836 +vt 0.678581 0.711464 +vt 0.710041 0.531255 +vt 0.715331 0.528661 +vt 0.716121 0.532404 +vt 0.710243 0.535411 +vt 0.704007 0.537575 +vt 0.704587 0.532885 +vt 0.698224 0.538504 +vt 0.699320 0.533584 +vt 0.704498 0.528861 +vt 0.709432 0.527628 +vt 0.704075 0.525170 +vt 0.699555 0.529451 +vt 0.699361 0.525712 +vt 0.714042 0.525699 +vt 0.708620 0.524266 +vt 0.712502 0.523128 +vt 0.677522 0.675690 +vt 0.675279 0.678678 +vt 0.672268 0.675031 +vt 0.674371 0.671960 +vt 0.669063 0.671728 +vt 0.672914 0.681476 +vt 0.669926 0.678078 +vt 0.666723 0.675000 +vt 0.675999 0.668839 +vt 0.679522 0.672321 +vt 0.671026 0.668651 +vt 0.672238 0.665964 +vt 0.682571 0.677016 +vt 0.680283 0.680262 +vt 0.677901 0.683011 +vt 0.675472 0.685512 +vt 0.694589 0.533381 +vt 0.693705 0.537810 +vt 0.689551 0.535848 +vt 0.692543 0.542488 +vt 0.688954 0.539649 +vt 0.690079 0.532108 +vt 0.684865 0.532972 +vt 0.685473 0.529597 +vt 0.679798 0.528628 +vt 0.684256 0.536346 +vt 0.678880 0.531802 +vt 0.686081 0.526223 +vt 0.690469 0.528493 +vt 0.686689 0.522849 +vt 0.690789 0.524940 +vt 0.680716 0.525453 +vt 0.681634 0.522279 +vt 0.682552 0.519104 +vt 0.694920 0.529449 +vt 0.694973 0.525765 +vt 0.563545 0.678274 +vt 0.564191 0.673341 +vt 0.566256 0.674923 +vt 0.564292 0.667406 +vt 0.566537 0.668648 +vt 0.561575 0.676348 +vt 0.562126 0.671759 +vt 0.562047 0.666165 +vt 0.565514 0.680200 +vt 0.568321 0.676504 +vt 0.567484 0.682126 +vt 0.570387 0.678086 +vt 0.568783 0.669890 +vt 0.571028 0.671131 +vt 0.566395 0.687085 +vt 0.564465 0.684811 +vt 0.565180 0.691713 +vt 0.569453 0.684052 +vt 0.568325 0.689358 +vt 0.567099 0.694335 +vt 0.562535 0.682538 +vt 0.560605 0.680265 +vt 0.563261 0.689090 +vt 0.561342 0.686468 +vt 0.559424 0.683846 +vt 0.675755 0.519280 +vt 0.674504 0.522261 +vt 0.669169 0.514656 +vt 0.673253 0.525242 +vt 0.667583 0.517430 +vt 0.670755 0.511881 +vt 0.663978 0.506598 +vt 0.665881 0.504061 +vt 0.659118 0.498873 +vt 0.662075 0.509136 +vt 0.656936 0.501125 +vt 0.667784 0.501523 +vt 0.672340 0.509106 +vt 0.669687 0.498985 +vt 0.673926 0.506332 +vt 0.661300 0.496622 +vt 0.663482 0.494370 +vt 0.665664 0.492118 +vt 0.677005 0.516299 +vt 0.678256 0.513318 +vt 0.562707 0.652152 +vt 0.561813 0.644083 +vt 0.564932 0.644305 +vt 0.561378 0.636552 +vt 0.564614 0.636405 +vt 0.559849 0.651582 +vt 0.558694 0.643861 +vt 0.558142 0.636699 +vt 0.565565 0.652723 +vt 0.568050 0.644527 +vt 0.568422 0.653293 +vt 0.571169 0.644749 +vt 0.567850 0.636258 +vt 0.571086 0.636111 +vt 0.568741 0.661950 +vt 0.566203 0.661043 +vt 0.571280 0.653864 +vt 0.571279 0.662858 +vt 0.563665 0.660135 +vt 0.561127 0.659227 +vt 0.653521 0.485078 +vt 0.657176 0.490368 +vt 0.654773 0.492266 +vt 0.650732 0.486628 +vt 0.652370 0.494164 +vt 0.646782 0.481811 +vt 0.650347 0.480528 +vt 0.643589 0.477213 +vt 0.647817 0.476271 +vt 0.647890 0.488230 +vt 0.643006 0.483302 +vt 0.653488 0.479660 +vt 0.656204 0.483631 +vt 0.656417 0.479000 +vt 0.651289 0.475917 +vt 0.654925 0.476752 +vt 0.659579 0.488470 +vt 0.658834 0.482236 +vt 0.661981 0.486572 +vt 0.565700 0.623725 +vt 0.562862 0.624691 +vt 0.564352 0.619778 +vt 0.566791 0.618377 +vt 0.566056 0.615156 +vt 0.560023 0.625656 +vt 0.561913 0.621178 +vt 0.564074 0.616998 +vt 0.569230 0.616977 +vt 0.568539 0.622760 +vt 0.571669 0.615577 +vt 0.571378 0.621794 +vt 0.568038 0.613313 +vt 0.570020 0.611471 +vt 0.572002 0.609629 +vt 0.568046 0.629095 +vt 0.564922 0.629640 +vt 0.571169 0.628549 +vt 0.561799 0.630185 +vt 0.558676 0.630731 +vt 0.645118 0.467421 +vt 0.646094 0.471859 +vt 0.641820 0.472230 +vt 0.641057 0.467209 +vt 0.640885 0.462494 +vt 0.644828 0.463083 +vt 0.641367 0.457842 +vt 0.645190 0.458724 +vt 0.647880 0.463351 +vt 0.648252 0.467477 +vt 0.650282 0.463492 +vt 0.648215 0.459254 +vt 0.650631 0.459631 +vt 0.649466 0.471761 +vt 0.650649 0.467457 +vt 0.651901 0.471680 +vt 0.608795 0.346476 +vt 0.611284 0.344681 +vt 0.613256 0.347486 +vt 0.610856 0.349484 +vt 0.615670 0.350251 +vt 0.613773 0.342885 +vt 0.615657 0.345488 +vt 0.617954 0.348045 +vt 0.608455 0.351483 +vt 0.606305 0.348272 +vt 0.606054 0.353481 +vt 0.603816 0.350067 +vt 0.613385 0.352456 +vt 0.611100 0.354661 +vt 0.608816 0.356866 +vt 0.604487 0.345039 +vt 0.607046 0.343444 +vt 0.602834 0.341796 +vt 0.601929 0.346634 +vt 0.600216 0.343192 +vt 0.609605 0.341849 +vt 0.612163 0.340254 +vt 0.605453 0.340400 +vt 0.608072 0.339003 +vt 0.610691 0.337607 +vt 0.647796 0.449727 +vt 0.646172 0.454220 +vt 0.642567 0.453009 +vt 0.644471 0.448171 +vt 0.647066 0.443502 +vt 0.650082 0.445399 +vt 0.652744 0.446874 +vt 0.650624 0.450887 +vt 0.655011 0.448135 +vt 0.649122 0.455056 +vt 0.652986 0.451855 +vt 0.651529 0.455719 +vt 0.620274 0.358137 +vt 0.622207 0.355513 +vt 0.626228 0.357802 +vt 0.624544 0.360615 +vt 0.624140 0.352890 +vt 0.627913 0.354989 +vt 0.622860 0.363427 +vt 0.618340 0.360760 +vt 0.621175 0.366240 +vt 0.614408 0.357796 +vt 0.616539 0.355379 +vt 0.616407 0.363383 +vt 0.612277 0.360213 +vt 0.618670 0.352961 +vt 0.620801 0.350544 +vt 0.656515 0.453831 +vt 0.655166 0.457128 +vt 0.653508 0.456412 +vt 0.654913 0.452838 +vt 0.654309 0.460511 +vt 0.652627 0.460054 +vt 0.656844 0.449392 +vt 0.658350 0.450646 +vt 0.659298 0.446140 +vt 0.660662 0.447600 +vt 0.659639 0.451897 +vt 0.660820 0.453147 +vt 0.659177 0.455834 +vt 0.661812 0.449046 +vt 0.662856 0.450484 +vt 0.657900 0.454831 +vt 0.656609 0.457859 +vt 0.655781 0.460992 +vt 0.657946 0.458596 +vt 0.657148 0.461484 +vt 0.967903 0.851542 +vt 0.969364 0.850920 +vt 0.970941 0.854450 +vt 0.969417 0.855308 +vt 0.972793 0.857969 +vt 0.970824 0.850297 +vt 0.972465 0.853592 +vt 0.974338 0.856924 +vt 0.967893 0.856165 +vt 0.966442 0.852164 +vt 0.966369 0.857023 +vt 0.964982 0.852786 +vt 0.971248 0.859014 +vt 0.969703 0.860059 +vt 0.968159 0.861105 +vt 0.965391 0.848136 +vt 0.966769 0.847765 +vt 0.964775 0.844164 +vt 0.964012 0.848507 +vt 0.963473 0.844303 +vt 0.968148 0.847394 +vt 0.969527 0.847022 +vt 0.966077 0.844025 +vt 0.967379 0.843887 +vt 0.968681 0.843748 +vt 0.654022 0.467463 +vt 0.654435 0.471048 +vt 0.653366 0.471363 +vt 0.652499 0.467437 +vt 0.655020 0.474671 +vt 0.654553 0.475382 +vt 0.652272 0.463698 +vt 0.653954 0.463953 +vt 0.655430 0.464242 +vt 0.655437 0.467579 +vt 0.656804 0.464547 +vt 0.655683 0.470975 +vt 0.656047 0.474400 +vt 0.656799 0.467740 +vt 0.657020 0.471022 +vt 0.657355 0.474347 +vt 0.966080 0.836769 +vt 0.967311 0.836945 +vt 0.967142 0.840414 +vt 0.965887 0.840372 +vt 0.968542 0.837121 +vt 0.968396 0.840456 +vt 0.964633 0.840329 +vt 0.964848 0.836594 +vt 0.963378 0.840287 +vt 0.963617 0.836418 +vt 0.965304 0.832920 +vt 0.966533 0.833185 +vt 0.965885 0.829270 +vt 0.964076 0.832654 +vt 0.964645 0.828955 +vt 0.967761 0.833450 +vt 0.968989 0.833716 +vt 0.967125 0.829585 +vt 0.968366 0.829899 +vt 0.969606 0.830214 +vt 0.565381 0.705188 +vt 0.563252 0.710247 +vt 0.561968 0.709737 +vt 0.564270 0.704499 +vt 0.560684 0.709227 +vt 0.561641 0.716114 +vt 0.560194 0.715788 +vt 0.558747 0.715463 +vt 0.566924 0.699802 +vt 0.567856 0.700669 +vt 0.569754 0.695377 +vt 0.570504 0.696419 +vt 0.563159 0.703809 +vt 0.565992 0.698936 +vt 0.569005 0.694335 +vt 0.568788 0.701536 +vt 0.566492 0.705878 +vt 0.569719 0.702403 +vt 0.571253 0.697461 +vt 0.572002 0.698504 +vt 0.564535 0.710757 +vt 0.563087 0.716439 +vt 0.567603 0.706568 +vt 0.565819 0.711266 +vt 0.564534 0.716764 +vt 0.968670 0.822053 +vt 0.969959 0.822392 +vt 0.969000 0.826262 +vt 0.967737 0.825935 +vt 0.971248 0.822732 +vt 0.970263 0.826590 +vt 0.966474 0.825607 +vt 0.967381 0.821713 +vt 0.965211 0.825279 +vt 0.966092 0.821373 +vt 0.968918 0.817369 +vt 0.971394 0.812359 +vt 0.972718 0.812865 +vt 0.967606 0.816982 +vt 0.970070 0.811854 +vt 0.970229 0.817757 +vt 0.971541 0.818144 +vt 0.972852 0.818531 +vt 0.974041 0.813370 +vt 0.975365 0.813876 +vt 0.560375 0.737816 +vt 0.558544 0.738044 +vt 0.558620 0.730543 +vt 0.556713 0.738272 +vt 0.556892 0.730595 +vt 0.560659 0.744446 +vt 0.558758 0.744828 +vt 0.556857 0.745209 +vt 0.560347 0.730490 +vt 0.559124 0.722924 +vt 0.560721 0.723058 +vt 0.557527 0.722789 +vt 0.562318 0.723193 +vt 0.562074 0.730437 +vt 0.563915 0.723328 +vt 0.563802 0.730384 +vt 0.562206 0.737588 +vt 0.562560 0.744065 +vt 0.564037 0.737360 +vt 0.564461 0.743683 +vt 0.982383 0.801976 +vt 0.977756 0.807925 +vt 0.976439 0.807195 +vt 0.983681 0.803011 +vt 0.979074 0.808655 +vt 0.981084 0.800941 +vt 0.975121 0.806465 +vt 0.979786 0.799907 +vt 0.973803 0.805734 +vt 0.985076 0.792906 +vt 0.986347 0.794299 +vt 0.990679 0.785685 +vt 0.991917 0.787462 +vt 0.978488 0.798872 +vt 0.983806 0.791514 +vt 0.989441 0.783908 +vt 0.987617 0.795692 +vt 0.988887 0.797085 +vt 0.993155 0.789240 +vt 0.994393 0.791017 +vt 0.562374 0.757783 +vt 0.560506 0.758517 +vt 0.559692 0.754751 +vt 0.558638 0.759252 +vt 0.557773 0.755367 +vt 0.563393 0.761023 +vt 0.561616 0.761903 +vt 0.559838 0.762784 +vt 0.561610 0.754136 +vt 0.559125 0.750292 +vt 0.561055 0.749788 +vt 0.557195 0.750797 +vt 0.562985 0.749284 +vt 0.563529 0.753520 +vt 0.564915 0.748779 +vt 0.565448 0.752904 +vt 0.564242 0.757048 +vt 0.565171 0.760143 +vt 0.566110 0.756314 +vt 0.566949 0.759262 +vt 0.288023 0.553523 +vt 0.290166 0.556083 +vt 0.288595 0.557661 +vt 0.292361 0.558528 +vt 0.290835 0.560210 +vt 0.289642 0.552064 +vt 0.291738 0.554506 +vt 0.293886 0.556846 +vt 0.286403 0.554983 +vt 0.287024 0.559239 +vt 0.284784 0.556442 +vt 0.285452 0.560816 +vt 0.289310 0.561893 +vt 0.287785 0.563575 +vt 0.282637 0.553362 +vt 0.284309 0.552047 +vt 0.280630 0.549856 +vt 0.283164 0.557902 +vt 0.280965 0.554677 +vt 0.278898 0.550986 +vt 0.285982 0.550732 +vt 0.287654 0.549418 +vt 0.282362 0.548726 +vt 0.284094 0.547596 +vt 0.285826 0.546466 +vt 0.566268 0.767192 +vt 0.567972 0.770178 +vt 0.566659 0.771725 +vt 0.564778 0.768491 +vt 0.565346 0.773273 +vt 0.569752 0.773136 +vt 0.568626 0.774943 +vt 0.567501 0.776751 +vt 0.563068 0.765222 +vt 0.564717 0.764150 +vt 0.563288 0.769789 +vt 0.561420 0.766294 +vt 0.566365 0.763078 +vt 0.567759 0.765894 +vt 0.568014 0.762006 +vt 0.569285 0.768631 +vt 0.570877 0.771328 +vt 0.569249 0.764596 +vt 0.570597 0.767084 +vt 0.572002 0.769520 +vt 0.279166 0.540493 +vt 0.281029 0.539866 +vt 0.282411 0.543997 +vt 0.280610 0.544889 +vt 0.282892 0.539238 +vt 0.284211 0.543106 +vt 0.278810 0.545781 +vt 0.277304 0.541120 +vt 0.277009 0.546673 +vt 0.275441 0.541748 +vt 0.276239 0.535857 +vt 0.278143 0.535493 +vt 0.275745 0.529972 +vt 0.274336 0.536221 +vt 0.273836 0.530101 +vt 0.280047 0.535129 +vt 0.281951 0.534765 +vt 0.277653 0.529844 +vt 0.279561 0.529715 +vt 0.281469 0.529587 +vt 0.724404 0.505847 +vt 0.727884 0.499162 +vt 0.729278 0.499900 +vt 0.725629 0.506810 +vt 0.730675 0.492655 +vt 0.732098 0.493132 +vt 0.721167 0.513734 +vt 0.720400 0.512630 +vt 0.716299 0.520665 +vt 0.716133 0.519463 +vt 0.719242 0.511642 +vt 0.723125 0.505074 +vt 0.717889 0.510711 +vt 0.715141 0.518272 +vt 0.713736 0.517086 +vt 0.726571 0.498630 +vt 0.729361 0.492371 +vt 0.721818 0.504396 +vt 0.725297 0.498201 +vt 0.728100 0.492185 +vt 0.278554 0.516832 +vt 0.280329 0.517030 +vt 0.279669 0.523552 +vt 0.277808 0.523501 +vt 0.282103 0.517227 +vt 0.281530 0.523604 +vt 0.275947 0.523449 +vt 0.276780 0.516635 +vt 0.274086 0.523398 +vt 0.275006 0.516438 +vt 0.278177 0.509878 +vt 0.279838 0.510207 +vt 0.280073 0.503525 +vt 0.276517 0.509550 +vt 0.278541 0.503059 +vt 0.281499 0.510535 +vt 0.283159 0.510864 +vt 0.281605 0.503992 +vt 0.283137 0.504458 +vt 0.284669 0.504925 +vt 0.733562 0.473749 +vt 0.735269 0.472839 +vt 0.735150 0.480002 +vt 0.732316 0.467089 +vt 0.734070 0.465586 +vt 0.733605 0.480180 +vt 0.734074 0.486633 +vt 0.732612 0.486405 +vt 0.731277 0.486361 +vt 0.732229 0.480424 +vt 0.730004 0.486409 +vt 0.730938 0.480701 +vt 0.732129 0.474387 +vt 0.730888 0.468075 +vt 0.730833 0.474890 +vt 0.729621 0.468804 +vt 0.287585 0.494521 +vt 0.285201 0.499187 +vt 0.283800 0.498556 +vt 0.288854 0.495339 +vt 0.286602 0.499819 +vt 0.286317 0.493704 +vt 0.282399 0.497924 +vt 0.280998 0.497292 +vt 0.283781 0.492069 +vt 0.285049 0.492886 +vt 0.287915 0.488225 +vt 0.289049 0.489242 +vt 0.290888 0.483751 +vt 0.291888 0.484974 +vt 0.286781 0.487208 +vt 0.289889 0.482528 +vt 0.290183 0.490258 +vt 0.291317 0.491275 +vt 0.292887 0.486197 +vt 0.293886 0.487420 +vt 0.722212 0.447091 +vt 0.723162 0.445731 +vt 0.727325 0.452083 +vt 0.724112 0.444371 +vt 0.728498 0.450731 +vt 0.718170 0.441844 +vt 0.719017 0.440339 +vt 0.719864 0.438834 +vt 0.726179 0.453389 +vt 0.731192 0.458684 +vt 0.729701 0.460179 +vt 0.732785 0.457010 +vt 0.728416 0.461315 +vt 0.725084 0.454607 +vt 0.727233 0.462272 +vt 0.724014 0.455779 +vt 0.721262 0.448451 +vt 0.717322 0.443349 +vt 0.720312 0.449811 +vt 0.716475 0.444854 +vt 0.143972 0.882178 +vt 0.145533 0.889314 +vt 0.144234 0.889979 +vt 0.147262 0.896690 +vt 0.146060 0.897609 +vt 0.145364 0.881751 +vt 0.146833 0.888649 +vt 0.148464 0.895770 +vt 0.142581 0.882605 +vt 0.142934 0.890645 +vt 0.141190 0.883033 +vt 0.141634 0.891310 +vt 0.144858 0.898529 +vt 0.143656 0.899448 +vt 0.139806 0.875964 +vt 0.141276 0.875743 +vt 0.138961 0.869710 +vt 0.140493 0.869648 +vt 0.139799 0.883460 +vt 0.138335 0.876185 +vt 0.137430 0.869772 +vt 0.142747 0.875522 +vt 0.144217 0.875301 +vt 0.142024 0.869585 +vt 0.143556 0.869523 +vt 0.710877 0.435859 +vt 0.707456 0.434465 +vt 0.708167 0.432782 +vt 0.708879 0.431099 +vt 0.712371 0.432572 +vt 0.704070 0.433697 +vt 0.704731 0.431965 +vt 0.705392 0.430232 +vt 0.711624 0.434216 +vt 0.715206 0.436618 +vt 0.714420 0.438210 +vt 0.715992 0.435026 +vt 0.713634 0.439802 +vt 0.710130 0.437503 +vt 0.712848 0.441394 +vt 0.709384 0.439147 +vt 0.706744 0.436148 +vt 0.703409 0.435430 +vt 0.706033 0.437831 +vt 0.702748 0.437162 +vt 0.142400 0.860390 +vt 0.141972 0.864609 +vt 0.140404 0.864576 +vt 0.143993 0.860482 +vt 0.143540 0.864642 +vt 0.140808 0.860299 +vt 0.138836 0.864543 +vt 0.139216 0.860207 +vt 0.137268 0.864510 +vt 0.139886 0.856446 +vt 0.141502 0.856587 +vt 0.140632 0.853005 +vt 0.137624 0.860115 +vt 0.138271 0.856305 +vt 0.138981 0.852797 +vt 0.143118 0.856728 +vt 0.144733 0.856869 +vt 0.142282 0.853213 +vt 0.143933 0.853421 +vt 0.145583 0.853629 +vt 0.666647 0.442038 +vt 0.663442 0.444720 +vt 0.662277 0.443143 +vt 0.665802 0.440304 +vt 0.669895 0.437522 +vt 0.670232 0.439584 +vt 0.674125 0.435232 +vt 0.674040 0.437522 +vt 0.670639 0.441323 +vt 0.667363 0.443659 +vt 0.671080 0.442901 +vt 0.674179 0.439360 +vt 0.674429 0.440971 +vt 0.664404 0.446266 +vt 0.668016 0.445222 +vt 0.665264 0.447796 +vt 0.975641 0.866031 +vt 0.977056 0.864823 +vt 0.979441 0.867942 +vt 0.978122 0.869190 +vt 0.981977 0.870712 +vt 0.978471 0.863616 +vt 0.980759 0.866695 +vt 0.983214 0.869409 +vt 0.976804 0.870437 +vt 0.974226 0.867238 +vt 0.975486 0.871684 +vt 0.972811 0.868445 +vt 0.980740 0.872015 +vt 0.979503 0.873318 +vt 0.978266 0.874621 +vt 0.971837 0.863765 +vt 0.973337 0.862614 +vt 0.970338 0.864915 +vt 0.974836 0.861463 +vt 0.976336 0.860312 +vt 0.681847 0.434919 +vt 0.677916 0.436018 +vt 0.678063 0.433869 +vt 0.681932 0.432990 +vt 0.678292 0.431572 +vt 0.685955 0.432153 +vt 0.685824 0.434076 +vt 0.689980 0.431498 +vt 0.689743 0.433483 +vt 0.682038 0.431023 +vt 0.686087 0.430229 +vt 0.690217 0.429513 +vt 0.685693 0.436000 +vt 0.681803 0.436774 +vt 0.685561 0.437923 +vt 0.689505 0.435469 +vt 0.689268 0.437454 +vt 0.677933 0.437869 +vt 0.681779 0.438592 +vt 0.678032 0.439571 +vt 0.986239 0.876543 +vt 0.987431 0.874995 +vt 0.990279 0.876737 +vt 0.989074 0.878449 +vt 0.993162 0.878365 +vt 0.988622 0.873448 +vt 0.991485 0.875025 +vt 0.994393 0.876476 +vt 0.987868 0.880162 +vt 0.985048 0.878091 +vt 0.986662 0.881874 +vt 0.983857 0.879638 +vt 0.991932 0.880254 +vt 0.990701 0.882143 +vt 0.989470 0.884032 +vt 0.982255 0.875839 +vt 0.983454 0.874432 +vt 0.981057 0.877247 +vt 0.984652 0.873025 +vt 0.985850 0.871618 +vt 0.697119 0.433046 +vt 0.693498 0.433136 +vt 0.693853 0.431166 +vt 0.697591 0.431143 +vt 0.694207 0.429197 +vt 0.701212 0.431414 +vt 0.700634 0.433229 +vt 0.698063 0.429239 +vt 0.701789 0.429598 +vt 0.700056 0.435044 +vt 0.696648 0.434950 +vt 0.699479 0.436860 +vt 0.693144 0.435105 +vt 0.696176 0.436853 +vt 0.692790 0.437074 +vt 0.143525 0.846762 +vt 0.145309 0.847227 +vt 0.144656 0.850266 +vt 0.142947 0.849946 +vt 0.147093 0.847691 +vt 0.146364 0.850586 +vt 0.141239 0.849627 +vt 0.141741 0.846297 +vt 0.139530 0.849307 +vt 0.139957 0.845832 +vt 0.142174 0.842999 +vt 0.144045 0.843631 +vt 0.142572 0.839716 +vt 0.140303 0.842366 +vt 0.140609 0.838905 +vt 0.145916 0.844263 +vt 0.147787 0.844896 +vt 0.144536 0.840527 +vt 0.146500 0.841338 +vt 0.148464 0.842149 +vt 0.041922 0.912874 +vt 0.034456 0.912547 +vt 0.034031 0.906226 +vt 0.042450 0.918906 +vt 0.034960 0.918394 +vt 0.041474 0.906391 +vt 0.033764 0.898955 +vt 0.041185 0.899010 +vt 0.033734 0.890262 +vt 0.048834 0.899371 +vt 0.049183 0.906940 +vt 0.056599 0.899885 +vt 0.057026 0.907681 +vt 0.041135 0.890278 +vt 0.048732 0.890564 +vt 0.056427 0.890984 +vt 0.049696 0.913685 +vt 0.050292 0.920017 +vt 0.057625 0.914739 +vt 0.058309 0.921428 +vt 0.512706 0.838118 +vt 0.512193 0.845651 +vt 0.503649 0.845670 +vt 0.511015 0.853975 +vt 0.502275 0.853765 +vt 0.521020 0.837718 +vt 0.520736 0.845632 +vt 0.519756 0.854184 +vt 0.504391 0.838518 +vt 0.495106 0.845689 +vt 0.496076 0.838918 +vt 0.486562 0.845708 +vt 0.493534 0.853556 +vt 0.484793 0.853347 +vt 0.496645 0.832878 +vt 0.504710 0.831995 +vt 0.497013 0.827203 +vt 0.487761 0.839318 +vt 0.488580 0.833761 +vt 0.489208 0.828620 +vt 0.512776 0.831112 +vt 0.520841 0.830229 +vt 0.504819 0.825787 +vt 0.512625 0.824370 +vt 0.520430 0.822953 +vt 0.041405 0.879748 +vt 0.034020 0.879670 +vt 0.034544 0.867656 +vt 0.041914 0.867868 +vt 0.035225 0.854693 +vt 0.042583 0.855089 +vt 0.035986 0.841255 +vt 0.050071 0.855894 +vt 0.049433 0.868413 +vt 0.057625 0.856904 +vt 0.057026 0.869124 +vt 0.043331 0.841860 +vt 0.050792 0.842963 +vt 0.058309 0.844316 +vt 0.048959 0.880107 +vt 0.056599 0.880607 +vt 0.506645 0.873348 +vt 0.504733 0.883519 +vt 0.495637 0.883676 +vt 0.503859 0.893428 +vt 0.515658 0.873446 +vt 0.513829 0.883362 +vt 0.513004 0.892939 +vt 0.497631 0.873250 +vt 0.486542 0.883833 +vt 0.488617 0.873152 +vt 0.477446 0.883991 +vt 0.494713 0.893918 +vt 0.485567 0.894407 +vt 0.476422 0.894896 +vt 0.491159 0.862885 +vt 0.500056 0.863119 +vt 0.479604 0.873054 +vt 0.482263 0.862650 +vt 0.508952 0.863353 +vt 0.517849 0.863588 +vt 0.835654 0.234412 +vt 0.830623 0.241902 +vt 0.825197 0.236393 +vt 0.825591 0.249392 +vt 0.819919 0.243795 +vt 0.840919 0.239364 +vt 0.836143 0.246917 +vt 0.831366 0.254469 +vt 0.830475 0.228992 +vt 0.819959 0.229896 +vt 0.825467 0.222636 +vt 0.815004 0.221914 +vt 0.814452 0.237155 +vt 0.809291 0.228952 +vt 0.830974 0.215377 +vt 0.835752 0.221591 +vt 0.836482 0.208117 +vt 0.841030 0.214189 +vt 0.820717 0.214877 +vt 0.826429 0.207839 +vt 0.832142 0.200801 +vt 0.840685 0.226922 +vt 0.845696 0.231812 +vt 0.845717 0.219432 +vt 0.850473 0.224260 +vt 0.506724 0.911320 +vt 0.509625 0.919653 +vt 0.500474 0.921310 +vt 0.512945 0.927811 +vt 0.503816 0.929885 +vt 0.515890 0.910073 +vt 0.518776 0.917996 +vt 0.522074 0.925736 +vt 0.497558 0.912568 +vt 0.491323 0.922967 +vt 0.488392 0.913815 +vt 0.482171 0.924624 +vt 0.494687 0.931960 +vt 0.485558 0.934035 +vt 0.486329 0.904348 +vt 0.495495 0.903493 +vt 0.479226 0.915063 +vt 0.477162 0.905203 +vt 0.504662 0.902637 +vt 0.513829 0.901782 +vt 0.816310 0.205246 +vt 0.810425 0.211955 +vt 0.806046 0.200464 +vt 0.804540 0.218665 +vt 0.800008 0.206774 +vt 0.812173 0.194222 +vt 0.801693 0.187885 +vt 0.808230 0.182283 +vt 0.797190 0.174664 +vt 0.795506 0.193758 +vt 0.790844 0.180099 +vt 0.815465 0.177222 +vt 0.818473 0.188115 +vt 0.823049 0.172432 +vt 0.804076 0.169639 +vt 0.812040 0.165433 +vt 0.823209 0.163776 +vt 0.822195 0.198536 +vt 0.824861 0.182076 +vt 0.828080 0.191827 +vt 0.180934 0.440591 +vt 0.170592 0.440065 +vt 0.172205 0.433774 +vt 0.159411 0.440115 +vt 0.159478 0.434011 +vt 0.177071 0.447186 +vt 0.169452 0.446148 +vt 0.166162 0.444394 +vt 0.184627 0.433908 +vt 0.174763 0.427066 +vt 0.187979 0.427052 +vt 0.161379 0.427371 +vt 0.201084 0.427232 +vt 0.197337 0.434160 +vt 0.214133 0.427509 +vt 0.210191 0.434471 +vt 0.192684 0.440981 +vt 0.187577 0.447747 +vt 0.205137 0.441303 +vt 0.199527 0.448069 +vt 0.799302 0.156502 +vt 0.792361 0.161247 +vt 0.787366 0.148043 +vt 0.785833 0.166275 +vt 0.780634 0.152690 +vt 0.794305 0.143483 +vt 0.782367 0.135463 +vt 0.789482 0.131192 +vt 0.777524 0.123918 +vt 0.775407 0.139745 +vt 0.770313 0.127845 +vt 0.796906 0.126944 +vt 0.801654 0.139097 +vt 0.804793 0.122731 +vt 0.809284 0.134756 +vt 0.784901 0.119996 +vt 0.792611 0.116082 +vt 0.800821 0.112180 +vt 0.807068 0.152326 +vt 0.814729 0.148135 +vt 0.130361 0.443286 +vt 0.117634 0.444830 +vt 0.119717 0.437270 +vt 0.106667 0.446065 +vt 0.108394 0.438397 +vt 0.128359 0.450954 +vt 0.115605 0.452979 +vt 0.105016 0.454406 +vt 0.132282 0.436059 +vt 0.121650 0.430113 +vt 0.134381 0.429021 +vt 0.110020 0.431216 +vt 0.147771 0.428065 +vt 0.145853 0.434873 +vt 0.145144 0.441452 +vt 0.143950 0.448306 +vt 0.780628 0.110258 +vt 0.772996 0.113819 +vt 0.769474 0.104482 +vt 0.765514 0.117390 +vt 0.761772 0.107675 +vt 0.777294 0.101307 +vt 0.767645 0.095226 +vt 0.775529 0.092467 +vt 0.768197 0.085369 +vt 0.759851 0.097990 +vt 0.760513 0.087630 +vt 0.783594 0.089718 +vt 0.785349 0.098167 +vt 0.791930 0.086984 +vt 0.793759 0.095080 +vt 0.775964 0.083065 +vt 0.783896 0.080676 +vt 0.792076 0.078159 +vt 0.788557 0.106722 +vt 0.796933 0.103220 +vt 0.088404 0.447430 +vt 0.079647 0.447316 +vt 0.081451 0.440619 +vt 0.070893 0.446918 +vt 0.073459 0.441065 +vt 0.087335 0.455437 +vt 0.078288 0.454852 +vt 0.066571 0.451728 +vt 0.089721 0.440075 +vt 0.082840 0.433949 +vt 0.091000 0.433105 +vt 0.075065 0.434763 +vt 0.099932 0.432204 +vt 0.098549 0.439333 +vt 0.097168 0.446972 +vt 0.095918 0.455257 +vt 0.779229 0.072428 +vt 0.771820 0.074228 +vt 0.777746 0.062124 +vt 0.764523 0.075886 +vt 0.771044 0.063104 +vt 0.784617 0.060864 +vt 0.785208 0.049379 +vt 0.791420 0.048683 +vt 0.793436 0.036312 +vt 0.779237 0.049630 +vt 0.788268 0.035810 +vt 0.798117 0.047096 +vt 0.791823 0.059043 +vt 0.805541 0.044175 +vt 0.799533 0.056380 +vt 0.798931 0.036192 +vt 0.805077 0.034827 +vt 0.812201 0.031596 +vt 0.786861 0.070344 +vt 0.794828 0.067833 +vt 0.981956 0.040976 +vt 0.977494 0.054498 +vt 0.972565 0.051750 +vt 0.973815 0.068088 +vt 0.968846 0.065892 +vt 0.986949 0.044335 +vt 0.982423 0.057245 +vt 0.978783 0.070285 +vt 0.976962 0.037617 +vt 0.967636 0.049003 +vt 0.971969 0.034258 +vt 0.962707 0.046255 +vt 0.963878 0.063696 +vt 0.958909 0.061500 +vt 0.976684 0.019479 +vt 0.981812 0.023490 +vt 0.981590 0.004682 +vt 0.986887 0.009365 +vt 0.966975 0.030899 +vt 0.971555 0.015468 +vt 0.976292 0.000000 +vt 0.986940 0.027500 +vt 0.992068 0.031511 +vt 0.992185 0.014047 +vt 0.997482 0.018730 +vt 0.263008 0.269046 +vt 0.275028 0.259421 +vt 0.278317 0.264837 +vt 0.266195 0.275222 +vt 0.281642 0.269329 +vt 0.287155 0.249524 +vt 0.290511 0.254084 +vt 0.293948 0.257642 +vt 0.254219 0.284868 +vt 0.251202 0.278129 +vt 0.242461 0.293406 +vt 0.239716 0.286400 +vt 0.269366 0.280544 +vt 0.257154 0.290817 +vt 0.245037 0.299676 +vt 0.248023 0.269812 +vt 0.259792 0.261163 +vt 0.244599 0.259128 +vt 0.236631 0.277920 +vt 0.233036 0.267232 +vt 0.271813 0.252153 +vt 0.283960 0.242963 +vt 0.256531 0.250720 +vt 0.268708 0.242110 +vt 0.281008 0.233398 +vt 0.964141 0.094002 +vt 0.969507 0.095305 +vt 0.968714 0.108451 +vt 0.963193 0.107552 +vt 0.968717 0.120968 +vt 0.974872 0.096609 +vt 0.974234 0.109350 +vt 0.974230 0.121448 +vt 0.957672 0.106653 +vt 0.958776 0.092699 +vt 0.952152 0.105754 +vt 0.953411 0.091395 +vt 0.963205 0.120487 +vt 0.957693 0.120006 +vt 0.952181 0.119526 +vt 0.960884 0.078320 +vt 0.955737 0.076595 +vt 0.966032 0.080046 +vt 0.971180 0.081771 +vt 0.976328 0.083496 +vt 0.217903 0.300146 +vt 0.228657 0.293586 +vt 0.230995 0.300467 +vt 0.220249 0.306467 +vt 0.233046 0.306650 +vt 0.210649 0.311821 +vt 0.207332 0.306539 +vt 0.200827 0.317057 +vt 0.196843 0.312611 +vt 0.222204 0.312296 +vt 0.213535 0.317172 +vt 0.203152 0.301392 +vt 0.214777 0.292841 +vt 0.198300 0.293755 +vt 0.192170 0.309279 +vt 0.181899 0.310325 +vt 0.225743 0.285307 +vt 0.210635 0.283385 +vt 0.221964 0.274931 +vt 0.966116 0.144113 +vt 0.970909 0.143688 +vt 0.973171 0.154480 +vt 0.968909 0.155258 +vt 0.976262 0.165284 +vt 0.975701 0.143264 +vt 0.977433 0.153702 +vt 0.979999 0.164308 +vt 0.964648 0.156035 +vt 0.961324 0.144537 +vt 0.960386 0.156813 +vt 0.956531 0.144962 +vt 0.972524 0.166259 +vt 0.968787 0.167235 +vt 0.965049 0.168211 +vt 0.958958 0.132581 +vt 0.964197 0.132598 +vt 0.953718 0.132564 +vt 0.969437 0.132616 +vt 0.974676 0.132633 +vt 0.062280 0.330719 +vt 0.054560 0.331798 +vt 0.056883 0.326347 +vt 0.046824 0.333014 +vt 0.048636 0.328708 +vt 0.059222 0.337196 +vt 0.052005 0.337235 +vt 0.044793 0.337236 +vt 0.065096 0.324294 +vt 0.058743 0.320871 +vt 0.067428 0.317975 +vt 0.059908 0.315356 +vt 0.050010 0.324234 +vt 0.050726 0.319509 +vt 0.076020 0.316013 +vt 0.073241 0.322856 +vt 0.084470 0.315452 +vt 0.081284 0.322340 +vt 0.069036 0.311813 +vt 0.078059 0.309493 +vt 0.086924 0.309007 +vt 0.069969 0.329914 +vt 0.066450 0.337081 +vt 0.077610 0.329522 +vt 0.073692 0.336851 +vt 0.166763 0.795075 +vt 0.159084 0.797139 +vt 0.160480 0.788037 +vt 0.151737 0.799129 +vt 0.153682 0.789635 +vt 0.166039 0.803583 +vt 0.157984 0.805897 +vt 0.150223 0.808169 +vt 0.167535 0.786320 +vt 0.162073 0.778707 +vt 0.168176 0.777388 +vt 0.163764 0.769262 +vt 0.155914 0.779838 +vt 0.158289 0.769889 +vt 0.174168 0.775695 +vt 0.175107 0.784366 +vt 0.181861 0.773680 +vt 0.183920 0.782115 +vt 0.168751 0.768368 +vt 0.172759 0.766938 +vt 0.175301 0.764705 +vt 0.175108 0.792865 +vt 0.174684 0.801183 +vt 0.184451 0.790436 +vt 0.184211 0.798653 +vt 0.213105 0.745866 +vt 0.215730 0.752090 +vt 0.202887 0.755282 +vt 0.217663 0.758799 +vt 0.204671 0.761622 +vt 0.225363 0.742637 +vt 0.228149 0.749195 +vt 0.230177 0.756178 +vt 0.200588 0.749509 +vt 0.189519 0.758368 +vt 0.187838 0.753350 +vt 0.175888 0.761401 +vt 0.191059 0.764108 +vt 0.185411 0.748224 +vt 0.197758 0.743943 +vt 0.182548 0.743023 +vt 0.194612 0.738445 +vt 0.174971 0.757291 +vt 0.173005 0.752640 +vt 0.170441 0.747712 +vt 0.209990 0.739932 +vt 0.222048 0.736323 +vt 0.206587 0.734091 +vt 0.218431 0.730073 +vt 0.206746 0.784938 +vt 0.195125 0.787714 +vt 0.194699 0.779508 +vt 0.206469 0.793268 +vt 0.194917 0.795952 +vt 0.206669 0.776762 +vt 0.193065 0.771397 +vt 0.205954 0.768886 +vt 0.218701 0.766187 +vt 0.219053 0.774091 +vt 0.231220 0.763768 +vt 0.231513 0.771818 +vt 0.218929 0.782347 +vt 0.218536 0.790791 +vt 0.231290 0.780181 +vt 0.230788 0.788711 +vt 0.260713 0.736083 +vt 0.263794 0.744012 +vt 0.252100 0.745331 +vt 0.266046 0.752050 +vt 0.254285 0.752991 +vt 0.272260 0.734421 +vt 0.275407 0.742869 +vt 0.277724 0.751295 +vt 0.249096 0.737902 +vt 0.240245 0.747001 +vt 0.237336 0.740035 +vt 0.242357 0.754305 +vt 0.233873 0.733253 +vt 0.245524 0.730587 +vt 0.230098 0.726503 +vt 0.241634 0.723270 +vt 0.257059 0.728190 +vt 0.268535 0.725927 +vt 0.253082 0.720261 +vt 0.264486 0.717363 +vt 0.255447 0.777674 +vt 0.243446 0.778679 +vt 0.243707 0.770266 +vt 0.254888 0.786163 +vt 0.242893 0.787217 +vt 0.255703 0.769255 +vt 0.243427 0.762101 +vt 0.255401 0.760996 +vt 0.267219 0.760266 +vt 0.267567 0.768603 +vt 0.278959 0.759725 +vt 0.279364 0.768132 +vt 0.267345 0.777002 +vt 0.266810 0.785402 +vt 0.279192 0.776495 +vt 0.278694 0.784787 +vt 0.975288 0.611530 +vt 0.974124 0.619586 +vt 0.967238 0.617891 +vt 0.968461 0.609894 +vt 0.960352 0.616195 +vt 0.973133 0.627580 +vt 0.966189 0.625780 +vt 0.959245 0.623979 +vt 0.969799 0.601824 +vt 0.976565 0.603433 +vt 0.971195 0.593718 +vt 0.977900 0.595316 +vt 0.961634 0.608257 +vt 0.963033 0.600214 +vt 0.964491 0.592120 +vt 0.983331 0.605043 +vt 0.982114 0.613167 +vt 0.990097 0.606652 +vt 0.984605 0.596913 +vt 0.991309 0.598511 +vt 0.981011 0.621282 +vt 0.980076 0.629381 +vt 0.988941 0.614804 +vt 0.987897 0.622977 +vt 0.987020 0.631181 +vt 0.935374 0.061707 +vt 0.939627 0.058901 +vt 0.941744 0.068576 +vt 0.937523 0.071769 +vt 0.943860 0.078252 +vt 0.943951 0.055994 +vt 0.946180 0.065515 +vt 0.948410 0.075036 +vt 0.933734 0.075223 +vt 0.931265 0.064315 +vt 0.939749 0.081800 +vt 0.936512 0.086009 +vt 0.929413 0.053161 +vt 0.933380 0.051584 +vt 0.927870 0.041886 +vt 0.937510 0.049225 +vt 0.941722 0.046474 +vt 0.931463 0.041431 +vt 0.935394 0.039549 +vt 0.939492 0.036953 +vt 0.971819 0.643204 +vt 0.971465 0.650598 +vt 0.964213 0.648016 +vt 0.964730 0.640983 +vt 0.956962 0.645435 +vt 0.971293 0.657556 +vt 0.963767 0.654483 +vt 0.956242 0.651410 +vt 0.965371 0.633524 +vt 0.972369 0.635492 +vt 0.957641 0.638762 +vt 0.958372 0.631556 +vt 0.979368 0.637460 +vt 0.978908 0.645424 +vt 0.986367 0.639428 +vt 0.978717 0.653179 +vt 0.978819 0.660630 +vt 0.985997 0.647645 +vt 0.985969 0.655760 +vt 0.986344 0.663703 +vt 0.719107 0.957499 +vt 0.718874 0.950111 +vt 0.725056 0.952431 +vt 0.725172 0.959681 +vt 0.731238 0.954752 +vt 0.718640 0.942724 +vt 0.724939 0.945182 +vt 0.731238 0.947640 +vt 0.725289 0.966931 +vt 0.719340 0.964886 +vt 0.725405 0.974180 +vt 0.719573 0.972274 +vt 0.731238 0.961863 +vt 0.731238 0.968975 +vt 0.731238 0.976086 +vt 0.713391 0.962842 +vt 0.713041 0.955317 +vt 0.707443 0.960798 +vt 0.713741 0.970368 +vt 0.707909 0.968462 +vt 0.712691 0.947791 +vt 0.712342 0.940265 +vt 0.706976 0.953134 +vt 0.706509 0.945471 +vt 0.706043 0.937807 +vt 0.971403 0.669954 +vt 0.971602 0.675669 +vt 0.962514 0.670350 +vt 0.962922 0.665473 +vt 0.953426 0.665031 +vt 0.971843 0.681246 +vt 0.962110 0.675049 +vt 0.952377 0.668853 +vt 0.963338 0.660243 +vt 0.971286 0.663962 +vt 0.954441 0.660993 +vt 0.955391 0.656524 +vt 0.979234 0.667682 +vt 0.979884 0.674434 +vt 0.987181 0.671401 +vt 0.980691 0.680988 +vt 0.981576 0.687442 +vt 0.988365 0.678915 +vt 0.989779 0.686307 +vt 0.991309 0.693638 +vt 0.270402 0.826415 +vt 0.263081 0.822341 +vt 0.272441 0.820144 +vt 0.277542 0.824056 +vt 0.281408 0.818287 +vt 0.257636 0.817242 +vt 0.268675 0.815230 +vt 0.279122 0.813740 +vt 0.283532 0.827299 +vt 0.278712 0.830024 +vt 0.289967 0.830207 +vt 0.287517 0.833400 +vt 0.284583 0.821782 +vt 0.288351 0.824574 +vt 0.292417 0.827015 +vt 0.273893 0.832749 +vt 0.263065 0.828944 +vt 0.269073 0.835474 +vt 0.285067 0.836592 +vt 0.282617 0.839785 +vt 0.252933 0.825219 +vt 0.245411 0.820300 +vt 0.255630 0.831559 +vt 0.242390 0.828436 +vt 0.229455 0.826503 +vt 0.405613 0.838724 +vt 0.404638 0.847949 +vt 0.394600 0.842373 +vt 0.395977 0.833051 +vt 0.384562 0.836797 +vt 0.403791 0.857092 +vt 0.393501 0.851852 +vt 0.383211 0.846612 +vt 0.397540 0.823834 +vt 0.406674 0.829444 +vt 0.399196 0.814669 +vt 0.407778 0.820136 +vt 0.386341 0.827378 +vt 0.388406 0.818224 +vt 0.390614 0.809202 +vt 0.415808 0.835054 +vt 0.415249 0.844397 +vt 0.424943 0.840663 +vt 0.416360 0.825603 +vt 0.424943 0.831070 +vt 0.414676 0.853526 +vt 0.414082 0.862332 +vt 0.424886 0.850070 +vt 0.424714 0.859102 +vt 0.424372 0.867573 +vt 0.044618 0.825136 +vt 0.060249 0.825508 +vt 0.051888 0.832090 +vt 0.036823 0.831848 +vt 0.043441 0.838752 +vt 0.075777 0.825875 +vt 0.066953 0.832332 +vt 0.057921 0.838905 +vt 0.021759 0.831606 +vt 0.028779 0.824749 +vt 0.006694 0.831364 +vt 0.012836 0.824356 +vt 0.028960 0.838599 +vt 0.014480 0.838446 +vt 0.000000 0.838292 +vt 0.035284 0.817858 +vt 0.052277 0.818499 +vt 0.043038 0.810805 +vt 0.017875 0.817190 +vt 0.068438 0.819086 +vt 0.084183 0.819646 +vt 0.060237 0.811948 +vt 0.076367 0.812903 +vt 0.091964 0.813763 +vt 0.184658 0.721566 +vt 0.195950 0.716060 +vt 0.199545 0.722130 +vt 0.207212 0.710733 +vt 0.210994 0.717248 +vt 0.181255 0.715876 +vt 0.192332 0.709966 +vt 0.203411 0.704188 +vt 0.203098 0.728148 +vt 0.191362 0.732869 +vt 0.214740 0.723705 +vt 0.188036 0.727237 +vt 0.179563 0.737775 +vt 0.176486 0.732494 +vt 0.167733 0.742774 +vt 0.173348 0.727190 +vt 0.170179 0.721875 +vt 0.164917 0.737826 +vt 0.162028 0.732873 +vt 0.159103 0.727917 +vt 0.205736 0.809998 +vt 0.194337 0.812350 +vt 0.194649 0.804162 +vt 0.206122 0.801608 +vt 0.183698 0.814829 +vt 0.183959 0.806776 +vt 0.218084 0.799259 +vt 0.217638 0.807921 +vt 0.230241 0.797261 +vt 0.229811 0.806051 +vt 0.217264 0.816947 +vt 0.205340 0.818482 +vt 0.216925 0.826155 +vt 0.204940 0.827011 +vt 0.229658 0.815299 +vt 0.193995 0.820524 +vt 0.183431 0.822834 +vt 0.193639 0.828692 +vt 0.183161 0.830816 +vt 0.233670 0.708312 +vt 0.222363 0.712667 +vt 0.218435 0.705642 +vt 0.229630 0.700730 +vt 0.214491 0.698587 +vt 0.240805 0.695936 +vt 0.244938 0.704107 +vt 0.251971 0.691202 +vt 0.225573 0.693119 +vt 0.236655 0.687739 +vt 0.247739 0.682403 +vt 0.249036 0.712224 +vt 0.237677 0.715835 +vt 0.256185 0.699977 +vt 0.260363 0.708705 +vt 0.226256 0.719631 +vt 0.254282 0.794628 +vt 0.266215 0.793745 +vt 0.266022 0.801710 +vt 0.278124 0.792985 +vt 0.277795 0.800762 +vt 0.254135 0.802837 +vt 0.266689 0.808979 +vt 0.278023 0.807789 +vt 0.254953 0.810558 +vt 0.242416 0.812917 +vt 0.242021 0.804320 +vt 0.242299 0.795759 +vt 0.100851 0.319471 +vt 0.108876 0.323115 +vt 0.104734 0.326942 +vt 0.116855 0.327227 +vt 0.104073 0.314757 +vt 0.112462 0.319771 +vt 0.120799 0.325397 +vt 0.100220 0.331090 +vt 0.092720 0.330247 +vt 0.095521 0.335399 +vt 0.088233 0.335972 +vt 0.112437 0.329501 +vt 0.107704 0.332071 +vt 0.102814 0.334788 +vt 0.085189 0.329678 +vt 0.089191 0.323054 +vt 0.080955 0.336469 +vt 0.096996 0.324690 +vt 0.092731 0.316761 +vt 0.095577 0.310965 +vt 0.165543 0.811774 +vt 0.174347 0.809307 +vt 0.174077 0.817286 +vt 0.173850 0.825169 +vt 0.165007 0.827522 +vt 0.173646 0.833003 +vt 0.164853 0.835237 +vt 0.156655 0.829888 +vt 0.156868 0.822158 +vt 0.148550 0.832259 +vt 0.148773 0.824583 +vt 0.156541 0.837503 +vt 0.148470 0.839785 +vt 0.165218 0.819727 +vt 0.157278 0.814199 +vt 0.149283 0.816603 +vt 0.402638 0.874840 +vt 0.402379 0.883026 +vt 0.392454 0.880257 +vt 0.392423 0.871118 +vt 0.382529 0.877489 +vt 0.402366 0.890472 +vt 0.392872 0.888638 +vt 0.383377 0.886804 +vt 0.392774 0.861542 +vt 0.403117 0.866126 +vt 0.382209 0.867395 +vt 0.382431 0.856957 +vt 0.413459 0.870710 +vt 0.412852 0.878563 +vt 0.423802 0.875295 +vt 0.412305 0.885794 +vt 0.411861 0.892307 +vt 0.423067 0.882286 +vt 0.422230 0.888562 +vt 0.421355 0.894141 +vt 0.077598 0.799419 +vt 0.091112 0.801847 +vt 0.083951 0.807039 +vt 0.068936 0.805495 +vt 0.105144 0.803971 +vt 0.098910 0.808339 +vt 0.053809 0.803465 +vt 0.065119 0.796385 +vt 0.074492 0.790112 +vt 0.085446 0.794001 +vt 0.082395 0.784602 +vt 0.097773 0.797681 +vt 0.110786 0.801257 +vt 0.092344 0.789119 +vt 0.103478 0.793882 +vt 0.115959 0.800792 +vt 0.403115 0.919702 +vt 0.397388 0.920455 +vt 0.397125 0.916714 +vt 0.396603 0.912542 +vt 0.403561 0.912055 +vt 0.391661 0.921209 +vt 0.390801 0.917390 +vt 0.389646 0.913029 +vt 0.403449 0.916038 +vt 0.409773 0.915362 +vt 0.408842 0.918949 +vt 0.416096 0.914686 +vt 0.414569 0.918196 +vt 0.410518 0.911568 +vt 0.417475 0.911081 +vt 0.407809 0.922394 +vt 0.402646 0.923166 +vt 0.406755 0.925765 +vt 0.412971 0.921623 +vt 0.411381 0.924983 +vt 0.397484 0.923937 +vt 0.392321 0.924708 +vt 0.402129 0.926547 +vt 0.397503 0.927329 +vt 0.392877 0.928111 +vt 0.116516 0.767402 +vt 0.118588 0.764449 +vt 0.123708 0.767886 +vt 0.122337 0.771284 +vt 0.128918 0.770897 +vt 0.120582 0.761594 +vt 0.125236 0.764886 +vt 0.129951 0.767703 +vt 0.121236 0.775446 +vt 0.114260 0.770605 +vt 0.119398 0.779551 +vt 0.111440 0.773908 +vt 0.128346 0.774858 +vt 0.128626 0.780264 +vt 0.130147 0.787792 +vt 0.108112 0.765715 +vt 0.104595 0.768696 +vt 0.111068 0.762907 +vt 0.113651 0.760157 +vt 0.116048 0.757350 +vt 0.394696 0.902273 +vt 0.393680 0.895937 +vt 0.402622 0.896971 +vt 0.386389 0.901898 +vt 0.384739 0.894902 +vt 0.403003 0.902649 +vt 0.411563 0.898005 +vt 0.411310 0.903025 +vt 0.420505 0.899040 +vt 0.410996 0.907501 +vt 0.403364 0.907634 +vt 0.419617 0.903401 +vt 0.418628 0.907367 +vt 0.395732 0.907768 +vt 0.388100 0.907902 +vt 0.107672 0.777164 +vt 0.115817 0.782776 +vt 0.111580 0.785923 +vt 0.123470 0.789708 +vt 0.119458 0.792107 +vt 0.103172 0.780653 +vt 0.107774 0.789794 +vt 0.098158 0.784653 +vt 0.116595 0.795338 +vt 0.089292 0.779813 +vt 0.095249 0.775638 +vt 0.100328 0.771968 +vt 0.400770 0.936869 +vt 0.397635 0.937910 +vt 0.397583 0.934336 +vt 0.394500 0.938952 +vt 0.393964 0.935264 +vt 0.400350 0.940340 +vt 0.397692 0.941505 +vt 0.395034 0.942671 +vt 0.401201 0.933407 +vt 0.397537 0.930802 +vt 0.401651 0.929963 +vt 0.393424 0.931640 +vt 0.405764 0.929125 +vt 0.404819 0.932479 +vt 0.409877 0.928287 +vt 0.403906 0.935828 +vt 0.403007 0.939174 +vt 0.408437 0.931550 +vt 0.407041 0.934786 +vt 0.405665 0.938008 +vt 0.128425 0.758969 +vt 0.126813 0.761917 +vt 0.122600 0.758689 +vt 0.132216 0.761560 +vt 0.131057 0.764598 +vt 0.124636 0.755746 +vt 0.118446 0.754368 +vt 0.120846 0.751256 +vt 0.123246 0.748057 +vt 0.126684 0.752777 +vt 0.125647 0.744814 +vt 0.130062 0.756037 +vt 0.133410 0.758566 +vt 0.128738 0.749796 +vt 0.131710 0.753113 +vt 0.134623 0.755595 +vt 0.177539 0.328560 +vt 0.166345 0.334449 +vt 0.164706 0.329338 +vt 0.155490 0.340352 +vt 0.153593 0.335099 +vt 0.175669 0.323676 +vt 0.163685 0.325032 +vt 0.162973 0.321127 +vt 0.173021 0.315819 +vt 0.152306 0.330875 +vt 0.151322 0.327166 +vt 0.174236 0.319557 +vt 0.183131 0.314820 +vt 0.186335 0.318212 +vt 0.179864 0.311975 +vt 0.189412 0.322700 +vt 0.976908 0.177346 +vt 0.980218 0.176394 +vt 0.984824 0.187734 +vt 0.983528 0.175442 +vt 0.981869 0.188495 +vt 0.989862 0.199227 +vt 0.987214 0.199686 +vt 0.995117 0.210797 +vt 0.987778 0.186973 +vt 0.992510 0.198768 +vt 0.997482 0.210695 +vt 0.984566 0.200146 +vt 0.978914 0.189256 +vt 0.981918 0.200605 +vt 0.975959 0.190017 +vt 0.992751 0.210899 +vt 0.990386 0.211001 +vt 0.988021 0.211103 +vt 0.973598 0.178297 +vt 0.970288 0.179249 +vt 0.029412 0.063521 +vt 0.034033 0.048977 +vt 0.051470 0.053065 +vt 0.039011 0.034867 +vt 0.056386 0.039332 +vt 0.011243 0.059537 +vt 0.016234 0.044179 +vt 0.021646 0.029243 +vt 0.046967 0.067113 +vt 0.068183 0.055729 +vt 0.063294 0.069918 +vt 0.082962 0.058370 +vt 0.073780 0.041476 +vt 0.091202 0.040141 +vt 0.059823 0.083979 +vt 0.043292 0.081790 +vt 0.057471 0.099011 +vt 0.040607 0.097703 +vt 0.077568 0.072073 +vt 0.074471 0.085164 +vt 0.072728 0.099194 +vt 0.025505 0.078932 +vt 0.007091 0.075740 +vt 0.022671 0.095644 +vt 0.004199 0.093211 +vt 0.351496 0.429381 +vt 0.366860 0.418222 +vt 0.373798 0.431222 +vt 0.380665 0.409516 +vt 0.386716 0.421213 +vt 0.345341 0.415259 +vt 0.361234 0.405024 +vt 0.375864 0.397308 +vt 0.358815 0.443365 +vt 0.382942 0.444254 +vt 0.367673 0.457371 +vt 0.394788 0.457685 +vt 0.395345 0.432619 +vt 0.406884 0.444292 +vt 0.349163 0.470832 +vt 0.342124 0.456771 +vt 0.329033 0.484465 +vt 0.324578 0.470809 +vt 0.379330 0.471961 +vt 0.359617 0.485710 +vt 0.329887 0.496232 +vt 0.335091 0.442176 +vt 0.328605 0.427174 +vt 0.318168 0.455788 +vt 0.311449 0.439928 +vt 0.021444 0.134358 +vt 0.021267 0.114091 +vt 0.039075 0.115457 +vt 0.003606 0.133356 +vt 0.002987 0.112372 +vt 0.038864 0.135065 +vt 0.055941 0.116116 +vt 0.071395 0.115715 +vt 0.070783 0.134421 +vt 0.055449 0.135185 +vt 0.056210 0.156109 +vt 0.040142 0.156541 +vt 0.058440 0.178780 +vt 0.043077 0.179898 +vt 0.071200 0.155010 +vt 0.072957 0.177176 +vt 0.023355 0.156529 +vt 0.006208 0.156295 +vt 0.027151 0.180691 +vt 0.010942 0.181323 +vt 0.330836 0.371405 +vt 0.346449 0.363282 +vt 0.351133 0.377446 +vt 0.361084 0.357037 +vt 0.326660 0.356540 +vt 0.341870 0.349010 +vt 0.356095 0.343006 +vt 0.335209 0.386197 +vt 0.356027 0.391396 +vt 0.339978 0.400839 +vt 0.366055 0.370887 +vt 0.370988 0.384371 +vt 0.323204 0.411895 +vt 0.318615 0.396407 +vt 0.306067 0.423756 +vt 0.301688 0.407348 +vt 0.314569 0.380782 +vt 0.310795 0.365087 +vt 0.297977 0.390784 +vt 0.294601 0.374142 +vt 0.053595 0.230534 +vt 0.039863 0.233241 +vt 0.032985 0.206928 +vt 0.026135 0.235922 +vt 0.017962 0.208571 +vt 0.047839 0.205147 +vt 0.062355 0.203089 +vt 0.067334 0.227777 +vt 0.076364 0.200615 +vt 0.080930 0.224517 +vt 0.072761 0.251587 +vt 0.059518 0.254303 +vt 0.077157 0.271937 +vt 0.086166 0.248073 +vt 0.092317 0.271990 +vt 0.046793 0.257628 +vt 0.034328 0.261256 +vt 0.064561 0.274367 +vt 0.052782 0.278089 +vt 0.041411 0.282456 +vt 0.704985 0.228638 +vt 0.684829 0.230417 +vt 0.684350 0.216583 +vt 0.706175 0.214058 +vt 0.683967 0.202458 +vt 0.668421 0.232057 +vt 0.666952 0.218975 +vt 0.654826 0.205457 +vt 0.728159 0.211506 +vt 0.726943 0.226781 +vt 0.750223 0.208940 +vt 0.706855 0.199662 +vt 0.728690 0.196913 +vt 0.750000 0.194187 +vt 0.725271 0.242512 +vt 0.723372 0.258469 +vt 0.700136 0.258211 +vt 0.749802 0.224886 +vt 0.748952 0.241627 +vt 0.747887 0.258765 +vt 0.702808 0.243383 +vt 0.682778 0.244228 +vt 0.666398 0.245031 +vt 0.679462 0.258026 +vt 0.662629 0.257952 +vt 0.069257 0.298634 +vt 0.059206 0.302700 +vt 0.056835 0.292623 +vt 0.067679 0.288640 +vt 0.049188 0.307572 +vt 0.046250 0.297401 +vt 0.079042 0.286246 +vt 0.079375 0.296179 +vt 0.090292 0.287153 +vt 0.089370 0.296371 +vt 0.079112 0.303405 +vt 0.069678 0.305863 +vt 0.088402 0.303151 +vt 0.060146 0.309788 +vt 0.050566 0.314448 +vt 0.650715 0.234759 +vt 0.645454 0.235875 +vt 0.649310 0.224578 +vt 0.653833 0.223008 +vt 0.653167 0.213281 +vt 0.641060 0.236917 +vt 0.644733 0.225993 +vt 0.648407 0.215069 +vt 0.658246 0.221129 +vt 0.657710 0.233495 +vt 0.656665 0.211247 +vt 0.657639 0.208721 +vt 0.654886 0.245781 +vt 0.647025 0.246490 +vt 0.650918 0.258026 +vt 0.641597 0.247172 +vt 0.637386 0.247841 +vt 0.643049 0.258211 +vt 0.637740 0.258469 +vt 0.633712 0.258765 +vt 0.117772 0.314696 +vt 0.115306 0.317071 +vt 0.106451 0.310718 +vt 0.126964 0.323435 +vt 0.124113 0.324159 +vt 0.108497 0.306713 +vt 0.097499 0.305833 +vt 0.099053 0.300243 +vt 0.100798 0.293074 +vt 0.110722 0.302101 +vt 0.104458 0.284970 +vt 0.113985 0.296100 +vt 0.120226 0.312326 +vt 0.129520 0.323150 +vt 0.123256 0.307306 +vt 0.131948 0.323227 +vt 0.134386 0.809704 +vt 0.139587 0.821690 +vt 0.136304 0.819003 +vt 0.144863 0.834249 +vt 0.141256 0.829593 +vt 0.137677 0.810549 +vt 0.142870 0.824377 +vt 0.148470 0.838905 +vt 0.131200 0.808924 +vt 0.133022 0.816317 +vt 0.128222 0.808275 +vt 0.129739 0.813630 +vt 0.137649 0.824937 +vt 0.134042 0.820281 +vt 0.123079 0.801390 +vt 0.125791 0.799867 +vt 0.125349 0.807690 +vt 0.120784 0.803173 +vt 0.129337 0.798864 +vt 0.133300 0.798120 +vt 0.249953 0.221047 +vt 0.263076 0.212558 +vt 0.265749 0.228364 +vt 0.253209 0.236863 +vt 0.276177 0.204002 +vt 0.278378 0.219828 +vt 0.240851 0.245287 +vt 0.236785 0.229402 +vt 0.228762 0.253598 +vt 0.223739 0.237834 +vt 0.232411 0.212585 +vt 0.246886 0.204727 +vt 0.229007 0.196684 +vt 0.217896 0.220755 +vt 0.210430 0.201976 +vt 0.260831 0.196331 +vt 0.274509 0.187668 +vt 0.244452 0.189540 +vt 0.259152 0.181324 +vt 0.273480 0.172574 +vt 0.658318 0.037849 +vt 0.676941 0.033916 +vt 0.678414 0.047485 +vt 0.657937 0.052085 +vt 0.677548 0.062282 +vt 0.695674 0.029827 +vt 0.699332 0.042257 +vt 0.703433 0.051707 +vt 0.638341 0.055427 +vt 0.639916 0.041468 +vt 0.619186 0.058141 +vt 0.621624 0.044929 +vt 0.656098 0.066633 +vt 0.635898 0.069172 +vt 0.616323 0.070803 +vt 0.640913 0.027364 +vt 0.658045 0.023380 +vt 0.641620 0.013189 +vt 0.623780 0.031349 +vt 0.625794 0.017586 +vt 0.675178 0.019395 +vt 0.692311 0.015410 +vt 0.657445 0.008793 +vt 0.673271 0.004396 +vt 0.689096 0.000000 +vt 0.364143 0.547686 +vt 0.381733 0.571125 +vt 0.362684 0.577088 +vt 0.398629 0.595520 +vt 0.377395 0.599047 +vt 0.380457 0.537003 +vt 0.399979 0.560828 +vt 0.419045 0.594109 +vt 0.347800 0.555368 +vt 0.343265 0.582852 +vt 0.330914 0.561719 +vt 0.323660 0.588516 +vt 0.355616 0.603985 +vt 0.333564 0.609629 +vt 0.318563 0.540586 +vt 0.332569 0.534125 +vt 0.306213 0.519453 +vt 0.317165 0.513121 +vt 0.313756 0.567404 +vt 0.303852 0.546292 +vt 0.293948 0.525180 +vt 0.345165 0.526157 +vt 0.359042 0.515945 +vt 0.325493 0.505583 +vt 0.395046 0.487700 +vt 0.409834 0.471881 +vt 0.427288 0.486209 +vt 0.421670 0.456790 +vt 0.438920 0.469605 +vt 0.413290 0.503945 +vt 0.446356 0.500033 +vt 0.432532 0.520051 +vt 0.466246 0.512720 +vt 0.457854 0.482229 +vt 0.477690 0.494152 +vt 0.416992 0.542060 +vt 0.397260 0.521649 +vt 0.452237 0.534343 +vt 0.436355 0.559528 +vt 0.376892 0.502758 +vt 0.544799 0.176046 +vt 0.554250 0.189482 +vt 0.538724 0.208350 +vt 0.564101 0.202650 +vt 0.548089 0.220941 +vt 0.560612 0.155377 +vt 0.569673 0.169564 +vt 0.580029 0.183312 +vt 0.529103 0.195629 +vt 0.522992 0.225119 +vt 0.513642 0.213040 +vt 0.506952 0.238739 +vt 0.531907 0.237140 +vt 0.515471 0.250201 +vt 0.503418 0.200847 +vt 0.518968 0.182647 +vt 0.491886 0.188481 +vt 0.507119 0.169444 +vt 0.498048 0.227254 +vt 0.488377 0.215722 +vt 0.477553 0.204120 +vt 0.536146 0.162073 +vt 0.554139 0.140313 +vt 0.524920 0.147974 +vt 0.551550 0.123932 +vt 0.226449 0.599972 +vt 0.208187 0.601081 +vt 0.208605 0.579456 +vt 0.190056 0.602810 +vt 0.190110 0.581164 +vt 0.225489 0.620949 +vt 0.208209 0.622356 +vt 0.193275 0.627297 +vt 0.227520 0.578908 +vt 0.209902 0.557133 +vt 0.228810 0.557669 +vt 0.211639 0.534460 +vt 0.191229 0.558435 +vt 0.194673 0.529125 +vt 0.248473 0.557417 +vt 0.247093 0.578910 +vt 0.268513 0.556770 +vt 0.266996 0.579188 +vt 0.230210 0.536343 +vt 0.249853 0.535923 +vt 0.270030 0.534352 +vt 0.245714 0.600404 +vt 0.244334 0.621897 +vt 0.265478 0.601606 +vt 0.263961 0.624024 +vt 0.475267 0.142354 +vt 0.487306 0.119135 +vt 0.507745 0.134158 +vt 0.492356 0.156059 +vt 0.499559 0.096334 +vt 0.522163 0.112130 +vt 0.478609 0.175886 +vt 0.463372 0.163309 +vt 0.465194 0.192425 +vt 0.445963 0.150999 +vt 0.456441 0.128191 +vt 0.426168 0.139203 +vt 0.450975 0.180811 +vt 0.434571 0.169452 +vt 0.415659 0.158524 +vt 0.466288 0.101419 +vt 0.477398 0.076312 +vt 0.435756 0.114861 +vt 0.444538 0.085235 +vt 0.451828 0.044536 +vt 0.525026 0.058537 +vt 0.507380 0.043747 +vt 0.522860 0.027822 +vt 0.538221 0.011311 +vt 0.552157 0.022621 +vt 0.489793 0.028600 +vt 0.507256 0.015092 +vt 0.524285 0.000000 +vt 0.538465 0.040552 +vt 0.554069 0.053282 +vt 0.542789 0.072618 +vt 0.569673 0.066012 +vt 0.560612 0.086342 +vt 0.566093 0.033932 +vt 0.580029 0.045242 +vt 0.532999 0.091925 +vt 0.512095 0.076633 +vt 0.554139 0.105795 +vt 0.491661 0.058499 +vt 0.471462 0.038943 +vt 0.388873 0.093510 +vt 0.398329 0.066590 +vt 0.421902 0.074807 +vt 0.413091 0.103653 +vt 0.407788 0.038309 +vt 0.430459 0.043797 +vt 0.403772 0.128170 +vt 0.379426 0.117710 +vt 0.393914 0.148200 +vt 0.353779 0.107631 +vt 0.327483 0.097742 +vt 0.337623 0.073244 +vt 0.370045 0.138329 +vt 0.344760 0.128760 +vt 0.318766 0.119342 +vt 0.363530 0.083375 +vt 0.373767 0.057036 +vt 0.384249 0.029654 +vt 0.348712 0.046813 +vt 0.360275 0.019417 +vt 0.491431 0.553798 +vt 0.471875 0.545142 +vt 0.486165 0.523634 +vt 0.497648 0.504866 +vt 0.517697 0.514675 +vt 0.506106 0.533218 +vt 0.526062 0.541917 +vt 0.510891 0.561659 +vt 0.546025 0.550172 +vt 0.530303 0.569124 +vt 0.537806 0.523879 +vt 0.557946 0.532780 +vt 0.493436 0.582538 +vt 0.474674 0.576013 +vt 0.474838 0.603985 +vt 0.512049 0.589116 +vt 0.492527 0.609629 +vt 0.455615 0.569595 +vt 0.456877 0.599047 +vt 0.438370 0.595520 +vt 0.811878 0.089099 +vt 0.815324 0.096347 +vt 0.805905 0.099766 +vt 0.802642 0.092063 +vt 0.819075 0.104422 +vt 0.809698 0.108295 +vt 0.800628 0.084270 +vt 0.809598 0.081572 +vt 0.800587 0.075472 +vt 0.818748 0.078884 +vt 0.821350 0.086171 +vt 0.827989 0.076200 +vt 0.809347 0.072657 +vt 0.818272 0.069756 +vt 0.827280 0.066813 +vt 0.825041 0.092951 +vt 0.828785 0.100557 +vt 0.830941 0.083260 +vt 0.834907 0.089568 +vt 0.838662 0.096697 +vt 0.086811 0.474225 +vt 0.087399 0.485032 +vt 0.079517 0.484927 +vt 0.070440 0.486445 +vt 0.069954 0.475523 +vt 0.088828 0.497068 +vt 0.080599 0.496491 +vt 0.071079 0.497831 +vt 0.078847 0.474265 +vt 0.078229 0.464037 +vt 0.086796 0.464365 +vt 0.069178 0.464532 +vt 0.094932 0.464321 +vt 0.094526 0.474357 +vt 0.103618 0.463602 +vt 0.095020 0.485552 +vt 0.096732 0.498099 +vt 0.102906 0.473834 +vt 0.103314 0.485280 +vt 0.105277 0.498120 +vt 0.836809 0.578300 +vt 0.831964 0.569862 +vt 0.848419 0.566372 +vt 0.852040 0.573967 +vt 0.864874 0.562881 +vt 0.827839 0.560251 +vt 0.845440 0.557675 +vt 0.863042 0.555098 +vt 0.856238 0.581594 +vt 0.842305 0.586533 +vt 0.860948 0.590388 +vt 0.848386 0.595528 +vt 0.867272 0.569633 +vt 0.870172 0.576655 +vt 0.873509 0.585249 +vt 0.828371 0.591473 +vt 0.821577 0.582634 +vt 0.814438 0.596412 +vt 0.835825 0.600667 +vt 0.823264 0.605807 +vt 0.815509 0.573352 +vt 0.810238 0.562828 +vt 0.806345 0.586967 +vt 0.799055 0.576842 +vt 0.792636 0.565405 +vt 0.094390 0.525235 +vt 0.097283 0.540493 +vt 0.086656 0.537897 +vt 0.084624 0.523348 +vt 0.075185 0.536993 +vt 0.099424 0.555954 +vt 0.088095 0.552696 +vt 0.076124 0.550889 +vt 0.082453 0.509422 +vt 0.091365 0.510614 +vt 0.073799 0.523353 +vt 0.072315 0.510216 +vt 0.099980 0.512187 +vt 0.103949 0.527355 +vt 0.109228 0.512533 +vt 0.107822 0.543144 +vt 0.110784 0.559091 +vt 0.114152 0.528051 +vt 0.119031 0.544207 +vt 0.122849 0.560533 +vt 0.418670 0.272958 +vt 0.427884 0.264556 +vt 0.435537 0.273910 +vt 0.426243 0.282128 +vt 0.442898 0.283286 +vt 0.438328 0.256663 +vt 0.446153 0.266258 +vt 0.453684 0.275871 +vt 0.417011 0.289665 +vt 0.409359 0.280682 +vt 0.406582 0.295272 +vt 0.399078 0.286489 +vt 0.433416 0.291333 +vt 0.423946 0.298718 +vt 0.413195 0.304146 +vt 0.400273 0.271839 +vt 0.410297 0.263856 +vt 0.390921 0.263369 +vt 0.389791 0.277889 +vt 0.375351 0.269829 +vt 0.419645 0.255244 +vt 0.429916 0.247108 +vt 0.401196 0.254896 +vt 0.410528 0.245995 +vt 0.420621 0.237610 +vt 0.099598 0.586101 +vt 0.097648 0.600644 +vt 0.085828 0.596017 +vt 0.087755 0.581843 +vt 0.073647 0.592270 +vt 0.094351 0.614737 +vt 0.082632 0.609808 +vt 0.070613 0.605662 +vt 0.088486 0.567374 +vt 0.100193 0.571181 +vt 0.075488 0.578617 +vt 0.076270 0.564793 +vt 0.112018 0.574736 +vt 0.111624 0.590023 +vt 0.124590 0.576563 +vt 0.109703 0.604894 +vt 0.106355 0.619292 +vt 0.124442 0.592241 +vt 0.122592 0.607511 +vt 0.119227 0.622317 +vt 0.388773 0.227986 +vt 0.400240 0.236830 +vt 0.391437 0.246154 +vt 0.398399 0.219052 +vt 0.410152 0.228190 +vt 0.382471 0.255503 +vt 0.373367 0.247957 +vt 0.373423 0.264866 +vt 0.362054 0.240446 +vt 0.368560 0.229803 +vt 0.349388 0.233584 +vt 0.355351 0.222662 +vt 0.366456 0.258388 +vt 0.356074 0.251358 +vt 0.343901 0.244738 +vt 0.380674 0.237749 +vt 0.376118 0.219700 +vt 0.385253 0.210404 +vt 0.362266 0.212207 +vt 0.370608 0.202451 +vt 0.077772 0.654410 +vt 0.066328 0.648837 +vt 0.072546 0.636101 +vt 0.054885 0.644092 +vt 0.060901 0.631486 +vt 0.071131 0.667200 +vt 0.059774 0.661455 +vt 0.048538 0.656610 +vt 0.078093 0.623131 +vt 0.089717 0.628309 +vt 0.066254 0.618705 +vt 0.101680 0.633161 +vt 0.096010 0.646632 +vt 0.114536 0.636606 +vt 0.108849 0.650506 +vt 0.084079 0.641490 +vt 0.089676 0.659838 +vt 0.083011 0.672911 +vt 0.102499 0.664146 +vt 0.095817 0.677657 +vt 0.322688 0.223335 +vt 0.308903 0.219317 +vt 0.311529 0.206990 +vt 0.294994 0.215614 +vt 0.319092 0.235278 +vt 0.306541 0.231751 +vt 0.293948 0.228567 +vt 0.326625 0.211543 +vt 0.314682 0.194880 +vt 0.331246 0.200054 +vt 0.318627 0.183093 +vt 0.296222 0.202725 +vt 0.297816 0.189963 +vt 0.299960 0.177390 +vt 0.347208 0.205744 +vt 0.341303 0.216671 +vt 0.336892 0.189020 +vt 0.354353 0.195398 +vt 0.336224 0.227985 +vt 0.331559 0.239493 +vt 0.556368 0.442901 +vt 0.553997 0.455713 +vt 0.537798 0.446218 +vt 0.552098 0.468420 +vt 0.534912 0.458716 +vt 0.571484 0.452145 +vt 0.570126 0.465239 +vt 0.569253 0.478125 +vt 0.541338 0.433769 +vt 0.521462 0.436784 +vt 0.526480 0.424864 +vt 0.504919 0.427442 +vt 0.517667 0.449012 +vt 0.500333 0.439309 +vt 0.533376 0.413480 +vt 0.545580 0.421437 +vt 0.541211 0.402363 +vt 0.511081 0.416086 +vt 0.519881 0.405615 +vt 0.536398 0.397272 +vt 0.559055 0.430018 +vt 0.573166 0.438912 +vt 0.550172 0.409163 +vt 0.561899 0.417101 +vt 0.575010 0.425609 +vt 0.351975 0.167508 +vt 0.329663 0.159663 +vt 0.336714 0.145721 +vt 0.360791 0.154508 +vt 0.306733 0.151977 +vt 0.311947 0.137077 +vt 0.383488 0.163582 +vt 0.373049 0.175672 +vt 0.404114 0.173086 +vt 0.392267 0.184313 +vt 0.363152 0.185825 +vt 0.343905 0.178592 +vt 0.380853 0.193627 +vt 0.323628 0.171737 +vt 0.302835 0.165072 +vt 0.529786 0.484277 +vt 0.532633 0.471198 +vt 0.550829 0.480987 +vt 0.548890 0.494017 +vt 0.569027 0.490732 +vt 0.544983 0.508113 +vt 0.525199 0.498565 +vt 0.568019 0.503650 +vt 0.564802 0.517470 +vt 0.505486 0.488634 +vt 0.510733 0.474323 +vt 0.485879 0.478129 +vt 0.514442 0.461321 +vt 0.491754 0.464049 +vt 0.496258 0.451310 +vt 0.423578 0.203359 +vt 0.409009 0.193592 +vt 0.421978 0.183165 +vt 0.437425 0.193673 +vt 0.450802 0.204466 +vt 0.436272 0.213467 +vt 0.462452 0.215397 +vt 0.447391 0.223767 +vt 0.422442 0.220888 +vt 0.410284 0.211331 +vt 0.433178 0.230686 +vt 0.396493 0.202186 +vt 0.138574 0.652122 +vt 0.123107 0.652091 +vt 0.128838 0.637565 +vt 0.144807 0.637310 +vt 0.133552 0.622656 +vt 0.162666 0.637116 +vt 0.155043 0.651335 +vt 0.181470 0.636951 +vt 0.172012 0.650138 +vt 0.149869 0.622002 +vt 0.168716 0.622050 +vt 0.147852 0.666606 +vt 0.131931 0.667007 +vt 0.140879 0.682403 +vt 0.164118 0.665682 +vt 0.157006 0.682403 +vt 0.116699 0.666361 +vt 0.125083 0.681928 +vt 0.109952 0.680505 +vt 0.466129 0.244487 +vt 0.457238 0.234110 +vt 0.472722 0.226322 +vt 0.481956 0.237242 +vt 0.490499 0.248160 +vt 0.474383 0.254885 +vt 0.498697 0.259076 +vt 0.482318 0.265294 +vt 0.459351 0.260417 +vt 0.451331 0.250467 +vt 0.467069 0.270383 +vt 0.442708 0.240551 +vt 0.154404 0.588503 +vt 0.138660 0.591386 +vt 0.138540 0.575184 +vt 0.154293 0.570947 +vt 0.136294 0.558710 +vt 0.172274 0.564202 +vt 0.171799 0.584638 +vt 0.151792 0.553531 +vt 0.170018 0.544907 +vt 0.171186 0.604239 +vt 0.152999 0.605627 +vt 0.136912 0.607236 +vt 0.822040 0.534393 +vt 0.802399 0.535491 +vt 0.800060 0.518344 +vt 0.820559 0.517698 +vt 0.798926 0.498655 +vt 0.782757 0.536590 +vt 0.779560 0.518990 +vt 0.777699 0.499116 +vt 0.841059 0.517052 +vt 0.841681 0.533294 +vt 0.861558 0.516405 +vt 0.861322 0.532196 +vt 0.820153 0.498195 +vt 0.841380 0.497735 +vt 0.862607 0.497275 +vt 0.843168 0.546742 +vt 0.861837 0.544981 +vt 0.824499 0.548503 +vt 0.805829 0.550263 +vt 0.787160 0.552024 +vt 0.138986 0.520802 +vt 0.125849 0.525662 +vt 0.120039 0.510043 +vt 0.132631 0.505433 +vt 0.115430 0.495666 +vt 0.147222 0.499416 +vt 0.153504 0.514086 +vt 0.162813 0.492695 +vt 0.168714 0.506442 +vt 0.127735 0.491356 +vt 0.142735 0.485807 +vt 0.163692 0.479746 +vt 0.162432 0.528726 +vt 0.146032 0.536825 +vt 0.179850 0.519185 +vt 0.131666 0.542043 +vt 0.827843 0.138510 +vt 0.821354 0.143348 +vt 0.817064 0.130330 +vt 0.830507 0.151612 +vt 0.825907 0.156992 +vt 0.825117 0.125939 +vt 0.813298 0.118564 +vt 0.822267 0.114431 +vt 0.831544 0.110322 +vt 0.833565 0.121704 +vt 0.840976 0.106223 +vt 0.842210 0.117547 +vt 0.835093 0.134166 +vt 0.836375 0.147168 +vt 0.842724 0.130070 +vt 0.842878 0.143191 +vt 0.125062 0.479207 +vt 0.113216 0.483008 +vt 0.112862 0.471832 +vt 0.124520 0.468642 +vt 0.113837 0.461901 +vt 0.126015 0.459315 +vt 0.140580 0.455942 +vt 0.138003 0.464556 +vt 0.154990 0.452089 +vt 0.152268 0.459831 +vt 0.139192 0.474349 +vt 0.154238 0.468549 +vt 0.825341 0.426037 +vt 0.847837 0.424782 +vt 0.844929 0.450321 +vt 0.870333 0.423526 +vt 0.867160 0.449411 +vt 0.828690 0.401216 +vt 0.851287 0.399728 +vt 0.873883 0.398241 +vt 0.822698 0.451231 +vt 0.842723 0.475065 +vt 0.820917 0.475661 +vt 0.864529 0.474468 +vt 0.799112 0.476258 +vt 0.800468 0.452141 +vt 0.777306 0.476854 +vt 0.778237 0.453051 +vt 0.802845 0.427293 +vt 0.806093 0.402704 +vt 0.780348 0.428548 +vt 0.783496 0.404191 +vt 0.814577 0.356956 +vt 0.819451 0.335174 +vt 0.841498 0.334283 +vt 0.824506 0.313704 +vt 0.846228 0.313227 +vt 0.792247 0.358197 +vt 0.797404 0.336066 +vt 0.802783 0.314181 +vt 0.836907 0.355715 +vt 0.863546 0.333392 +vt 0.859237 0.354474 +vt 0.885593 0.332500 +vt 0.867950 0.312750 +vt 0.889673 0.312273 +vt 0.855119 0.376440 +vt 0.832591 0.377901 +vt 0.881566 0.353234 +vt 0.877646 0.374979 +vt 0.810064 0.379362 +vt 0.787537 0.380823 +vt 0.978466 0.543739 +vt 0.978302 0.561875 +vt 0.968186 0.559004 +vt 0.978138 0.580011 +vt 0.967859 0.573771 +vt 0.988418 0.543242 +vt 0.988418 0.564746 +vt 0.988418 0.586251 +vt 0.968514 0.544237 +vt 0.958071 0.556133 +vt 0.958562 0.544735 +vt 0.947955 0.553261 +vt 0.957579 0.567530 +vt 0.947299 0.561290 +vt 0.959053 0.533337 +vt 0.968842 0.529470 +vt 0.959545 0.521940 +vt 0.948610 0.545233 +vt 0.949265 0.537204 +vt 0.949920 0.529176 +vt 0.978630 0.525604 +vt 0.988418 0.521737 +vt 0.969169 0.514704 +vt 0.978794 0.507468 +vt 0.988418 0.500232 +vt 0.838938 0.966480 +vt 0.839217 0.979901 +vt 0.830458 0.979739 +vt 0.830039 0.969495 +vt 0.821698 0.979577 +vt 0.839496 0.993322 +vt 0.830876 0.989983 +vt 0.822256 0.986645 +vt 0.829621 0.959250 +vt 0.838659 0.953059 +vt 0.829203 0.949006 +vt 0.821141 0.972509 +vt 0.820583 0.965441 +vt 0.820025 0.958373 +vt 0.847698 0.946869 +vt 0.847837 0.963466 +vt 0.856736 0.940678 +vt 0.856736 0.960452 +vt 0.838381 0.939638 +vt 0.847558 0.930271 +vt 0.856736 0.920904 +vt 0.847977 0.980064 +vt 0.848116 0.996661 +vt 0.856736 0.980226 +vt 0.856736 1.000000 +vt 0.761910 0.760900 +vt 0.763715 0.743906 +vt 0.768397 0.743585 +vt 0.767494 0.761343 +vt 0.773079 0.743264 +vt 0.765520 0.726912 +vt 0.769299 0.725827 +vt 0.773079 0.724742 +vt 0.766592 0.779101 +vt 0.760104 0.777894 +vt 0.765689 0.796859 +vt 0.758299 0.794887 +vt 0.773079 0.761786 +vt 0.773079 0.780308 +vt 0.773079 0.798830 +vt 0.753617 0.776686 +vt 0.756325 0.760457 +vt 0.747130 0.775479 +vt 0.750910 0.792916 +vt 0.743520 0.790945 +vt 0.759033 0.744227 +vt 0.761740 0.727997 +vt 0.750740 0.760013 +vt 0.754351 0.744548 +vt 0.757961 0.729082 +vt 0.180780 0.449421 +vt 0.184377 0.455171 +vt 0.168834 0.461921 +vt 0.188349 0.460990 +vt 0.172898 0.469394 +vt 0.166271 0.454721 +vt 0.166709 0.448067 +vt 0.177933 0.443806 +vt 0.168647 0.441685 +vt 0.175462 0.438260 +vt 0.190864 0.439239 +vt 0.196563 0.444122 +vt 0.204646 0.434520 +vt 0.185164 0.434357 +vt 0.196309 0.430216 +vt 0.202263 0.449004 +vt 0.207963 0.453886 +vt 0.212984 0.438823 +vt 0.221321 0.443127 +vt 0.229659 0.447430 +vt 0.718066 0.270690 +vt 0.720908 0.272341 +vt 0.716232 0.276791 +vt 0.711084 0.281127 +vt 0.708330 0.275983 +vt 0.724293 0.274966 +vt 0.719572 0.280164 +vt 0.714756 0.285539 +vt 0.713873 0.272902 +vt 0.713018 0.268183 +vt 0.712914 0.263050 +vt 0.720943 0.262291 +vt 0.706187 0.270353 +vt 0.704349 0.264478 +vt 0.719339 0.266969 +vt 0.724641 0.267663 +vt 0.728822 0.270123 +vt 0.727902 0.262870 +vt 0.733255 0.265457 +vt 0.729485 0.281157 +vt 0.727332 0.278229 +vt 0.731778 0.274205 +vt 0.733257 0.279725 +vt 0.736467 0.270721 +vt 0.733002 0.286501 +vt 0.727791 0.285659 +vt 0.731881 0.293904 +vt 0.738072 0.277992 +vt 0.738608 0.286601 +vt 0.738608 0.295880 +vt 0.723369 0.283333 +vt 0.725459 0.291684 +vt 0.719649 0.288977 +vt 0.811988 0.061247 +vt 0.803241 0.064753 +vt 0.807914 0.052594 +vt 0.816798 0.047967 +vt 0.813935 0.039474 +vt 0.823054 0.033439 +vt 0.820629 0.025875 +vt 0.832659 0.026513 +vt 0.826018 0.042778 +vt 0.842506 0.019142 +vt 0.835406 0.037309 +vt 0.830034 0.018287 +vt 0.840090 0.009455 +vt 0.850473 0.000000 +vt 0.820959 0.057457 +vt 0.830042 0.053524 +vt 0.046433 0.497263 +vt 0.032567 0.505149 +vt 0.034812 0.490180 +vt 0.018168 0.513741 +vt 0.021477 0.496657 +vt 0.045833 0.509814 +vt 0.031235 0.518764 +vt 0.016072 0.528560 +vt 0.047640 0.484158 +vt 0.037667 0.474310 +vt 0.049185 0.470616 +vt 0.040827 0.457989 +vt 0.025594 0.478063 +vt 0.030116 0.458714 +vt 0.059593 0.467041 +vt 0.059453 0.479047 +vt 0.050899 0.456855 +vt 0.059693 0.454905 +vt 0.059235 0.490793 +vt 0.059303 0.502554 +vt 0.861977 0.618268 +vt 0.871596 0.614304 +vt 0.877312 0.628276 +vt 0.869226 0.631150 +vt 0.883139 0.642822 +vt 0.881216 0.610341 +vt 0.885397 0.625401 +vt 0.889673 0.641181 +vt 0.861140 0.634025 +vt 0.852357 0.622232 +vt 0.853054 0.636899 +vt 0.842737 0.626196 +vt 0.876605 0.644464 +vt 0.870071 0.646106 +vt 0.863537 0.647748 +vt 0.843869 0.611017 +vt 0.854987 0.606250 +vt 0.832752 0.615783 +vt 0.866104 0.601484 +vt 0.877222 0.596717 +vt 0.046865 0.533116 +vt 0.061066 0.526909 +vt 0.062113 0.539422 +vt 0.047696 0.544298 +vt 0.062837 0.552104 +vt 0.032193 0.550735 +vt 0.031638 0.541072 +vt 0.016147 0.557953 +vt 0.015898 0.549902 +vt 0.048434 0.555515 +vt 0.033116 0.560293 +vt 0.015506 0.565276 +vt 0.031122 0.530577 +vt 0.046110 0.521693 +vt 0.015594 0.540359 +vt 0.060021 0.514607 +vt 0.373178 0.289459 +vt 0.378967 0.297755 +vt 0.363010 0.296217 +vt 0.357948 0.288255 +vt 0.346438 0.293886 +vt 0.384614 0.306065 +vt 0.368071 0.304180 +vt 0.350885 0.301482 +vt 0.352886 0.280292 +vt 0.367103 0.281193 +vt 0.347825 0.272329 +vt 0.360886 0.272941 +vt 0.341991 0.286290 +vt 0.337544 0.278693 +vt 0.333097 0.271097 +vt 0.379070 0.280699 +vt 0.386952 0.289143 +vt 0.370617 0.272315 +vt 0.393696 0.297704 +vt 0.399870 0.306325 +vt 0.048573 0.578861 +vt 0.062189 0.577790 +vt 0.060506 0.590659 +vt 0.046895 0.590955 +vt 0.057710 0.603456 +vt 0.033303 0.592928 +vt 0.035358 0.581330 +vt 0.022172 0.595043 +vt 0.024231 0.584459 +vt 0.044087 0.603124 +vt 0.029908 0.604605 +vt 0.013992 0.609142 +vt 0.034733 0.570471 +vt 0.048906 0.567042 +vt 0.022815 0.574872 +vt 0.062914 0.564916 +vt 0.787418 0.954758 +vt 0.786473 0.940555 +vt 0.797657 0.945915 +vt 0.785528 0.926353 +vt 0.797027 0.931802 +vt 0.776549 0.949486 +vt 0.775289 0.935195 +vt 0.774029 0.920904 +vt 0.798287 0.960029 +vt 0.808841 0.951275 +vt 0.809156 0.965300 +vt 0.820025 0.956635 +vt 0.808526 0.937251 +vt 0.820025 0.942700 +vt 0.809471 0.979325 +vt 0.798917 0.974142 +vt 0.809786 0.993349 +vt 0.799547 0.988256 +vt 0.820025 0.970571 +vt 0.820025 0.984507 +vt 0.820025 0.998443 +vt 0.788363 0.968960 +vt 0.777809 0.963778 +vt 0.789308 0.983163 +vt 0.779069 0.978069 +vt 0.040368 0.615171 +vt 0.053647 0.616113 +vt 0.048633 0.628666 +vt 0.035874 0.627262 +vt 0.042985 0.641150 +vt 0.030742 0.639559 +vt 0.037020 0.653598 +vt 0.018270 0.638870 +vt 0.022756 0.626892 +vt 0.005684 0.638631 +vt 0.009459 0.627041 +vt 0.025291 0.651961 +vt 0.013420 0.651239 +vt 0.001479 0.650976 +vt 0.026514 0.615698 +vt 0.012372 0.616959 +vt 0.112874 0.236131 +vt 0.099375 0.242354 +vt 0.094231 0.220300 +vt 0.117933 0.254618 +vt 0.105196 0.261771 +vt 0.107418 0.215628 +vt 0.089697 0.197586 +vt 0.102504 0.194158 +vt 0.086347 0.174923 +vt 0.114934 0.190485 +vt 0.120672 0.211002 +vt 0.127135 0.186722 +vt 0.133933 0.206086 +vt 0.098842 0.172292 +vt 0.110677 0.169554 +vt 0.122084 0.166980 +vt 0.127147 0.231110 +vt 0.132061 0.250160 +vt 0.141716 0.225647 +vt 0.149989 0.248023 +vt 0.707704 0.157354 +vt 0.726669 0.157554 +vt 0.727481 0.170197 +vt 0.745600 0.157930 +vt 0.747334 0.169067 +vt 0.708142 0.143134 +vt 0.726336 0.145040 +vt 0.744070 0.147229 +vt 0.707870 0.171394 +vt 0.728310 0.183230 +vt 0.707504 0.185469 +vt 0.748919 0.181024 +vt 0.686305 0.187775 +vt 0.688745 0.172727 +vt 0.667370 0.189151 +vt 0.671366 0.174309 +vt 0.688671 0.157506 +vt 0.689023 0.141791 +vt 0.670753 0.159405 +vt 0.666522 0.141774 +vt 0.171403 0.188244 +vt 0.183305 0.200986 +vt 0.170048 0.209530 +vt 0.194858 0.214263 +vt 0.181026 0.181875 +vt 0.193859 0.192222 +vt 0.156101 0.218102 +vt 0.147139 0.200545 +vt 0.180505 0.224535 +vt 0.165950 0.235034 +vt 0.139258 0.183024 +vt 0.150830 0.178940 +vt 0.133299 0.164839 +vt 0.144020 0.162396 +vt 0.159795 0.194544 +vt 0.161378 0.174021 +vt 0.170429 0.169387 +vt 0.153950 0.158915 +vt 0.162673 0.151630 +vt 0.728272 0.120005 +vt 0.726949 0.132397 +vt 0.710323 0.128522 +vt 0.742609 0.126032 +vt 0.743098 0.136578 +vt 0.692744 0.125257 +vt 0.698574 0.107884 +vt 0.674872 0.121795 +vt 0.682459 0.102779 +vt 0.705251 0.089650 +vt 0.717637 0.099900 +vt 0.711634 0.075147 +vt 0.721659 0.087933 +vt 0.690830 0.081759 +vt 0.713679 0.113962 +vt 0.730070 0.108248 +vt 0.742526 0.115645 +vt 0.732110 0.097506 +vt 0.742773 0.105473 +vt 0.198446 0.255792 +vt 0.210982 0.246557 +vt 0.217035 0.261759 +vt 0.205244 0.270612 +vt 0.192965 0.281001 +vt 0.186059 0.265766 +vt 0.181364 0.291172 +vt 0.174544 0.276414 +vt 0.176495 0.250683 +vt 0.190085 0.240195 +vt 0.164495 0.262615 +vt 0.203837 0.230089 +vt 0.646371 0.097649 +vt 0.664837 0.099653 +vt 0.657366 0.117326 +vt 0.639955 0.112785 +vt 0.648701 0.133188 +vt 0.622366 0.109104 +vt 0.627724 0.095905 +vt 0.604688 0.105853 +vt 0.608987 0.094292 +vt 0.632795 0.127436 +vt 0.617016 0.122795 +vt 0.600083 0.117782 +vt 0.632297 0.082629 +vt 0.651997 0.082043 +vt 0.612894 0.082731 +vt 0.672293 0.080488 +vt 0.155456 0.299464 +vt 0.164622 0.287674 +vt 0.171605 0.299368 +vt 0.162060 0.307064 +vt 0.177525 0.311601 +vt 0.151101 0.315730 +vt 0.146207 0.311702 +vt 0.139436 0.324881 +vt 0.136917 0.324164 +vt 0.168343 0.315071 +vt 0.155956 0.320005 +vt 0.141966 0.325670 +vt 0.141231 0.308168 +vt 0.148210 0.292679 +vt 0.135279 0.303727 +vt 0.134417 0.323589 +vt 0.155515 0.277055 +vt 0.140188 0.287101 +vt 0.144830 0.269626 +vt 0.616654 0.155289 +vt 0.625982 0.159500 +vt 0.615382 0.171909 +vt 0.608211 0.168603 +vt 0.605323 0.184234 +vt 0.634816 0.165049 +vt 0.623455 0.176176 +vt 0.613236 0.187488 +vt 0.601964 0.166270 +vt 0.607769 0.152369 +vt 0.596666 0.164922 +vt 0.600165 0.151397 +vt 0.599689 0.181722 +vt 0.595606 0.179711 +vt 0.592348 0.177954 +vt 0.612468 0.137547 +vt 0.624938 0.141586 +vt 0.602025 0.136887 +vt 0.637665 0.146919 +vt 0.648458 0.154291 +vt 0.131257 0.283117 +vt 0.127452 0.296977 +vt 0.119147 0.287924 +vt 0.123991 0.280362 +vt 0.111757 0.276575 +vt 0.121656 0.270037 +vt 0.133112 0.267504 +vt 0.647254 0.178076 +vt 0.642213 0.171986 +vt 0.655131 0.164444 +vt 0.656992 0.176255 +vt 0.653354 0.188599 +vt 0.642339 0.186039 +vt 0.646966 0.201209 +vt 0.632407 0.181392 +vt 0.636072 0.196469 +vt 0.624157 0.191731 +vt 0.842099 0.286932 +vt 0.843333 0.277250 +vt 0.844886 0.286932 +vt 0.844776 0.268137 +vt 0.846593 0.281806 +vt 0.840223 0.281806 +vt 0.841591 0.268137 +vt 0.842959 0.254469 +vt 0.843558 0.290918 +vt 0.846059 0.297753 +vt 0.843689 0.298892 +vt 0.847043 0.309143 +vt 0.848411 0.295474 +vt 0.850228 0.309143 +vt 0.841282 0.297753 +vt 0.838855 0.295474 +vt 0.843858 0.309143 +vt 0.840673 0.309143 +vt 0.837487 0.309143 +vt 0.591531 0.155663 +vt 0.594674 0.153029 +vt 0.592338 0.164569 +vt 0.589042 0.163745 +vt 0.589184 0.176209 +vt 0.586838 0.160980 +vt 0.590866 0.150277 +vt 0.585179 0.157245 +vt 0.586093 0.174472 +vt 0.583049 0.172740 +vt 0.580029 0.171011 +vt 0.595376 0.141688 +vt 0.590275 0.143662 +vt 0.595261 0.130446 +vt 0.184270 0.054485 +vt 0.192126 0.060516 +vt 0.177668 0.071438 +vt 0.200074 0.065406 +vt 0.185052 0.076772 +vt 0.199340 0.044492 +vt 0.207306 0.050649 +vt 0.215616 0.055198 +vt 0.169960 0.065254 +vt 0.164653 0.084470 +vt 0.154812 0.098714 +vt 0.147257 0.091832 +vt 0.171072 0.090455 +vt 0.158894 0.110345 +vt 0.157174 0.077575 +vt 0.148391 0.071325 +vt 0.161983 0.058831 +vt 0.139705 0.064851 +vt 0.137713 0.086284 +vt 0.124903 0.080969 +vt 0.176629 0.047721 +vt 0.191804 0.037303 +vt 0.154201 0.052176 +vt 0.169332 0.040634 +vt 0.184781 0.029659 +vt 0.471454 0.408756 +vt 0.455429 0.398663 +vt 0.462466 0.388144 +vt 0.441698 0.387192 +vt 0.465533 0.419643 +vt 0.448527 0.409159 +vt 0.430482 0.396737 +vt 0.477795 0.398174 +vt 0.468064 0.376778 +vt 0.484277 0.387916 +vt 0.472818 0.366206 +vt 0.449639 0.377301 +vt 0.455300 0.365618 +vt 0.456401 0.347788 +vt 0.502532 0.397311 +vt 0.494426 0.407339 +vt 0.490505 0.378746 +vt 0.510460 0.389011 +vt 0.488100 0.418224 +vt 0.482878 0.429606 +vt 0.133220 0.031504 +vt 0.149782 0.019806 +vt 0.156026 0.026698 +vt 0.166701 0.007339 +vt 0.172379 0.014708 +vt 0.126150 0.024710 +vt 0.143656 0.012935 +vt 0.161172 0.000000 +vt 0.140220 0.038348 +vt 0.162503 0.033634 +vt 0.147080 0.045293 +vt 0.178356 0.022138 +vt 0.132516 0.057279 +vt 0.125508 0.049315 +vt 0.119303 0.068236 +vt 0.112789 0.059385 +vt 0.117368 0.041662 +vt 0.108662 0.034165 +vt 0.103927 0.051538 +vt 0.438069 0.432171 +vt 0.443332 0.420456 +vt 0.460311 0.430818 +vt 0.423565 0.422784 +vt 0.427936 0.411441 +vt 0.454761 0.442597 +vt 0.478084 0.441123 +vt 0.472876 0.453348 +vt 0.466413 0.466859 +vt 0.447854 0.455297 +vt 0.430965 0.443918 +vt 0.416512 0.433197 +vt 0.148625 0.128191 +vt 0.149841 0.143522 +vt 0.140235 0.144588 +vt 0.157518 0.130353 +vt 0.159185 0.142273 +vt 0.139428 0.126325 +vt 0.130103 0.145292 +vt 0.119541 0.146280 +vt 0.119259 0.125054 +vt 0.129622 0.125051 +vt 0.131808 0.104788 +vt 0.141555 0.108411 +vt 0.120993 0.103734 +vt 0.149876 0.113271 +vt 0.157483 0.118751 +vt 0.440513 0.365696 +vt 0.431932 0.373589 +vt 0.424725 0.358958 +vt 0.421706 0.378608 +vt 0.433844 0.353333 +vt 0.418669 0.344402 +vt 0.428390 0.340218 +vt 0.413189 0.329884 +vt 0.414786 0.362911 +vt 0.408567 0.347616 +vt 0.402698 0.332524 +vt 0.437857 0.335386 +vt 0.442265 0.346660 +vt 0.447198 0.330232 +vt 0.450336 0.339465 +vt 0.423544 0.326726 +vt 0.433809 0.323224 +vt 0.444028 0.319550 +vt 0.447389 0.356158 +vt 0.453414 0.345799 +vt 0.097142 0.150600 +vt 0.084752 0.153022 +vt 0.084447 0.132481 +vt 0.096848 0.129850 +vt 0.084965 0.113900 +vt 0.097406 0.110810 +vt 0.085841 0.097877 +vt 0.109470 0.106581 +vt 0.108393 0.127014 +vt 0.097891 0.094652 +vt 0.109958 0.089110 +vt 0.108646 0.148201 +vt 0.403905 0.364564 +vt 0.409892 0.379521 +vt 0.397239 0.379212 +vt 0.414149 0.393395 +vt 0.401196 0.391724 +vt 0.392163 0.365487 +vt 0.384498 0.380569 +vt 0.379639 0.367247 +vt 0.388810 0.392952 +vt 0.374414 0.353298 +vt 0.386639 0.351115 +vt 0.369007 0.339036 +vt 0.380890 0.336420 +vt 0.397956 0.349537 +vt 0.392026 0.334476 +vt 0.096455 0.074912 +vt 0.088963 0.073711 +vt 0.094597 0.062387 +vt 0.102963 0.068345 +vt 0.107936 0.076809 +vt 0.097382 0.082549 +vt 0.086609 0.085011 +vt 0.404199 0.410489 +vt 0.397216 0.414213 +vt 0.392391 0.404081 +vt 0.403364 0.402454 +vt 0.414913 0.405174 +vt 0.412008 0.415148 +vt 0.405259 0.423607 +vt 0.441016 0.756210 +vt 0.438001 0.758174 +vt 0.436801 0.748290 +vt 0.435053 0.760312 +vt 0.433754 0.749869 +vt 0.443044 0.766370 +vt 0.440152 0.768897 +vt 0.437336 0.771612 +vt 0.439906 0.746868 +vt 0.436418 0.739250 +vt 0.439574 0.738322 +vt 0.436721 0.731062 +vt 0.433314 0.740322 +vt 0.433607 0.731711 +vt 0.442832 0.737681 +vt 0.443126 0.745763 +vt 0.446243 0.737471 +vt 0.446519 0.745132 +vt 0.439883 0.730547 +vt 0.443143 0.730298 +vt 0.446547 0.730448 +vt 0.444164 0.754592 +vt 0.446088 0.764216 +vt 0.447511 0.753494 +vt 0.449360 0.762623 +vt 0.509914 0.954951 +vt 0.509694 0.960808 +vt 0.506767 0.959121 +vt 0.509463 0.966696 +vt 0.506521 0.965320 +vt 0.512729 0.957048 +vt 0.512621 0.962495 +vt 0.512406 0.968072 +vt 0.507099 0.952853 +vt 0.503840 0.957433 +vt 0.504284 0.950755 +vt 0.500913 0.955746 +vt 0.503578 0.963944 +vt 0.500636 0.962568 +vt 0.504849 0.943965 +vt 0.507488 0.946539 +vt 0.505475 0.937119 +vt 0.507905 0.940203 +vt 0.501469 0.948657 +vt 0.502211 0.941391 +vt 0.503045 0.934035 +vt 0.510126 0.949114 +vt 0.512765 0.951688 +vt 0.510335 0.943287 +vt 0.512765 0.946371 +vt 0.450484 0.788280 +vt 0.447895 0.791829 +vt 0.443385 0.780453 +vt 0.446129 0.777371 +vt 0.445447 0.795630 +vt 0.440726 0.783731 +vt 0.449040 0.774684 +vt 0.453355 0.785237 +vt 0.452203 0.772586 +vt 0.456413 0.782838 +vt 0.459367 0.795120 +vt 0.456325 0.798163 +vt 0.466556 0.802682 +vt 0.462359 0.792831 +vt 0.471766 0.802188 +vt 0.453876 0.802014 +vt 0.451724 0.806270 +vt 0.463651 0.805862 +vt 0.461522 0.809998 +vt 0.459781 0.814611 +vt 0.142240 0.919521 +vt 0.142425 0.929397 +vt 0.139553 0.928560 +vt 0.139275 0.919017 +vt 0.136681 0.927722 +vt 0.142610 0.939273 +vt 0.139830 0.938103 +vt 0.137051 0.936933 +vt 0.138997 0.909474 +vt 0.142055 0.909646 +vt 0.138720 0.899931 +vt 0.141870 0.899770 +vt 0.136310 0.918512 +vt 0.135940 0.909302 +vt 0.135570 0.900092 +vt 0.145112 0.909817 +vt 0.145204 0.920026 +vt 0.148169 0.909989 +vt 0.145019 0.899609 +vt 0.148169 0.899448 +vt 0.145297 0.930235 +vt 0.145389 0.940443 +vt 0.148169 0.920531 +vt 0.148169 0.931072 +vt 0.148169 0.941613 +vt 0.482443 0.811636 +vt 0.481846 0.816519 +vt 0.471027 0.814767 +vt 0.472464 0.810219 +vt 0.481311 0.821478 +vt 0.469840 0.819616 +vt 0.474404 0.806274 +vt 0.483167 0.806903 +vt 0.476595 0.802631 +vt 0.493101 0.805574 +vt 0.493268 0.810512 +vt 0.503505 0.802189 +vt 0.504378 0.806972 +vt 0.483952 0.802247 +vt 0.492934 0.800637 +vt 0.502633 0.797405 +vt 0.493435 0.815450 +vt 0.493602 0.820387 +vt 0.505250 0.811755 +vt 0.506123 0.816539 +vt 0.456479 0.857879 +vt 0.454130 0.861392 +vt 0.448902 0.852796 +vt 0.451906 0.864801 +vt 0.447137 0.856707 +vt 0.460928 0.868248 +vt 0.458183 0.871191 +vt 0.455607 0.874022 +vt 0.450728 0.848793 +vt 0.442892 0.845001 +vt 0.436490 0.837608 +vt 0.437056 0.832757 +vt 0.441656 0.849364 +vt 0.435818 0.842397 +vt 0.444109 0.840561 +vt 0.445314 0.836069 +vt 0.452594 0.844728 +vt 0.446513 0.831552 +vt 0.437552 0.827865 +vt 0.438013 0.822953 +vt 0.458912 0.854297 +vt 0.463785 0.865230 +vt 0.454481 0.840633 +vt 0.461386 0.850680 +vt 0.466699 0.862174 +vt 0.527343 0.797234 +vt 0.516747 0.805632 +vt 0.515213 0.801140 +vt 0.529471 0.801303 +vt 0.518281 0.810124 +vt 0.525215 0.793165 +vt 0.513678 0.796648 +vt 0.523086 0.789097 +vt 0.512144 0.792156 +vt 0.531194 0.779679 +vt 0.533826 0.783197 +vt 0.537469 0.768539 +vt 0.540489 0.771385 +vt 0.520958 0.785028 +vt 0.528562 0.776160 +vt 0.534448 0.765692 +vt 0.536458 0.786716 +vt 0.539090 0.790234 +vt 0.543510 0.774232 +vt 0.546531 0.777078 +vt 0.465532 0.908235 +vt 0.462271 0.909940 +vt 0.461922 0.895532 +vt 0.459093 0.911133 +vt 0.458967 0.897079 +vt 0.465580 0.923211 +vt 0.462168 0.925085 +vt 0.458520 0.928860 +vt 0.465035 0.893770 +vt 0.460670 0.882593 +vt 0.463640 0.880326 +vt 0.457882 0.884745 +vt 0.466733 0.877982 +vt 0.468236 0.892099 +vt 0.469886 0.875600 +vt 0.471479 0.890474 +vt 0.468776 0.907125 +vt 0.468835 0.922606 +vt 0.472011 0.906313 +vt 0.472011 0.922635 +vt 0.549924 0.744239 +vt 0.547918 0.759936 +vt 0.544647 0.757878 +vt 0.553316 0.745420 +vt 0.551190 0.761994 +vt 0.546561 0.743199 +vt 0.541375 0.755821 +vt 0.543253 0.742440 +vt 0.538104 0.753763 +vt 0.543441 0.729311 +vt 0.546493 0.727869 +vt 0.541886 0.715160 +vt 0.539974 0.741821 +vt 0.540502 0.731316 +vt 0.540132 0.723696 +vt 0.549769 0.727551 +vt 0.553157 0.727795 +vt 0.544608 0.711863 +vt 0.547695 0.710282 +vt 0.550963 0.709560 +vt 0.926520 0.401888 +vt 0.929630 0.401255 +vt 0.927910 0.416079 +vt 0.932515 0.400785 +vt 0.929453 0.386029 +vt 0.932682 0.386354 +vt 0.935657 0.386910 +vt 0.925149 0.417733 +vt 0.927076 0.430853 +vt 0.924972 0.433632 +vt 0.926687 0.445601 +vt 0.930581 0.414574 +vt 0.929454 0.428305 +vt 0.928729 0.442008 +vt 0.923415 0.436874 +vt 0.922209 0.419681 +vt 0.921095 0.439738 +vt 0.918606 0.421803 +vt 0.925393 0.449559 +vt 0.925597 0.454247 +vt 0.928048 0.460030 +vt 0.922958 0.402849 +vt 0.925716 0.386171 +vt 0.918718 0.404300 +vt 0.921219 0.387010 +vt 0.538963 0.676043 +vt 0.543943 0.692843 +vt 0.541070 0.695155 +vt 0.541710 0.673382 +vt 0.546983 0.691153 +vt 0.538532 0.698713 +vt 0.533738 0.682012 +vt 0.536445 0.702221 +vt 0.531611 0.685386 +vt 0.536283 0.678865 +vt 0.527860 0.667099 +vt 0.530650 0.664111 +vt 0.521834 0.655232 +vt 0.525627 0.670326 +vt 0.517199 0.655926 +vt 0.533204 0.660691 +vt 0.535639 0.657055 +vt 0.524722 0.651813 +vt 0.527117 0.647596 +vt 0.529264 0.642980 +vt 0.940622 0.354083 +vt 0.947895 0.338036 +vt 0.950709 0.341139 +vt 0.943665 0.356270 +vt 0.953146 0.344714 +vt 0.955649 0.321969 +vt 0.958197 0.325983 +vt 0.960318 0.330553 +vt 0.937508 0.371350 +vt 0.934314 0.370087 +vt 0.946376 0.358846 +vt 0.940412 0.372921 +vt 0.930541 0.369440 +vt 0.936915 0.352671 +vt 0.925899 0.369718 +vt 0.932212 0.352424 +vt 0.944323 0.335875 +vt 0.952246 0.319066 +vt 0.939615 0.335128 +vt 0.947563 0.317832 +vt 0.529131 0.692717 +vt 0.530185 0.688892 +vt 0.534922 0.704384 +vt 0.533680 0.706478 +vt 0.539307 0.720408 +vt 0.532436 0.709775 +vt 0.528122 0.697048 +vt 0.531190 0.713673 +vt 0.527136 0.701632 +vt 0.538142 0.720371 +vt 0.536749 0.722501 +vt 0.535243 0.725714 +vt 0.523809 0.684321 +vt 0.524405 0.679221 +vt 0.519495 0.671595 +vt 0.523083 0.689591 +vt 0.519029 0.677550 +vt 0.524743 0.674462 +vt 0.519591 0.665859 +vt 0.518949 0.660563 +vt 0.020614 0.959202 +vt 0.019886 0.969357 +vt 0.017289 0.965094 +vt 0.017785 0.955443 +vt 0.014693 0.960831 +vt 0.018118 0.977167 +vt 0.015827 0.972355 +vt 0.013537 0.967543 +vt 0.017637 0.944198 +vt 0.020647 0.947483 +vt 0.017166 0.932157 +vt 0.020333 0.934982 +vt 0.014957 0.951684 +vt 0.014627 0.940914 +vt 0.013999 0.929331 +vt 0.023657 0.950768 +vt 0.023442 0.962961 +vt 0.026668 0.954053 +vt 0.023501 0.937808 +vt 0.026668 0.940633 +vt 0.022483 0.973620 +vt 0.020408 0.981979 +vt 0.026271 0.966720 +vt 0.025080 0.977883 +vt 0.022699 0.986791 +vt 0.978497 0.913738 +vt 0.981374 0.911955 +vt 0.980201 0.924171 +vt 0.977355 0.925611 +vt 0.979750 0.936092 +vt 0.984249 0.910216 +vt 0.982960 0.923073 +vt 0.982444 0.935649 +vt 0.974334 0.927737 +vt 0.975616 0.915608 +vt 0.971463 0.929562 +vt 0.972830 0.917277 +vt 0.976907 0.937076 +vt 0.973767 0.939143 +vt 0.969638 0.944707 +vt 0.977065 0.903959 +vt 0.980026 0.901832 +vt 0.978599 0.892550 +vt 0.974169 0.905841 +vt 0.975494 0.894829 +vt 0.983029 0.899542 +vt 0.986054 0.897171 +vt 0.981747 0.889910 +vt 0.984924 0.887031 +vt 0.988116 0.884032 +vt 0.445750 0.939593 +vt 0.441834 0.948769 +vt 0.439748 0.946722 +vt 0.443512 0.937952 +vt 0.437662 0.944674 +vt 0.437682 0.957909 +vt 0.435737 0.955374 +vt 0.433792 0.952840 +vt 0.446780 0.928949 +vt 0.449195 0.930346 +vt 0.449306 0.919595 +vt 0.451834 0.920582 +vt 0.441300 0.936413 +vt 0.444471 0.927958 +vt 0.446945 0.919213 +vt 0.451824 0.932556 +vt 0.448042 0.941438 +vt 0.454558 0.935171 +vt 0.454698 0.922776 +vt 0.443920 0.950816 +vt 0.439628 0.960443 +vt 0.450361 0.943383 +vt 0.446006 0.952863 +vt 0.441573 0.962978 +vt 0.981495 0.958853 +vt 0.983211 0.969890 +vt 0.980368 0.968280 +vt 0.985167 0.980828 +vt 0.982217 0.978519 +vt 0.984249 0.959769 +vt 0.986054 0.971500 +vt 0.988116 0.983137 +vt 0.978717 0.958029 +vt 0.977525 0.966670 +vt 0.975887 0.957388 +vt 0.974682 0.965060 +vt 0.979267 0.976210 +vt 0.976318 0.973901 +vt 0.974459 0.948619 +vt 0.977462 0.947755 +vt 0.973032 0.956837 +vt 0.971355 0.949846 +vt 0.980261 0.947620 +vt 0.982960 0.947850 +vt 0.453332 0.909853 +vt 0.450841 0.909774 +vt 0.451335 0.899910 +vt 0.448486 0.910081 +vt 0.449043 0.900948 +vt 0.453708 0.898940 +vt 0.450738 0.890431 +vt 0.452978 0.888625 +vt 0.449000 0.881763 +vt 0.448560 0.892199 +vt 0.446985 0.884218 +vt 0.455339 0.886743 +vt 0.456240 0.898106 +vt 0.451071 0.879270 +vt 0.453255 0.876702 +vt 0.456098 0.910705 +vt 0.393245 0.963437 +vt 0.393159 0.972235 +vt 0.389481 0.969422 +vt 0.393074 0.981033 +vt 0.389310 0.977635 +vt 0.396837 0.965665 +vt 0.396837 0.975048 +vt 0.396837 0.984432 +vt 0.389653 0.961210 +vt 0.385803 0.966609 +vt 0.386060 0.958982 +vt 0.382125 0.963796 +vt 0.385546 0.974236 +vt 0.381782 0.970838 +vt 0.386317 0.951355 +vt 0.389824 0.952997 +vt 0.386574 0.943727 +vt 0.389995 0.944784 +vt 0.382468 0.956754 +vt 0.382811 0.949713 +vt 0.383153 0.942671 +vt 0.393331 0.954639 +vt 0.396837 0.956281 +vt 0.393416 0.945841 +vt 0.396837 0.946898 +vt 0.447917 0.871235 +vt 0.446069 0.874330 +vt 0.442245 0.867825 +vt 0.444263 0.877391 +vt 0.440681 0.871428 +vt 0.443828 0.864191 +vt 0.437824 0.861938 +vt 0.439116 0.857807 +vt 0.433105 0.856360 +vt 0.436526 0.866043 +vt 0.432084 0.860946 +vt 0.440395 0.853624 +vt 0.445453 0.860495 +vt 0.434091 0.851753 +vt 0.435007 0.847106 +vt 0.449849 0.868070 +vt 0.459564 0.781220 +vt 0.455702 0.771277 +vt 0.459453 0.770558 +vt 0.452935 0.761779 +vt 0.456738 0.761496 +vt 0.462948 0.780156 +vt 0.463371 0.770232 +vt 0.466702 0.779420 +vt 0.467373 0.770104 +vt 0.460693 0.761587 +vt 0.464723 0.761865 +vt 0.470468 0.788970 +vt 0.467089 0.790059 +vt 0.474452 0.798700 +vt 0.471553 0.800114 +vt 0.470642 0.778848 +vt 0.474323 0.787928 +vt 0.478210 0.797177 +vt 0.464659 0.791246 +vt 0.470371 0.801313 +vt 0.924083 0.278608 +vt 0.919397 0.279920 +vt 0.918217 0.269619 +vt 0.915320 0.281479 +vt 0.914128 0.271497 +vt 0.925251 0.289567 +vt 0.920574 0.290466 +vt 0.916522 0.291692 +vt 0.922900 0.267910 +vt 0.917035 0.259807 +vt 0.921689 0.257734 +vt 0.915848 0.250728 +vt 0.912953 0.261972 +vt 0.911804 0.253136 +vt 0.926725 0.255724 +vt 0.927979 0.266314 +vt 0.931951 0.253746 +vt 0.920435 0.248342 +vt 0.925383 0.245971 +vt 0.930513 0.243607 +vt 0.929175 0.277461 +vt 0.930343 0.288887 +vt 0.933256 0.264775 +vt 0.934471 0.276397 +vt 0.935643 0.288316 +vt 0.453939 0.745607 +vt 0.454938 0.753203 +vt 0.451125 0.753089 +vt 0.450143 0.745132 +vt 0.449859 0.737837 +vt 0.453627 0.738634 +vt 0.450145 0.731130 +vt 0.453888 0.732211 +vt 0.457498 0.739718 +vt 0.457852 0.746398 +vt 0.461420 0.740946 +vt 0.457728 0.733558 +vt 0.461616 0.735038 +vt 0.458885 0.753665 +vt 0.461822 0.747346 +vt 0.462898 0.754299 +vt 0.917621 0.232601 +vt 0.913316 0.235429 +vt 0.911695 0.229062 +vt 0.915799 0.226076 +vt 0.909654 0.223451 +vt 0.909467 0.238156 +vt 0.907994 0.231891 +vt 0.906128 0.226361 +vt 0.920173 0.222985 +vt 0.922230 0.229704 +vt 0.924680 0.219841 +vt 0.926991 0.226773 +vt 0.913527 0.220331 +vt 0.917631 0.217071 +vt 0.921851 0.213741 +vt 0.923926 0.237331 +vt 0.919122 0.239993 +vt 0.928898 0.234656 +vt 0.914654 0.242627 +vt 0.910689 0.245218 +vt 0.202486 0.174260 +vt 0.199696 0.182987 +vt 0.187727 0.175667 +vt 0.214113 0.175705 +vt 0.213158 0.186239 +vt 0.177510 0.166154 +vt 0.183235 0.163084 +vt 0.168170 0.155546 +vt 0.174753 0.155496 +vt 0.188217 0.158934 +vt 0.195496 0.163944 +vt 0.192207 0.154563 +vt 0.197587 0.158714 +vt 0.181501 0.152957 +vt 0.187491 0.149406 +vt 0.192289 0.169672 +vt 0.203899 0.167021 +vt 0.213990 0.167395 +vt 0.204295 0.160853 +vt 0.212997 0.160467 +vt 0.731738 0.068245 +vt 0.738261 0.072272 +vt 0.736206 0.079868 +vt 0.744783 0.076299 +vt 0.743965 0.085867 +vt 0.734968 0.063254 +vt 0.740316 0.065026 +vt 0.745664 0.066798 +vt 0.734156 0.088163 +vt 0.725203 0.079662 +vt 0.743274 0.095569 +vt 0.716581 0.068970 +vt 0.720855 0.066775 +vt 0.708126 0.057184 +vt 0.713262 0.059681 +vt 0.728489 0.073595 +vt 0.725215 0.064219 +vt 0.729620 0.061481 +vt 0.718693 0.060192 +vt 0.724272 0.059709 +vt 0.507826 0.631233 +vt 0.509458 0.623644 +vt 0.515274 0.629839 +vt 0.511091 0.616054 +vt 0.517038 0.623244 +vt 0.502155 0.626801 +vt 0.503673 0.618215 +vt 0.505191 0.609629 +vt 0.513467 0.636364 +vt 0.521150 0.637567 +vt 0.519047 0.642894 +vt 0.523080 0.631965 +vt 0.516596 0.647669 +vt 0.511573 0.642752 +vt 0.513971 0.652168 +vt 0.509636 0.649070 +vt 0.506194 0.638822 +vt 0.500636 0.635387 +vt 0.504561 0.646412 +vt 0.499118 0.643973 +vt 0.656298 0.277014 +vt 0.658028 0.269148 +vt 0.663147 0.272574 +vt 0.659759 0.261281 +vt 0.664301 0.263797 +vt 0.650602 0.272677 +vt 0.652909 0.265721 +vt 0.655217 0.258765 +vt 0.661994 0.281352 +vt 0.668266 0.276001 +vt 0.667689 0.285690 +vt 0.673385 0.279428 +vt 0.668843 0.266313 +vt 0.673385 0.268829 +vt 0.667113 0.295378 +vt 0.660840 0.290129 +vt 0.666536 0.305066 +vt 0.659686 0.298907 +vt 0.673385 0.290027 +vt 0.673385 0.300626 +vt 0.673385 0.311226 +vt 0.654567 0.284881 +vt 0.648294 0.279633 +vt 0.652836 0.292748 +vt 0.645986 0.286588 +vt 0.242042 0.166956 +vt 0.257378 0.159223 +vt 0.258181 0.169178 +vt 0.243093 0.177125 +vt 0.273090 0.150576 +vt 0.273193 0.160469 +vt 0.227854 0.183549 +vt 0.227459 0.172860 +vt 0.226331 0.164298 +vt 0.240533 0.158505 +vt 0.224359 0.157048 +vt 0.256204 0.150792 +vt 0.272610 0.142119 +vt 0.237873 0.151227 +vt 0.253031 0.143641 +vt 0.271193 0.134322 +vt 0.284179 0.433669 +vt 0.279294 0.437459 +vt 0.278377 0.417438 +vt 0.283567 0.414266 +vt 0.277460 0.397417 +vt 0.274409 0.441249 +vt 0.273187 0.420610 +vt 0.271964 0.399970 +vt 0.288758 0.411095 +vt 0.289064 0.429879 +vt 0.293948 0.407923 +vt 0.293948 0.426089 +vt 0.282956 0.394864 +vt 0.288452 0.392310 +vt 0.293948 0.389757 +vt 0.289369 0.448664 +vt 0.284790 0.453072 +vt 0.289675 0.467448 +vt 0.293948 0.444255 +vt 0.293948 0.462422 +vt 0.280211 0.457480 +vt 0.275631 0.461889 +vt 0.285401 0.472475 +vt 0.281127 0.477502 +vt 0.276854 0.482528 +vt 0.441873 0.717002 +vt 0.438845 0.717045 +vt 0.440391 0.710787 +vt 0.443288 0.710750 +vt 0.442075 0.704744 +vt 0.435868 0.717164 +vt 0.437532 0.710742 +vt 0.439348 0.704564 +vt 0.446263 0.710550 +vt 0.445004 0.717108 +vt 0.449511 0.710983 +vt 0.448328 0.717658 +vt 0.444821 0.704633 +vt 0.447607 0.703942 +vt 0.450451 0.702381 +vt 0.443915 0.723565 +vt 0.440694 0.723520 +vt 0.447291 0.723995 +vt 0.437576 0.723732 +vt 0.434509 0.724072 +vt 0.506340 0.977468 +vt 0.506388 0.971428 +vt 0.509217 0.972625 +vt 0.508960 0.978585 +vt 0.512046 0.973822 +vt 0.508695 0.984566 +vt 0.506350 0.983462 +vt 0.508427 0.990556 +vt 0.506388 0.989434 +vt 0.511580 0.979702 +vt 0.511041 0.985669 +vt 0.510466 0.991679 +vt 0.504005 0.982359 +vt 0.503721 0.976351 +vt 0.501659 0.981256 +vt 0.504349 0.988311 +vt 0.502310 0.987189 +vt 0.503559 0.970232 +vt 0.501101 0.975234 +vt 0.500729 0.969035 +vt 0.875834 0.937095 +vt 0.876211 0.932910 +vt 0.882358 0.935892 +vt 0.881863 0.939439 +vt 0.888506 0.938875 +vt 0.876503 0.928614 +vt 0.882842 0.932469 +vt 0.889180 0.936324 +vt 0.881345 0.943233 +vt 0.875285 0.941059 +vt 0.880293 0.946723 +vt 0.874355 0.944524 +vt 0.887915 0.941843 +vt 0.887494 0.945647 +vt 0.887325 0.950703 +vt 0.869403 0.939366 +vt 0.869848 0.934871 +vt 0.863610 0.937912 +vt 0.868613 0.943187 +vt 0.862968 0.942282 +vt 0.870064 0.929927 +vt 0.870164 0.924759 +vt 0.863885 0.932707 +vt 0.863917 0.926945 +vt 0.863826 0.920904 +vt 0.010764 0.984186 +vt 0.005872 0.984959 +vt 0.005000 0.978203 +vt 0.009360 0.978113 +vt 0.004127 0.971446 +vt 0.000633 0.984950 +vt 0.000317 0.977496 +vt 0.000000 0.970042 +vt 0.013077 0.976429 +vt 0.014961 0.981849 +vt 0.007957 0.972039 +vt 0.011193 0.971009 +vt 0.016846 0.987269 +vt 0.012167 0.990259 +vt 0.018730 0.992690 +vt 0.006745 0.991715 +vt 0.000950 0.992404 +vt 0.013570 0.996332 +vt 0.007617 0.998471 +vt 0.001267 0.999858 +vt 0.933854 0.810510 +vt 0.932444 0.812714 +vt 0.928135 0.805350 +vt 0.930378 0.814726 +vt 0.925792 0.807828 +vt 0.937501 0.817996 +vt 0.936898 0.820111 +vt 0.936512 0.822131 +vt 0.930243 0.803032 +vt 0.924119 0.798052 +vt 0.926705 0.795570 +vt 0.920249 0.790787 +vt 0.921599 0.800972 +vt 0.917602 0.794137 +vt 0.929336 0.793379 +vt 0.932306 0.800859 +vt 0.931988 0.791333 +vt 0.934347 0.798759 +vt 0.923204 0.788116 +vt 0.926365 0.785899 +vt 0.929628 0.783908 +vt 0.935277 0.808339 +vt 0.938248 0.815818 +vt 0.936707 0.806184 +vt 0.939066 0.813609 +vt 0.674805 0.969638 +vt 0.675837 0.977549 +vt 0.673576 0.977196 +vt 0.677009 0.985847 +vt 0.674789 0.985705 +vt 0.677108 0.970195 +vt 0.678098 0.977902 +vt 0.679229 0.985988 +vt 0.672501 0.969081 +vt 0.671315 0.976842 +vt 0.670198 0.968524 +vt 0.669055 0.976489 +vt 0.672570 0.985564 +vt 0.670350 0.985422 +vt 0.669352 0.961014 +vt 0.671702 0.961758 +vt 0.668916 0.954715 +vt 0.671317 0.955621 +vt 0.667894 0.967967 +vt 0.667003 0.960270 +vt 0.666516 0.953809 +vt 0.674052 0.962502 +vt 0.676401 0.963246 +vt 0.673718 0.956527 +vt 0.676118 0.957433 +vt 0.920051 0.813328 +vt 0.917106 0.816419 +vt 0.914477 0.812644 +vt 0.911715 0.808720 +vt 0.913438 0.803331 +vt 0.914173 0.819630 +vt 0.912176 0.817020 +vt 0.910095 0.814337 +vt 0.916800 0.808413 +vt 0.919166 0.804474 +vt 0.923022 0.810475 +vt 0.915366 0.798394 +vt 0.926989 0.816321 +vt 0.923083 0.817908 +vt 0.930169 0.821187 +vt 0.919470 0.819900 +vt 0.916004 0.822094 +vt 0.925547 0.821800 +vt 0.921438 0.822938 +vt 0.917584 0.824339 +vt 0.488916 0.952035 +vt 0.485508 0.955353 +vt 0.482525 0.953562 +vt 0.486420 0.949137 +vt 0.479295 0.951784 +vt 0.482844 0.958533 +vt 0.479477 0.957617 +vt 0.475851 0.956717 +vt 0.490880 0.944464 +vt 0.492788 0.948608 +vt 0.495623 0.939668 +vt 0.496891 0.945125 +vt 0.483732 0.946218 +vt 0.488831 0.940232 +vt 0.494262 0.934035 +vt 0.494411 0.952575 +vt 0.491027 0.954895 +vt 0.495608 0.956277 +vt 0.497973 0.950232 +vt 0.498774 0.954813 +vt 0.487999 0.957170 +vt 0.485693 0.959479 +vt 0.492555 0.957726 +vt 0.489726 0.959144 +vt 0.487645 0.960202 +vt 0.216123 0.112994 +vt 0.223360 0.109234 +vt 0.223621 0.112607 +vt 0.230775 0.105496 +vt 0.230911 0.107896 +vt 0.216026 0.109147 +vt 0.223120 0.105967 +vt 0.231990 0.102941 +vt 0.216302 0.117225 +vt 0.224834 0.115917 +vt 0.227487 0.119969 +vt 0.219149 0.126669 +vt 0.232274 0.109729 +vt 0.234743 0.110581 +vt 0.217101 0.121715 +vt 0.208784 0.126732 +vt 0.208926 0.121658 +vt 0.200174 0.131359 +vt 0.201521 0.125998 +vt 0.209905 0.132102 +vt 0.198617 0.136215 +vt 0.209244 0.116794 +vt 0.209445 0.112401 +vt 0.202543 0.120616 +vt 0.203121 0.115693 +vt 0.862791 0.178205 +vt 0.864255 0.173858 +vt 0.871958 0.178702 +vt 0.866497 0.169594 +vt 0.875682 0.174792 +vt 0.856228 0.173542 +vt 0.856723 0.168908 +vt 0.857640 0.164317 +vt 0.869317 0.182750 +vt 0.880002 0.183336 +vt 0.886849 0.187434 +vt 0.881676 0.190960 +vt 0.885526 0.179832 +vt 0.897648 0.184583 +vt 0.875767 0.187059 +vt 0.873194 0.190776 +vt 0.867572 0.186689 +vt 0.871488 0.194201 +vt 0.866241 0.190258 +vt 0.878550 0.194523 +vt 0.876530 0.197837 +vt 0.861771 0.182375 +vt 0.855881 0.177949 +vt 0.860857 0.186110 +vt 0.855405 0.181859 +vt 0.461074 0.705704 +vt 0.457246 0.702772 +vt 0.459899 0.698735 +vt 0.453373 0.699659 +vt 0.456354 0.696066 +vt 0.458947 0.710285 +vt 0.454938 0.707252 +vt 0.463434 0.701359 +vt 0.462644 0.694466 +vt 0.465914 0.697038 +vt 0.465434 0.690080 +vt 0.459374 0.691893 +vt 0.462413 0.687430 +vt 0.469184 0.699610 +vt 0.466946 0.703891 +vt 0.472455 0.702183 +vt 0.470446 0.706379 +vt 0.468455 0.692729 +vt 0.471476 0.695379 +vt 0.474497 0.698029 +vt 0.464812 0.708272 +vt 0.462836 0.712804 +vt 0.468505 0.710659 +vt 0.466664 0.715066 +vt 0.888216 0.200854 +vt 0.883549 0.197818 +vt 0.886579 0.194280 +vt 0.885790 0.204058 +vt 0.881298 0.201063 +vt 0.890963 0.197292 +vt 0.890959 0.190670 +vt 0.894132 0.193523 +vt 0.896015 0.187026 +vt 0.898169 0.196467 +vt 0.895318 0.200272 +vt 0.902570 0.199653 +vt 0.899557 0.203411 +vt 0.897512 0.189649 +vt 0.901075 0.192538 +vt 0.905640 0.195774 +vt 0.892577 0.203825 +vt 0.890000 0.207002 +vt 0.896656 0.206925 +vt 0.893926 0.210075 +vt 0.455722 0.720673 +vt 0.454608 0.726265 +vt 0.450872 0.724937 +vt 0.451935 0.718948 +vt 0.453229 0.712848 +vt 0.457165 0.715310 +vt 0.461070 0.717535 +vt 0.459583 0.722529 +vt 0.464957 0.719641 +vt 0.458446 0.727849 +vt 0.463482 0.724451 +vt 0.462336 0.729561 +vt 0.907328 0.210828 +vt 0.903970 0.214183 +vt 0.900479 0.210348 +vt 0.900850 0.217235 +vt 0.897563 0.213455 +vt 0.903592 0.206901 +vt 0.906834 0.203228 +vt 0.910844 0.207271 +vt 0.910142 0.199442 +vt 0.914482 0.211859 +vt 0.910671 0.215276 +vt 0.914439 0.203612 +vt 0.918390 0.208355 +vt 0.907054 0.218521 +vt 0.903726 0.221507 +vt 0.252756 0.113655 +vt 0.258406 0.116069 +vt 0.259274 0.122917 +vt 0.251012 0.119125 +vt 0.256152 0.130014 +vt 0.264242 0.118408 +vt 0.268279 0.126409 +vt 0.244232 0.114732 +vt 0.247476 0.111090 +vt 0.238194 0.110039 +vt 0.242382 0.108451 +vt 0.246671 0.125031 +vt 0.239429 0.119201 +vt 0.250160 0.107212 +vt 0.253259 0.108257 +vt 0.252564 0.103215 +vt 0.247061 0.106167 +vt 0.251986 0.103534 +vt 0.256357 0.109303 +vt 0.259456 0.110348 +vt 0.253141 0.102897 +vt 0.253719 0.102578 +vt 0.254296 0.102260 +vt 0.011829 0.590670 +vt 0.012303 0.596198 +vt 0.008919 0.597442 +vt 0.007598 0.594103 +vt 0.005665 0.599092 +vt 0.012496 0.601807 +vt 0.010240 0.600781 +vt 0.007554 0.600510 +vt 0.006276 0.590763 +vt 0.010794 0.585305 +vt 0.004955 0.587424 +vt 0.009479 0.580020 +vt 0.003777 0.597673 +vt 0.001888 0.596255 +vt 0.000000 0.594837 +vt 0.015571 0.579912 +vt 0.013140 0.572634 +vt 0.016881 0.587514 +vt 0.015946 0.595766 +vt 0.013890 0.604343 +vt 0.238379 0.131737 +vt 0.246228 0.137526 +vt 0.233366 0.144579 +vt 0.228079 0.138340 +vt 0.221430 0.150295 +vt 0.217714 0.143933 +vt 0.211345 0.154081 +vt 0.213377 0.137852 +vt 0.223074 0.132290 +vt 0.208782 0.148043 +vt 0.205059 0.142159 +vt 0.232067 0.125564 +vt 0.880679 0.171265 +vt 0.869853 0.165673 +vt 0.874044 0.162010 +vt 0.859256 0.160040 +vt 0.861395 0.155996 +vt 0.886751 0.168013 +vt 0.878793 0.158518 +vt 0.893703 0.164927 +vt 0.883821 0.155112 +vt 0.863884 0.152110 +vt 0.866547 0.148301 +vt 0.908613 0.171336 +vt 0.899572 0.173995 +vt 0.923522 0.177745 +vt 0.912450 0.179967 +vt 0.901095 0.161924 +vt 0.918369 0.168735 +vt 0.935643 0.175547 +vt 0.891963 0.176773 +vt 0.903476 0.182238 +vt 0.199609 0.150789 +vt 0.202783 0.150194 +vt 0.204034 0.155341 +vt 0.198803 0.154211 +vt 0.194956 0.150828 +vt 0.197333 0.147693 +vt 0.191800 0.146318 +vt 0.200206 0.145121 +vt 0.194827 0.143332 +vt 0.196966 0.140085 +vt 0.571297 0.919603 +vt 0.572681 0.918665 +vt 0.572483 0.924331 +vt 0.572088 0.930131 +vt 0.569714 0.928050 +vt 0.574462 0.917460 +vt 0.574462 0.924836 +vt 0.574462 0.932212 +vt 0.570604 0.923298 +vt 0.568923 0.921210 +vt 0.570604 0.916844 +vt 0.567340 0.918593 +vt 0.567340 0.925969 +vt 0.564967 0.923888 +vt 0.572483 0.913267 +vt 0.574462 0.910084 +vt 0.569714 0.913298 +vt 0.572088 0.908002 +vt 0.574462 0.902707 +vt 0.252364 0.082387 +vt 0.256130 0.078734 +vt 0.257681 0.081370 +vt 0.253927 0.085038 +vt 0.259231 0.084005 +vt 0.259698 0.074577 +vt 0.261211 0.077202 +vt 0.262723 0.079828 +vt 0.249955 0.088257 +vt 0.248414 0.085592 +vt 0.245771 0.091076 +vt 0.244295 0.088406 +vt 0.255490 0.087689 +vt 0.251496 0.090921 +vt 0.247246 0.093745 +vt 0.246873 0.082928 +vt 0.250801 0.079736 +vt 0.245331 0.080263 +vt 0.242819 0.085737 +vt 0.241344 0.083067 +vt 0.254580 0.076099 +vt 0.258186 0.071952 +vt 0.249237 0.077085 +vt 0.253029 0.073463 +vt 0.256674 0.069327 +vt 0.895460 0.222386 +vt 0.892984 0.225007 +vt 0.889514 0.220796 +vt 0.892297 0.218496 +vt 0.885921 0.217197 +vt 0.890683 0.227844 +vt 0.886999 0.223413 +vt 0.881536 0.219161 +vt 0.894908 0.216110 +vt 0.898048 0.219882 +vt 0.888850 0.215063 +vt 0.891420 0.212738 +vt 0.900785 0.224147 +vt 0.898166 0.226840 +vt 0.903064 0.228992 +vt 0.895804 0.229985 +vt 0.893633 0.233114 +vt 0.900387 0.231802 +vt 0.898020 0.235249 +vt 0.895998 0.240873 +vt 0.240022 0.090886 +vt 0.241380 0.093544 +vt 0.236974 0.095807 +vt 0.242738 0.096203 +vt 0.238404 0.098451 +vt 0.235598 0.093165 +vt 0.232743 0.098008 +vt 0.227977 0.100334 +vt 0.226081 0.097668 +vt 0.234677 0.100644 +vt 0.231025 0.095377 +vt 0.229742 0.092758 +vt 0.234331 0.090526 +vt 0.228675 0.090145 +vt 0.233118 0.087888 +vt 0.224820 0.095052 +vt 0.223876 0.092460 +vt 0.238665 0.088227 +vt 0.237307 0.085569 +vt 0.879452 0.211133 +vt 0.875591 0.208213 +vt 0.877372 0.206131 +vt 0.871296 0.205099 +vt 0.877417 0.213072 +vt 0.873863 0.210165 +vt 0.869690 0.207073 +vt 0.881502 0.209069 +vt 0.879257 0.203791 +vt 0.883583 0.206753 +vt 0.872945 0.202991 +vt 0.874675 0.200615 +vt 0.887642 0.209678 +vt 0.885290 0.211983 +vt 0.882731 0.214052 +vt 0.880069 0.216001 +vt 0.214670 0.102853 +vt 0.220543 0.100181 +vt 0.221967 0.102971 +vt 0.215469 0.105809 +vt 0.209237 0.108737 +vt 0.208723 0.105620 +vt 0.203140 0.111711 +vt 0.202739 0.108434 +vt 0.208004 0.102867 +vt 0.213849 0.100153 +vt 0.207183 0.100297 +vt 0.202057 0.105629 +vt 0.201236 0.103059 +vt 0.219488 0.097532 +vt 0.213015 0.097581 +vt 0.218618 0.094953 +vt 0.863396 0.195664 +vt 0.861915 0.197815 +vt 0.856973 0.193836 +vt 0.858401 0.191667 +vt 0.851958 0.189762 +vt 0.860418 0.199808 +vt 0.855487 0.195832 +vt 0.850473 0.191757 +vt 0.859715 0.189149 +vt 0.864843 0.193197 +vt 0.853343 0.187574 +vt 0.854525 0.185004 +vt 0.869851 0.197051 +vt 0.868265 0.199470 +vt 0.866714 0.201601 +vt 0.865180 0.203588 +vt 0.709369 0.307188 +vt 0.706943 0.307235 +vt 0.706944 0.303638 +vt 0.704517 0.307282 +vt 0.704461 0.303731 +vt 0.709310 0.310832 +vt 0.706941 0.310832 +vt 0.704573 0.310832 +vt 0.709428 0.303544 +vt 0.706946 0.300040 +vt 0.709487 0.299900 +vt 0.706947 0.296443 +vt 0.704405 0.300181 +vt 0.704349 0.296631 +vt 0.712027 0.299759 +vt 0.711911 0.303450 +vt 0.714568 0.299618 +vt 0.714394 0.303356 +vt 0.709545 0.296255 +vt 0.712144 0.296068 +vt 0.714742 0.295880 +vt 0.711794 0.307141 +vt 0.711678 0.310832 +vt 0.714220 0.307094 +vt 0.714047 0.310832 +vt 0.493355 0.966153 +vt 0.490238 0.966616 +vt 0.490462 0.963909 +vt 0.486953 0.966971 +vt 0.487282 0.964312 +vt 0.493054 0.968966 +vt 0.489881 0.969425 +vt 0.486574 0.969777 +vt 0.493492 0.963343 +vt 0.490419 0.961404 +vt 0.493301 0.960538 +vt 0.487506 0.961949 +vt 0.496238 0.959626 +vt 0.496441 0.962710 +vt 0.499202 0.958692 +vt 0.496361 0.965618 +vt 0.496139 0.968437 +vt 0.499350 0.962044 +vt 0.499311 0.965046 +vt 0.499179 0.967873 +vt 0.923796 0.827362 +vt 0.922875 0.825385 +vt 0.927095 0.824649 +vt 0.919626 0.827774 +vt 0.918831 0.826291 +vt 0.927991 0.826943 +vt 0.931666 0.824253 +vt 0.932234 0.826509 +vt 0.936413 0.824026 +vt 0.932625 0.828946 +vt 0.928498 0.829166 +vt 0.932861 0.831910 +vt 0.936501 0.826068 +vt 0.936673 0.828528 +vt 0.936828 0.831680 +vt 0.924212 0.828987 +vt 0.919848 0.828610 +vt 0.928778 0.831831 +vt 0.924463 0.831136 +vt 0.919380 0.828622 +vt 0.479771 0.967282 +vt 0.475985 0.967310 +vt 0.476489 0.964368 +vt 0.472143 0.967303 +vt 0.479470 0.970095 +vt 0.475763 0.970130 +vt 0.472011 0.970130 +vt 0.480279 0.964440 +vt 0.477559 0.961178 +vt 0.481202 0.961543 +vt 0.472606 0.964266 +vt 0.473732 0.960807 +vt 0.484476 0.961898 +vt 0.483882 0.964455 +vt 0.483446 0.967180 +vt 0.483088 0.969989 +vt 0.615669 0.305195 +vt 0.615697 0.307993 +vt 0.613011 0.307804 +vt 0.612970 0.305088 +vt 0.610326 0.307615 +vt 0.615725 0.310792 +vt 0.613053 0.310520 +vt 0.610381 0.310248 +vt 0.612928 0.302372 +vt 0.615642 0.302396 +vt 0.612886 0.299656 +vt 0.615614 0.299598 +vt 0.610270 0.304982 +vt 0.610214 0.302348 +vt 0.610159 0.299715 +vt 0.618355 0.302420 +vt 0.618369 0.305301 +vt 0.621069 0.302444 +vt 0.618341 0.299539 +vt 0.621069 0.299481 +vt 0.618383 0.308182 +vt 0.618397 0.311063 +vt 0.621069 0.305408 +vt 0.621069 0.308371 +vt 0.621069 0.311335 +vt 0.672065 0.947699 +vt 0.672923 0.945123 +vt 0.675510 0.946348 +vt 0.674587 0.948837 +vt 0.678098 0.947573 +vt 0.673918 0.942942 +vt 0.676573 0.944245 +vt 0.679229 0.945549 +vt 0.673943 0.952101 +vt 0.671484 0.951067 +vt 0.677108 0.949975 +vt 0.676401 0.953136 +vt 0.669026 0.950033 +vt 0.669544 0.946561 +vt 0.666567 0.948998 +vt 0.670335 0.943898 +vt 0.671263 0.941639 +vt 0.667023 0.945424 +vt 0.667748 0.942673 +vt 0.668607 0.940336 +vt 0.870969 0.949324 +vt 0.865768 0.948359 +vt 0.867363 0.946110 +vt 0.872834 0.947209 +vt 0.860341 0.947956 +vt 0.861838 0.945537 +vt 0.878197 0.949360 +vt 0.875720 0.951415 +vt 0.882469 0.953214 +vt 0.873526 0.953163 +vt 0.869010 0.951080 +vt 0.871473 0.954758 +vt 0.867004 0.952656 +vt 0.879562 0.955226 +vt 0.877214 0.956990 +vt 0.875145 0.958630 +vt 0.863943 0.950159 +vt 0.858600 0.949819 +vt 0.862003 0.951735 +vt 0.856736 0.951403 +vt 0.121602 0.960746 +vt 0.123014 0.956341 +vt 0.125379 0.957450 +vt 0.122720 0.950185 +vt 0.125124 0.951366 +vt 0.119270 0.959709 +vt 0.120648 0.955231 +vt 0.120316 0.949005 +vt 0.123934 0.961784 +vt 0.127745 0.958559 +vt 0.126266 0.962822 +vt 0.130111 0.959668 +vt 0.127528 0.952546 +vt 0.129932 0.953726 +vt 0.123657 0.965918 +vt 0.121355 0.964952 +vt 0.120482 0.968431 +vt 0.128598 0.963859 +vt 0.125958 0.966884 +vt 0.122754 0.969325 +vt 0.119054 0.963986 +vt 0.116752 0.963020 +vt 0.118209 0.967537 +vt 0.115937 0.966642 +vt 0.113665 0.965748 +vt 0.266363 0.064701 +vt 0.269796 0.059223 +vt 0.271117 0.061848 +vt 0.267754 0.067331 +vt 0.272437 0.064472 +vt 0.273521 0.053548 +vt 0.274773 0.056146 +vt 0.276025 0.058744 +vt 0.264510 0.072486 +vt 0.263053 0.069859 +vt 0.269145 0.069962 +vt 0.265967 0.075113 +vt 0.261597 0.067232 +vt 0.264973 0.062070 +vt 0.260140 0.064605 +vt 0.268476 0.056599 +vt 0.272269 0.050949 +vt 0.263582 0.059440 +vt 0.267156 0.053974 +vt 0.271017 0.048351 +vt 0.903447 0.243298 +vt 0.900985 0.245906 +vt 0.899677 0.240320 +vt 0.902094 0.237219 +vt 0.898681 0.248541 +vt 0.897421 0.243617 +vt 0.904830 0.234510 +vt 0.906226 0.240747 +vt 0.907396 0.247753 +vt 0.904606 0.250246 +vt 0.908484 0.255576 +vt 0.902152 0.252711 +vt 0.899865 0.255162 +vt 0.905707 0.258038 +vt 0.903292 0.260515 +vt 0.901058 0.263000 +vt 0.282235 0.041983 +vt 0.286994 0.036134 +vt 0.288082 0.038498 +vt 0.283372 0.044444 +vt 0.289170 0.040862 +vt 0.291867 0.030264 +vt 0.292908 0.032525 +vt 0.293948 0.034786 +vt 0.278897 0.050335 +vt 0.277705 0.047794 +vt 0.284510 0.046904 +vt 0.280089 0.052876 +vt 0.276514 0.045254 +vt 0.281097 0.039523 +vt 0.275322 0.042713 +vt 0.285905 0.033769 +vt 0.290827 0.028004 +vt 0.279959 0.037062 +vt 0.284817 0.031405 +vt 0.289787 0.025743 +vt 0.908126 0.275872 +vt 0.905818 0.278257 +vt 0.904522 0.269095 +vt 0.906887 0.266648 +vt 0.903707 0.280698 +vt 0.902347 0.271573 +vt 0.909634 0.264263 +vt 0.910830 0.273600 +vt 0.912058 0.283368 +vt 0.909405 0.285505 +vt 0.913300 0.293353 +vt 0.907158 0.287806 +vt 0.905115 0.290190 +vt 0.910703 0.295343 +vt 0.908521 0.297550 +vt 0.906547 0.299867 +vt 0.038625 0.978109 +vt 0.036585 0.971040 +vt 0.038981 0.971440 +vt 0.040944 0.978584 +vt 0.041377 0.971840 +vt 0.035738 0.963592 +vt 0.038180 0.963938 +vt 0.040623 0.964285 +vt 0.043683 0.985491 +vt 0.041461 0.984926 +vt 0.046809 0.992278 +vt 0.044695 0.991618 +vt 0.043263 0.979060 +vt 0.045904 0.986055 +vt 0.048922 0.992938 +vt 0.039240 0.984362 +vt 0.037018 0.983798 +vt 0.033987 0.977159 +vt 0.042582 0.990958 +vt 0.040468 0.990298 +vt 0.036306 0.977634 +vt 0.034188 0.970639 +vt 0.033295 0.963245 +vt 0.031792 0.970239 +vt 0.030853 0.962898 +vt 0.941397 0.205779 +vt 0.944150 0.204383 +vt 0.944181 0.211630 +vt 0.941460 0.213025 +vt 0.944212 0.218876 +vt 0.946902 0.202987 +vt 0.946902 0.210234 +vt 0.946902 0.217482 +vt 0.938739 0.214421 +vt 0.938645 0.207176 +vt 0.936018 0.215816 +vt 0.935893 0.208572 +vt 0.941522 0.220271 +vt 0.938833 0.221666 +vt 0.936143 0.223060 +vt 0.938551 0.199931 +vt 0.941335 0.198534 +vt 0.938458 0.192686 +vt 0.935768 0.201328 +vt 0.935643 0.194083 +vt 0.944118 0.197137 +vt 0.946902 0.195740 +vt 0.941272 0.191288 +vt 0.944087 0.189890 +vt 0.946902 0.188492 +vt 0.041156 0.938725 +vt 0.043530 0.939051 +vt 0.040842 0.947624 +vt 0.045904 0.939376 +vt 0.044288 0.930016 +vt 0.046605 0.930358 +vt 0.048922 0.930700 +vt 0.038930 0.955960 +vt 0.036483 0.955639 +vt 0.043263 0.947941 +vt 0.041377 0.956281 +vt 0.038421 0.947308 +vt 0.034035 0.955318 +vt 0.036000 0.946992 +vt 0.031588 0.954997 +vt 0.038781 0.938399 +vt 0.041971 0.929674 +vt 0.033579 0.946676 +vt 0.036407 0.938074 +vt 0.039653 0.929331 +vt 0.120397 0.853993 +vt 0.118182 0.852057 +vt 0.123151 0.846300 +vt 0.125445 0.848276 +vt 0.128403 0.840919 +vt 0.115967 0.850122 +vt 0.120857 0.844324 +vt 0.126014 0.838905 +vt 0.127740 0.850253 +vt 0.122612 0.855928 +vt 0.130034 0.852229 +vt 0.124827 0.857863 +vt 0.130792 0.842933 +vt 0.133181 0.844947 +vt 0.135570 0.846960 +vt 0.118110 0.862340 +vt 0.115944 0.860454 +vt 0.114548 0.869859 +vt 0.112386 0.868034 +vt 0.120276 0.864227 +vt 0.116710 0.871684 +vt 0.113779 0.858568 +vt 0.111613 0.856682 +vt 0.110224 0.866209 +vt 0.108062 0.864384 +vt 0.968610 0.908708 +vt 0.971357 0.907397 +vt 0.970240 0.918457 +vt 0.969444 0.898068 +vt 0.972447 0.896628 +vt 0.967769 0.919422 +vt 0.969067 0.930100 +vt 0.966915 0.930281 +vt 0.967866 0.942034 +vt 0.964778 0.931035 +vt 0.965342 0.920446 +vt 0.962648 0.932077 +vt 0.966054 0.941213 +vt 0.964214 0.941625 +vt 0.962361 0.942653 +vt 0.965905 0.909857 +vt 0.966469 0.899267 +vt 0.962935 0.921500 +vt 0.963222 0.910923 +vt 0.963509 0.900347 +vt 0.106646 0.885670 +vt 0.107801 0.875356 +vt 0.110020 0.877104 +vt 0.104337 0.884008 +vt 0.105582 0.873608 +vt 0.108955 0.887331 +vt 0.112239 0.878853 +vt 0.111264 0.888993 +vt 0.114458 0.880601 +vt 0.111702 0.899950 +vt 0.109298 0.898379 +vt 0.113636 0.911396 +vt 0.111159 0.909914 +vt 0.113573 0.890654 +vt 0.114106 0.901520 +vt 0.116112 0.912877 +vt 0.106894 0.896809 +vt 0.104490 0.895238 +vt 0.108682 0.908433 +vt 0.106206 0.906952 +vt 0.882038 0.961350 +vt 0.884668 0.959424 +vt 0.885730 0.967306 +vt 0.887494 0.957429 +vt 0.887915 0.965408 +vt 0.883594 0.969186 +vt 0.886591 0.976178 +vt 0.884676 0.978133 +vt 0.887350 0.985544 +vt 0.888506 0.974222 +vt 0.889180 0.983454 +vt 0.882761 0.980088 +vt 0.881555 0.971033 +vt 0.880846 0.982043 +vt 0.879566 0.972862 +vt 0.885520 0.987634 +vt 0.883690 0.989724 +vt 0.881860 0.991814 +vt 0.879799 0.963140 +vt 0.877754 0.964861 +vt 0.116299 0.931497 +vt 0.112146 0.920202 +vt 0.114645 0.921602 +vt 0.113814 0.930173 +vt 0.109648 0.918802 +vt 0.118783 0.932820 +vt 0.117144 0.923001 +vt 0.121268 0.934144 +vt 0.119642 0.924401 +vt 0.125049 0.944200 +vt 0.122601 0.942949 +vt 0.123753 0.935467 +vt 0.127497 0.945451 +vt 0.120152 0.941698 +vt 0.117704 0.940447 +vt 0.925147 0.839042 +vt 0.924885 0.834683 +vt 0.928994 0.835452 +vt 0.921370 0.838088 +vt 0.921090 0.833572 +vt 0.932963 0.835743 +vt 0.933003 0.839873 +vt 0.936863 0.835795 +vt 0.936973 0.839881 +vt 0.929054 0.839662 +vt 0.933052 0.843729 +vt 0.928867 0.844096 +vt 0.932727 0.848083 +vt 0.937351 0.842946 +vt 0.938193 0.843996 +vt 0.924914 0.843630 +vt 0.921054 0.842650 +vt 0.928334 0.848868 +vt 0.924269 0.848431 +vt 0.920292 0.847329 +vt 0.979473 0.336517 +vt 0.978365 0.341125 +vt 0.975010 0.339831 +vt 0.976592 0.334841 +vt 0.972031 0.337733 +vt 0.977589 0.345862 +vt 0.973914 0.344921 +vt 0.969023 0.344143 +vt 0.978101 0.330162 +vt 0.980703 0.332056 +vt 0.979574 0.325639 +vt 0.981994 0.327668 +vt 0.974151 0.332559 +vt 0.976041 0.327799 +vt 0.977816 0.323245 +vt 0.983671 0.333427 +vt 0.982582 0.337751 +vt 0.986833 0.334224 +vt 0.985834 0.338576 +vt 0.984848 0.329155 +vt 0.987908 0.329921 +vt 0.981666 0.342178 +vt 0.981010 0.346760 +vt 0.984990 0.343026 +vt 0.984376 0.347625 +vt 0.922057 0.858542 +vt 0.923295 0.853431 +vt 0.927354 0.854091 +vt 0.917940 0.857185 +vt 0.919233 0.852199 +vt 0.931644 0.853708 +vt 0.930227 0.859805 +vt 0.935783 0.852362 +vt 0.934553 0.859352 +vt 0.926081 0.859498 +vt 0.928899 0.865574 +vt 0.924672 0.864823 +vt 0.927548 0.871080 +vt 0.933559 0.865845 +vt 0.932576 0.871986 +vt 0.920618 0.863674 +vt 0.916477 0.862212 +vt 0.923148 0.870014 +vt 0.919044 0.868738 +vt 0.914904 0.867206 +vt 0.977494 0.355677 +vt 0.977843 0.360776 +vt 0.974771 0.359738 +vt 0.974324 0.354815 +vt 0.971856 0.358895 +vt 0.978271 0.365805 +vt 0.975218 0.364633 +vt 0.972365 0.363743 +vt 0.973862 0.349895 +vt 0.977354 0.350709 +vt 0.971205 0.354012 +vt 0.970298 0.349094 +vt 0.980701 0.351549 +vt 0.980766 0.356658 +vt 0.984071 0.352421 +vt 0.984157 0.357627 +vt 0.981232 0.362200 +vt 0.981724 0.367539 +vt 0.984718 0.363456 +vt 0.986347 0.371029 +vt 0.919952 0.879848 +vt 0.915811 0.878417 +vt 0.917399 0.873645 +vt 0.921532 0.875021 +vt 0.911735 0.876900 +vt 0.913284 0.872091 +vt 0.926064 0.876389 +vt 0.924564 0.881481 +vt 0.931379 0.877923 +vt 0.923166 0.886338 +vt 0.918538 0.884501 +vt 0.921988 0.890940 +vt 0.917419 0.888982 +vt 0.930056 0.883603 +vt 0.928697 0.888975 +vt 0.927392 0.893987 +vt 0.914408 0.883073 +vt 0.910374 0.881665 +vt 0.913317 0.887634 +vt 0.909319 0.886416 +vt 0.979276 0.379518 +vt 0.976241 0.378644 +vt 0.976056 0.374162 +vt 0.973348 0.377659 +vt 0.973201 0.373226 +vt 0.979291 0.383755 +vt 0.976133 0.382839 +vt 0.973193 0.381749 +vt 0.975680 0.369466 +vt 0.978646 0.370565 +vt 0.972843 0.368554 +vt 0.978978 0.375116 +vt 0.981869 0.372036 +vt 0.985221 0.373693 +vt 0.985160 0.377116 +vt 0.982036 0.376107 +vt 0.982591 0.380168 +vt 0.982886 0.384322 +vt 0.986048 0.380706 +vt 0.987767 0.383871 +vt 0.912374 0.896852 +vt 0.912666 0.892121 +vt 0.916724 0.893297 +vt 0.908386 0.896195 +vt 0.908686 0.891186 +vt 0.916385 0.897800 +vt 0.921149 0.895267 +vt 0.920573 0.899615 +vt 0.926228 0.898587 +vt 0.920187 0.904276 +vt 0.916332 0.902847 +vt 0.920368 0.909706 +vt 0.916610 0.908833 +vt 0.925136 0.902950 +vt 0.924045 0.907249 +vt 0.923193 0.910714 +vt 0.912363 0.902146 +vt 0.908332 0.901662 +vt 0.912551 0.908321 +vt 0.908432 0.907807 +vt 0.976951 0.395346 +vt 0.973826 0.393607 +vt 0.974826 0.390230 +vt 0.970770 0.391799 +vt 0.971806 0.388708 +vt 0.975898 0.398921 +vt 0.972726 0.396894 +vt 0.969635 0.394779 +vt 0.977935 0.391674 +vt 0.975628 0.386671 +vt 0.978780 0.387809 +vt 0.972646 0.385395 +vt 0.982271 0.388671 +vt 0.981221 0.392962 +vt 0.985829 0.389528 +vt 0.984663 0.394119 +vt 0.980216 0.396945 +vt 0.979232 0.400774 +vt 0.983689 0.398335 +vt 0.982810 0.402364 +vt 0.900051 0.894138 +vt 0.900271 0.900071 +vt 0.896299 0.899494 +vt 0.895784 0.892977 +vt 0.892351 0.899034 +vt 0.900762 0.906253 +vt 0.897253 0.906351 +vt 0.893857 0.906835 +vt 0.895694 0.886812 +vt 0.900147 0.888453 +vt 0.896017 0.881011 +vt 0.900603 0.883017 +vt 0.891490 0.891792 +vt 0.891188 0.885092 +vt 0.891365 0.878918 +vt 0.904496 0.889937 +vt 0.904265 0.895250 +vt 0.905059 0.884848 +vt 0.904291 0.900882 +vt 0.904496 0.906929 +vt 0.991960 0.396084 +vt 0.988240 0.395169 +vt 0.989184 0.390653 +vt 0.992555 0.391758 +vt 0.990200 0.386019 +vt 0.996163 0.392557 +vt 0.995831 0.396835 +vt 0.999889 0.393203 +vt 0.999779 0.397505 +vt 0.993169 0.387403 +vt 0.996495 0.388280 +vt 1.000000 0.388901 +vt 0.995499 0.401113 +vt 0.991400 0.400349 +vt 0.995167 0.405391 +vt 0.999668 0.401807 +vt 0.999557 0.406108 +vt 0.987440 0.399447 +vt 0.990858 0.404586 +vt 0.986712 0.403606 +vt 0.897791 0.870494 +vt 0.899114 0.865677 +vt 0.904050 0.868020 +vt 0.900643 0.861090 +vt 0.905611 0.863306 +vt 0.892857 0.868048 +vt 0.894087 0.863247 +vt 0.895584 0.858798 +vt 0.902639 0.872846 +vt 0.908804 0.870187 +vt 0.907316 0.875012 +vt 0.910395 0.865370 +vt 0.906033 0.879886 +vt 0.901462 0.877830 +vt 0.896737 0.875589 +vt 0.891935 0.873255 +vt 0.889575 0.238564 +vt 0.887515 0.242160 +vt 0.884543 0.238383 +vt 0.885432 0.246152 +vt 0.882420 0.242462 +vt 0.892469 0.242697 +vt 0.890488 0.245937 +vt 0.888444 0.249841 +vt 0.886623 0.234496 +vt 0.881571 0.234606 +vt 0.883557 0.230557 +vt 0.878599 0.230828 +vt 0.879409 0.238772 +vt 0.876397 0.235083 +vt 0.885190 0.226747 +vt 0.888616 0.230995 +vt 0.880434 0.226684 +vt 0.881536 0.222758 +vt 0.891585 0.235761 +vt 0.894326 0.240787 +vt 0.908870 0.854051 +vt 0.907238 0.858660 +vt 0.902314 0.856683 +vt 0.897303 0.854649 +vt 0.899190 0.850612 +vt 0.904068 0.852350 +vt 0.905848 0.847986 +vt 0.907597 0.843484 +vt 0.911917 0.844829 +vt 0.901187 0.846496 +vt 0.903238 0.842113 +vt 0.910450 0.849451 +vt 0.914931 0.850864 +vt 0.913520 0.855675 +vt 0.916162 0.846119 +vt 0.911987 0.860522 +vt 0.991360 0.359270 +vt 0.987682 0.358457 +vt 0.987629 0.353334 +vt 0.991328 0.354277 +vt 0.987885 0.348467 +vt 0.995121 0.355240 +vt 0.995209 0.360192 +vt 0.998962 0.356212 +vt 0.991500 0.349291 +vt 0.995187 0.350104 +vt 0.998909 0.350910 +vt 0.995400 0.365022 +vt 0.991564 0.364327 +vt 0.995642 0.369790 +vt 0.999144 0.361168 +vt 0.999413 0.365893 +vt 0.999726 0.370503 +vt 0.988083 0.363985 +vt 0.991854 0.369415 +vt 0.988657 0.369715 +vt 0.914347 0.835437 +vt 0.915320 0.830676 +vt 0.918060 0.832258 +vt 0.917812 0.836874 +vt 0.916215 0.825894 +vt 0.918101 0.827633 +vt 0.917150 0.841475 +vt 0.913214 0.840156 +vt 0.909256 0.838739 +vt 0.910849 0.833812 +vt 0.905288 0.837274 +vt 0.912398 0.828763 +vt 0.913924 0.823653 +vt 0.907336 0.832094 +vt 0.909383 0.826685 +vt 0.911430 0.821163 +vt 0.992499 0.339185 +vt 0.995883 0.339158 +vt 0.995458 0.344723 +vt 0.991911 0.344256 +vt 0.999281 0.339038 +vt 0.999030 0.345147 +vt 0.988413 0.343705 +vt 0.989145 0.339022 +vt 0.990013 0.334392 +vt 0.993207 0.334089 +vt 0.990949 0.329788 +vt 0.996411 0.333472 +vt 0.999619 0.332698 +vt 0.993974 0.328980 +vt 0.996990 0.327724 +vt 1.000000 0.326242 +vt 0.419080 0.949933 +vt 0.417721 0.946459 +vt 0.421849 0.947548 +vt 0.416362 0.942985 +vt 0.420942 0.942880 +vt 0.415406 0.947650 +vt 0.413594 0.945370 +vt 0.411781 0.943089 +vt 0.422755 0.952216 +vt 0.425976 0.948637 +vt 0.426429 0.954499 +vt 0.430103 0.949727 +vt 0.425523 0.942775 +vt 0.430103 0.942671 +vt 0.426882 0.960361 +vt 0.423661 0.956884 +vt 0.427335 0.966223 +vt 0.424567 0.961552 +vt 0.430103 0.956782 +vt 0.430103 0.963838 +vt 0.430103 0.970894 +vt 0.420439 0.953407 +vt 0.417218 0.949930 +vt 0.421799 0.956882 +vt 0.419030 0.952211 +vt 0.972360 0.329509 +vt 0.969856 0.334266 +vt 0.968091 0.330031 +vt 0.967268 0.339158 +vt 0.965152 0.334149 +vt 0.971009 0.325946 +vt 0.966341 0.325625 +vt 0.969887 0.322127 +vt 0.964599 0.321135 +vt 0.962795 0.329123 +vt 0.960318 0.324089 +vt 0.973433 0.318630 +vt 0.973885 0.321929 +vt 0.976979 0.315132 +vt 0.976740 0.317945 +vt 0.968879 0.318181 +vt 0.973160 0.315227 +vt 0.977440 0.312273 +vt 0.974695 0.325020 +vt 0.976945 0.320665 +vt 0.943927 0.855981 +vt 0.948989 0.853357 +vt 0.951007 0.863832 +vt 0.944786 0.864832 +vt 0.952455 0.874134 +vt 0.954176 0.850444 +vt 0.957400 0.862690 +vt 0.959881 0.874755 +vt 0.938913 0.865551 +vt 0.939118 0.858028 +vt 0.945269 0.873484 +vt 0.938563 0.872778 +vt 0.939388 0.850129 +vt 0.942858 0.846951 +vt 0.939691 0.842041 +vt 0.946591 0.842766 +vt 0.950455 0.838079 +vt 0.941683 0.837831 +vt 0.944003 0.832118 +vt 0.946487 0.825654 +vt 0.271958 0.928159 +vt 0.269093 0.936483 +vt 0.267685 0.932354 +vt 0.268726 0.925624 +vt 0.266277 0.928225 +vt 0.265989 0.944824 +vt 0.266501 0.939113 +vt 0.267013 0.933402 +vt 0.269481 0.918953 +vt 0.274342 0.919872 +vt 0.269806 0.912372 +vt 0.276006 0.911640 +vt 0.265494 0.923088 +vt 0.264620 0.918034 +vt 0.263606 0.913104 +vt 0.279203 0.920791 +vt 0.275190 0.930695 +vt 0.284064 0.921710 +vt 0.282205 0.910908 +vt 0.288405 0.910176 +vt 0.270502 0.940612 +vt 0.265477 0.950535 +vt 0.278421 0.933230 +vt 0.271910 0.944741 +vt 0.264965 0.956247 +vt 0.952967 0.893841 +vt 0.951818 0.902834 +vt 0.943350 0.897520 +vt 0.949593 0.910978 +vt 0.941494 0.904399 +vt 0.961676 0.897900 +vt 0.960613 0.908445 +vt 0.957992 0.917950 +vt 0.944579 0.889977 +vt 0.935534 0.892802 +vt 0.936835 0.886499 +vt 0.933993 0.898604 +vt 0.937857 0.879793 +vt 0.945209 0.881915 +vt 0.953144 0.884206 +vt 0.961369 0.886581 +vt 0.276708 0.903480 +vt 0.269558 0.905909 +vt 0.268847 0.899668 +vt 0.262407 0.908338 +vt 0.261135 0.903756 +vt 0.276559 0.895579 +vt 0.267785 0.893751 +vt 0.275666 0.888123 +vt 0.266481 0.888262 +vt 0.259903 0.899379 +vt 0.258823 0.895226 +vt 0.283548 0.882495 +vt 0.284271 0.891491 +vt 0.291429 0.876867 +vt 0.291983 0.887402 +vt 0.274139 0.881297 +vt 0.281797 0.874332 +vt 0.289455 0.867368 +vt 0.283859 0.901051 +vt 0.291010 0.898623 +vt 0.941810 0.923984 +vt 0.936689 0.928609 +vt 0.932207 0.919842 +vt 0.931037 0.931826 +vt 0.927983 0.922812 +vt 0.947925 0.932915 +vt 0.941319 0.938129 +vt 0.934225 0.941669 +vt 0.935869 0.915637 +vt 0.928022 0.912580 +vt 0.930273 0.908458 +vt 0.925195 0.915454 +vt 0.932251 0.903811 +vt 0.938982 0.910463 +vt 0.946184 0.918067 +vt 0.953623 0.926147 +vt 0.272085 0.875287 +vt 0.265047 0.883302 +vt 0.263514 0.878739 +vt 0.258008 0.891317 +vt 0.257390 0.887590 +vt 0.269637 0.869889 +vt 0.261915 0.874442 +vt 0.266926 0.864899 +vt 0.260284 0.870277 +vt 0.256905 0.883985 +vt 0.256486 0.880441 +vt 0.271937 0.855356 +vt 0.275761 0.861038 +vt 0.276947 0.845813 +vt 0.281884 0.852188 +vt 0.264083 0.860113 +vt 0.267882 0.849949 +vt 0.271681 0.839785 +vt 0.279124 0.867272 +vt 0.286163 0.859257 +vt 0.918138 0.924535 +vt 0.919095 0.933713 +vt 0.913404 0.932448 +vt 0.913178 0.923869 +vt 0.908297 0.929756 +vt 0.920092 0.943287 +vt 0.913582 0.941229 +vt 0.907794 0.937169 +vt 0.912858 0.915694 +vt 0.917261 0.916152 +vt 0.908625 0.922323 +vt 0.908600 0.914850 +vt 0.921491 0.916358 +vt 0.923183 0.924280 +vt 0.925073 0.933517 +vt 0.927061 0.943411 +vt 0.694981 0.281256 +vt 0.699586 0.278433 +vt 0.700287 0.286295 +vt 0.696280 0.288511 +vt 0.700988 0.294157 +vt 0.704349 0.275623 +vt 0.704349 0.284052 +vt 0.704349 0.292481 +vt 0.692383 0.290673 +vt 0.690692 0.284105 +vt 0.688650 0.292755 +vt 0.686878 0.286992 +vt 0.697563 0.295787 +vt 0.694011 0.297325 +vt 0.690268 0.298726 +vt 0.688877 0.277704 +vt 0.693651 0.274043 +vt 0.687001 0.271387 +vt 0.684795 0.281649 +vt 0.682557 0.276516 +vt 0.698885 0.270571 +vt 0.704349 0.267194 +vt 0.692306 0.266850 +vt 0.698184 0.262709 +vt 0.704349 0.258765 +vt 0.902325 0.917641 +vt 0.900833 0.920242 +vt 0.898662 0.913535 +vt 0.899112 0.923498 +vt 0.896089 0.915210 +vt 0.901483 0.912685 +vt 0.904802 0.913486 +vt 0.904799 0.919937 +vt 0.904076 0.925668 +vt 0.902992 0.931038 +vt 0.679461 0.290800 +vt 0.681633 0.285974 +vt 0.683696 0.289932 +vt 0.676824 0.288946 +vt 0.679260 0.282435 +vt 0.681607 0.292250 +vt 0.685138 0.294729 +vt 0.681739 0.296069 +vt 0.686270 0.299944 +vt 0.678348 0.296251 +vt 0.674961 0.295852 +vt 0.682081 0.301025 +vt 0.677765 0.302013 +vt 0.673385 0.302956 +vt 0.923602 0.743233 +vt 0.920911 0.727941 +vt 0.930046 0.732352 +vt 0.933285 0.747960 +vt 0.939181 0.736762 +vt 0.919527 0.712218 +vt 0.928010 0.716339 +vt 0.936492 0.720459 +vt 0.937326 0.763299 +vt 0.927163 0.758237 +vt 0.941768 0.778502 +vt 0.931159 0.773097 +vt 0.942969 0.752687 +vt 0.947490 0.768360 +vt 0.952377 0.783908 +vt 0.917000 0.753175 +vt 0.913918 0.738506 +vt 0.906836 0.748113 +vt 0.920551 0.767692 +vt 0.909942 0.762286 +vt 0.911777 0.723531 +vt 0.911044 0.708097 +vt 0.904235 0.733778 +vt 0.902642 0.719120 +vt 0.902562 0.703977 +vt 0.919153 0.356198 +vt 0.911461 0.359443 +vt 0.917921 0.342311 +vt 0.926036 0.338817 +vt 0.924968 0.325246 +vt 0.903437 0.363077 +vt 0.909428 0.346276 +vt 0.916013 0.329570 +vt 0.933393 0.336266 +vt 0.926181 0.353729 +vt 0.933496 0.321477 +vt 0.941170 0.318821 +vt 0.920097 0.371227 +vt 0.913426 0.373661 +vt 0.915707 0.388780 +vt 0.906176 0.376711 +vt 0.898636 0.380069 +vt 0.909433 0.391249 +vt 0.902653 0.394182 +vt 0.895619 0.397349 +vt 0.921630 0.679473 +vt 0.924411 0.663305 +vt 0.930450 0.666657 +vt 0.928480 0.683093 +vt 0.936489 0.670010 +vt 0.927877 0.647843 +vt 0.933221 0.650878 +vt 0.938566 0.653914 +vt 0.927577 0.699786 +vt 0.919884 0.695920 +vt 0.935329 0.686713 +vt 0.935270 0.703653 +vt 0.912191 0.692053 +vt 0.914780 0.675853 +vt 0.904499 0.688186 +vt 0.918373 0.659952 +vt 0.922532 0.644807 +vt 0.907930 0.672233 +vt 0.912334 0.656600 +vt 0.917187 0.641771 +vt 0.908366 0.426399 +vt 0.902554 0.429244 +vt 0.901479 0.411924 +vt 0.907753 0.409001 +vt 0.896579 0.432302 +vt 0.894980 0.415010 +vt 0.913574 0.406405 +vt 0.913855 0.423981 +vt 0.916703 0.441387 +vt 0.911258 0.442923 +vt 0.921671 0.456743 +vt 0.905777 0.445450 +vt 0.900277 0.448472 +vt 0.916260 0.457614 +vt 0.911047 0.459849 +vt 0.905934 0.462765 +vt 0.935716 0.620034 +vt 0.939925 0.607120 +vt 0.944195 0.608817 +vt 0.940227 0.622222 +vt 0.948466 0.610513 +vt 0.944217 0.594490 +vt 0.948297 0.595675 +vt 0.952377 0.596860 +vt 0.936524 0.636154 +vt 0.931672 0.633513 +vt 0.944738 0.624410 +vt 0.941377 0.638795 +vt 0.926820 0.630872 +vt 0.931205 0.617846 +vt 0.921968 0.628231 +vt 0.935655 0.605424 +vt 0.940137 0.593305 +vt 0.926695 0.615658 +vt 0.931385 0.603728 +vt 0.936057 0.592120 +vt 0.931656 0.479533 +vt 0.926943 0.481774 +vt 0.918265 0.471748 +vt 0.923203 0.469512 +vt 0.922250 0.484126 +vt 0.913410 0.474431 +vt 0.928307 0.468169 +vt 0.936411 0.477515 +vt 0.933494 0.467272 +vt 0.941188 0.475609 +vt 0.945784 0.486634 +vt 0.955789 0.495639 +vt 0.951261 0.497170 +vt 0.950378 0.484677 +vt 0.960318 0.494109 +vt 0.941189 0.488592 +vt 0.936594 0.490550 +vt 0.931999 0.492508 +vt 0.946732 0.498701 +vt 0.942203 0.500232 +vt 0.535220 0.887259 +vt 0.529311 0.891207 +vt 0.528101 0.879870 +vt 0.523568 0.894815 +vt 0.523010 0.883221 +vt 0.537352 0.898155 +vt 0.530639 0.902297 +vt 0.524233 0.906104 +vt 0.533197 0.876231 +vt 0.526969 0.868368 +vt 0.531461 0.865061 +vt 0.525877 0.856785 +vt 0.522524 0.871422 +vt 0.522074 0.859522 +vt 0.536044 0.861246 +vt 0.538301 0.872019 +vt 0.540765 0.856671 +vt 0.543118 0.866737 +vt 0.529868 0.853818 +vt 0.534236 0.850393 +vt 0.539171 0.846281 +vt 0.541458 0.882633 +vt 0.544681 0.893342 +vt 0.546989 0.876153 +vt 0.554119 0.886872 +vt 0.603993 0.286159 +vt 0.604677 0.298874 +vt 0.599195 0.298284 +vt 0.605360 0.311590 +vt 0.600561 0.311021 +vt 0.610159 0.286770 +vt 0.610159 0.299464 +vt 0.610159 0.312158 +vt 0.597828 0.285547 +vt 0.593713 0.297694 +vt 0.591662 0.284936 +vt 0.588231 0.297104 +vt 0.595763 0.310452 +vt 0.590964 0.309883 +vt 0.589612 0.272178 +vt 0.596461 0.272811 +vt 0.587562 0.259419 +vt 0.595094 0.260074 +vt 0.585497 0.284324 +vt 0.582763 0.271545 +vt 0.580029 0.258765 +vt 0.603310 0.273444 +vt 0.610159 0.274077 +vt 0.602626 0.260728 +vt 0.610159 0.261383 +vt 0.543858 0.929917 +vt 0.535403 0.933927 +vt 0.533723 0.923575 +vt 0.526947 0.937936 +vt 0.525959 0.927563 +vt 0.546270 0.940185 +vt 0.537121 0.944196 +vt 0.527972 0.948207 +vt 0.541541 0.919537 +vt 0.532122 0.913059 +vt 0.539416 0.908932 +vt 0.525042 0.916986 +vt 0.547136 0.904404 +vt 0.549466 0.915399 +vt 0.555069 0.899676 +vt 0.557444 0.911211 +vt 0.552313 0.925908 +vt 0.555419 0.936174 +vt 0.560769 0.921899 +vt 0.564569 0.932163 +vt 0.798923 0.889488 +vt 0.795615 0.902725 +vt 0.788796 0.898031 +vt 0.792794 0.916393 +vt 0.786539 0.911882 +vt 0.806823 0.894601 +vt 0.802434 0.907418 +vt 0.799049 0.920904 +vt 0.791165 0.884446 +vt 0.781977 0.893337 +vt 0.783689 0.879545 +vt 0.775158 0.888643 +vt 0.780284 0.907371 +vt 0.774029 0.902861 +vt 0.785439 0.866235 +vt 0.793756 0.871394 +vt 0.787245 0.853652 +vt 0.796198 0.858782 +vt 0.776356 0.874714 +vt 0.777689 0.861359 +vt 0.779224 0.848866 +vt 0.803204 0.877117 +vt 0.813220 0.883121 +vt 0.807016 0.864601 +vt 0.822628 0.873646 +vt 0.926030 0.539574 +vt 0.935682 0.539160 +vt 0.934480 0.547920 +vt 0.945334 0.538745 +vt 0.944056 0.547313 +vt 0.926277 0.531076 +vt 0.936297 0.530864 +vt 0.946317 0.530652 +vt 0.924905 0.548527 +vt 0.932888 0.556991 +vt 0.923194 0.557783 +vt 0.931100 0.566216 +vt 0.942582 0.556198 +vt 0.941010 0.565242 +vt 0.913500 0.558575 +vt 0.915329 0.549134 +vt 0.903806 0.559367 +vt 0.921191 0.567191 +vt 0.911281 0.568165 +vt 0.901371 0.569139 +vt 0.916378 0.539989 +vt 0.916258 0.531288 +vt 0.905753 0.549740 +vt 0.906726 0.540403 +vt 0.906238 0.531500 +vt 0.810630 0.837567 +vt 0.808916 0.850934 +vt 0.798120 0.846515 +vt 0.822966 0.838019 +vt 0.821631 0.853120 +vt 0.800127 0.835089 +vt 0.789125 0.842035 +vt 0.791214 0.831488 +vt 0.781031 0.837523 +vt 0.793646 0.822116 +vt 0.802824 0.824997 +vt 0.796554 0.814022 +vt 0.806145 0.816631 +vt 0.783097 0.827326 +vt 0.785410 0.818274 +vt 0.787959 0.810362 +vt 0.813887 0.825954 +vt 0.826340 0.824795 +vt 0.817727 0.817136 +vt 0.834496 0.812025 +vt 0.863904 0.700210 +vt 0.865411 0.713714 +vt 0.835868 0.709515 +vt 0.866809 0.727396 +vt 0.837566 0.724386 +vt 0.893414 0.705774 +vt 0.894953 0.717913 +vt 0.896053 0.730405 +vt 0.834394 0.694646 +vt 0.806326 0.705315 +vt 0.804884 0.689083 +vt 0.776784 0.701116 +vt 0.808322 0.721377 +vt 0.779078 0.718367 +vt 0.803811 0.672736 +vt 0.833068 0.679781 +vt 0.802922 0.656332 +vt 0.831815 0.664916 +vt 0.775374 0.683519 +vt 0.774554 0.665691 +vt 0.774029 0.647748 +vt 0.862325 0.686825 +vt 0.891581 0.693870 +vt 0.860709 0.673500 +vt 0.889602 0.682084 +vt 0.854644 0.816008 +vt 0.871930 0.809293 +vt 0.871597 0.825725 +vt 0.890379 0.801880 +vt 0.890379 0.819852 +vt 0.855804 0.801045 +vt 0.872262 0.792861 +vt 0.890379 0.783908 +vt 0.853649 0.831115 +vt 0.871264 0.842156 +vt 0.852986 0.846509 +vt 0.870932 0.858588 +vt 0.890379 0.837824 +vt 0.890379 0.855797 +vt 0.836381 0.850905 +vt 0.837372 0.835540 +vt 0.852488 0.862048 +vt 0.836052 0.866844 +vt 0.839686 0.821327 +vt 0.842662 0.807690 +vt 0.841776 0.754142 +vt 0.839559 0.739262 +vt 0.868062 0.741316 +vt 0.869207 0.755414 +vt 0.896566 0.743369 +vt 0.870279 0.769631 +vt 0.844140 0.769024 +vt 0.871315 0.783908 +vt 0.846579 0.783908 +vt 0.896639 0.756685 +vt 0.896418 0.770238 +vt 0.896052 0.783908 +vt 0.818001 0.768418 +vt 0.814344 0.752871 +vt 0.791862 0.767811 +vt 0.821843 0.783908 +vt 0.797107 0.783908 +vt 0.811056 0.737209 +vt 0.786913 0.751599 +vt 0.782553 0.735156 +vt 0.621281 0.880431 +vt 0.619738 0.907380 +vt 0.608881 0.899164 +vt 0.611015 0.874665 +vt 0.599634 0.891066 +vt 0.618167 0.934318 +vt 0.606629 0.923621 +vt 0.596829 0.912147 +vt 0.612916 0.850081 +vt 0.622765 0.853461 +vt 0.614699 0.825455 +vt 0.624220 0.826480 +vt 0.602146 0.869878 +vt 0.604073 0.848479 +vt 0.605706 0.826974 +vt 0.633284 0.858026 +vt 0.632477 0.886850 +vt 0.644139 0.863183 +vt 0.634091 0.829202 +vt 0.644139 0.832771 +vt 0.631670 0.915674 +vt 0.630863 0.944498 +vt 0.644139 0.893595 +vt 0.644139 0.924008 +vt 0.644139 0.954420 +vt 0.553467 0.855093 +vt 0.550775 0.866644 +vt 0.547351 0.859889 +vt 0.557444 0.856606 +vt 0.555069 0.872374 +vt 0.550426 0.853695 +vt 0.545670 0.851083 +vt 0.550680 0.845886 +vt 0.544860 0.841251 +vt 0.555718 0.842489 +vt 0.560769 0.839991 +vt 0.551115 0.835534 +vt 0.557747 0.829358 +vt 0.564569 0.822953 +vt 0.595138 0.866397 +vt 0.592535 0.883124 +vt 0.587483 0.873880 +vt 0.589346 0.899639 +vt 0.583602 0.886355 +vt 0.590825 0.864487 +vt 0.584377 0.861877 +vt 0.590106 0.853381 +vt 0.582244 0.848493 +vt 0.579016 0.872554 +vt 0.575010 0.858494 +vt 0.596570 0.849247 +vt 0.589655 0.839342 +vt 0.597417 0.831886 +vt 0.911728 0.515517 +vt 0.908099 0.508151 +vt 0.921166 0.508615 +vt 0.904079 0.500933 +vt 0.899904 0.515287 +vt 0.895032 0.507687 +vt 0.889673 0.500232 +vt 0.923552 0.515748 +vt 0.934233 0.509079 +vt 0.947299 0.509543 +vt 0.947201 0.516210 +vt 0.918486 0.501634 +vt 0.932893 0.502334 +vt 0.947299 0.503035 +vt 0.935377 0.515979 +vt 0.936130 0.523189 +vt 0.925354 0.523185 +vt 0.946906 0.523193 +vt 0.914578 0.523180 +vt 0.903801 0.523176 +vt 0.810024 0.810382 +vt 0.800074 0.807310 +vt 0.804051 0.801634 +vt 0.790731 0.803589 +vt 0.814583 0.805436 +vt 0.808333 0.796649 +vt 0.812768 0.792009 +vt 0.825708 0.796764 +vt 0.793670 0.797670 +vt 0.796722 0.792320 +vt 0.799829 0.787255 +vt 0.819945 0.800978 +vt 0.831556 0.805307 +vt 0.825419 0.808913 +vt 0.843168 0.809636 +vt 0.836407 0.812228 +vt 0.838647 0.801519 +vt 0.851587 0.806273 +vt 0.821190 0.812156 +vt 0.832964 0.813281 +vt 0.198858 0.083728 +vt 0.192057 0.080641 +vt 0.207986 0.068744 +vt 0.215872 0.070918 +vt 0.224188 0.057562 +vt 0.223741 0.072316 +vt 0.205630 0.086714 +vt 0.231600 0.073327 +vt 0.212387 0.089649 +vt 0.232954 0.058287 +vt 0.241851 0.057919 +vt 0.250813 0.057005 +vt 0.187520 0.101111 +vt 0.169409 0.115509 +vt 0.165171 0.110243 +vt 0.193174 0.105971 +vt 0.173962 0.122294 +vt 0.181981 0.096896 +vt 0.176673 0.093970 +vt 0.161561 0.108015 +vt 0.501486 0.365140 +vt 0.481674 0.350575 +vt 0.485969 0.341926 +vt 0.507187 0.359078 +vt 0.490235 0.332701 +vt 0.462039 0.335826 +vt 0.464751 0.324774 +vt 0.467433 0.312273 +vt 0.528404 0.376231 +vt 0.521648 0.379337 +vt 0.549622 0.393383 +vt 0.541987 0.393351 +vt 0.513036 0.353129 +vt 0.535838 0.373557 +vt 0.558640 0.393985 +vt 0.516249 0.383309 +vt 0.496083 0.371425 +vt 0.537118 0.394457 +vt 0.477323 0.358070 +vt 0.459265 0.343980 +vt 0.347028 0.960602 +vt 0.338729 0.964508 +vt 0.339403 0.953558 +vt 0.347478 0.949282 +vt 0.340077 0.942608 +vt 0.330429 0.968415 +vt 0.331328 0.957834 +vt 0.332227 0.947254 +vt 0.355552 0.945006 +vt 0.355327 0.956695 +vt 0.363627 0.940730 +vt 0.363627 0.952789 +vt 0.347927 0.937962 +vt 0.355777 0.933316 +vt 0.363627 0.928670 +vt 0.355103 0.968384 +vt 0.354878 0.980074 +vt 0.346129 0.983241 +vt 0.363627 0.964848 +vt 0.363627 0.976907 +vt 0.346579 0.971921 +vt 0.338055 0.975458 +vt 0.329531 0.978995 +vt 0.337381 0.986408 +vt 0.328632 0.989575 +vt 0.974439 0.737794 +vt 0.965501 0.742754 +vt 0.963931 0.729511 +vt 0.973393 0.723260 +vt 0.962362 0.716268 +vt 0.956562 0.747714 +vt 0.954470 0.735763 +vt 0.952377 0.723812 +vt 0.982855 0.717008 +vt 0.983378 0.732835 +vt 0.992317 0.710756 +vt 0.992317 0.727875 +vt 0.972347 0.708725 +vt 0.982332 0.701181 +vt 0.992317 0.693638 +vt 0.983901 0.748661 +vt 0.975486 0.752329 +vt 0.984424 0.764488 +vt 0.992317 0.744993 +vt 0.992317 0.762112 +vt 0.967070 0.755997 +vt 0.958655 0.759665 +vt 0.976532 0.766864 +vt 0.968639 0.769240 +vt 0.960747 0.771616 +vn 0.708500 -0.230500 0.667000 +vn 0.738900 -0.117300 0.663500 +vn 0.741800 -0.116400 0.660500 +vn 0.711400 -0.230700 0.663800 +vn 0.789700 -0.126400 0.600300 +vn 0.749100 -0.000200 0.662400 +vn 0.751800 -0.000300 0.659400 +vn 0.800000 -0.000500 0.600000 +vn 0.657600 -0.341900 0.671300 +vn 0.655600 -0.339400 0.674500 +vn 0.577600 -0.445700 0.683900 +vn 0.577900 -0.441000 0.686700 +vn 0.757600 -0.253100 0.601600 +vn 0.699300 -0.378600 0.606300 +vn 0.611500 -0.497400 0.615300 +vn 0.673300 -0.354800 0.648600 +vn 0.727100 -0.241900 0.642400 +vn 0.705000 -0.385000 0.595600 +vn 0.594700 -0.460700 0.658800 +vn 0.622300 -0.500700 0.601700 +vn 0.758700 -0.123800 0.639600 +vn 0.769400 -0.000200 0.638700 +vn 0.761800 -0.262800 0.592000 +vn 0.795600 -0.134900 0.590500 +vn 0.807200 -0.000200 0.590300 +vn -0.708500 -0.230500 0.667000 +vn -0.727100 -0.241900 0.642400 +vn -0.673300 -0.354800 0.648600 +vn -0.655600 -0.339400 0.674500 +vn -0.594700 -0.460700 0.658800 +vn -0.761800 -0.262800 0.592000 +vn -0.705000 -0.385000 0.595600 +vn -0.622300 -0.500700 0.601700 +vn -0.657600 -0.341900 0.671300 +vn -0.711400 -0.230700 0.663800 +vn -0.699300 -0.378600 0.606300 +vn -0.757600 -0.253100 0.601600 +vn -0.577900 -0.441000 0.686700 +vn -0.577600 -0.445700 0.683900 +vn -0.611500 -0.497400 0.615300 +vn -0.741800 -0.116400 0.660500 +vn -0.738900 -0.117300 0.663500 +vn -0.751800 -0.000300 0.659400 +vn -0.789700 -0.126400 0.600300 +vn -0.800000 -0.000500 0.600000 +vn -0.758700 -0.123800 0.639600 +vn -0.795600 -0.134900 0.590500 +vn -0.749100 -0.000200 0.662400 +vn -0.769400 -0.000200 0.638700 +vn -0.807200 -0.000200 0.590300 +vn 0.816300 -0.298000 0.494700 +vn 0.854700 -0.153000 0.496000 +vn 0.831700 -0.145800 0.535700 +vn 0.795300 -0.283800 0.535600 +vn 0.867700 -0.000400 0.497100 +vn 0.844100 -0.000300 0.536200 +vn 0.734700 -0.415800 0.536000 +vn 0.752500 -0.436800 0.492800 +vn 0.646700 -0.541500 0.537100 +vn 0.660100 -0.569200 0.490000 +vn 0.761400 -0.449700 0.466800 +vn 0.827600 -0.306600 0.470200 +vn 0.767100 -0.447900 0.459200 +vn 0.665900 -0.586000 0.461700 +vn 0.673000 -0.584100 0.453700 +vn 0.867300 -0.157100 0.472200 +vn 0.880700 -0.000400 0.473600 +vn 0.832500 -0.304800 0.462700 +vn 0.871700 -0.155700 0.464600 +vn 0.884700 0.000100 0.466000 +vn -0.816300 -0.298000 0.494700 +vn -0.827600 -0.306600 0.470200 +vn -0.761400 -0.449700 0.466800 +vn -0.752500 -0.436800 0.492800 +vn -0.665900 -0.586000 0.461700 +vn -0.832500 -0.304800 0.462700 +vn -0.767100 -0.447900 0.459200 +vn -0.673000 -0.584100 0.453700 +vn -0.734700 -0.415800 0.536000 +vn -0.795300 -0.283800 0.535600 +vn -0.660100 -0.569200 0.490000 +vn -0.646700 -0.541500 0.537100 +vn -0.831700 -0.145800 0.535700 +vn -0.854700 -0.153000 0.496000 +vn -0.844100 -0.000300 0.536200 +vn -0.867300 -0.157100 0.472200 +vn -0.871700 -0.155700 0.464600 +vn -0.867700 -0.000400 0.497100 +vn -0.880700 -0.000400 0.473600 +vn -0.884700 0.000100 0.466000 +vn 0.409900 -0.772500 0.484900 +vn 0.543300 -0.684000 0.486800 +vn 0.535400 -0.650300 0.539000 +vn 0.408200 -0.734300 0.542300 +vn 0.518100 -0.599700 0.609900 +vn 0.264000 -0.793600 0.548100 +vn 0.259000 -0.834600 0.486200 +vn 0.100600 -0.824500 0.556900 +vn 0.088600 -0.866200 0.491800 +vn 0.400100 -0.675100 0.619700 +vn 0.267300 -0.728100 0.631100 +vn 0.116600 -0.756400 0.643600 +vn 0.254400 -0.856700 0.448600 +vn 0.408400 -0.794000 0.450200 +vn 0.261900 -0.860600 0.436700 +vn 0.081300 -0.888200 0.452200 +vn 0.088400 -0.894100 0.438900 +vn 0.545400 -0.703700 0.455300 +vn 0.416300 -0.795800 0.439700 +vn 0.553100 -0.703500 0.446200 +vn -0.409900 -0.772500 0.484900 +vn -0.408400 -0.794000 0.450200 +vn -0.254400 -0.856700 0.448600 +vn -0.259000 -0.834600 0.486200 +vn -0.081300 -0.888200 0.452200 +vn -0.416300 -0.795800 0.439700 +vn -0.261900 -0.860600 0.436700 +vn -0.088400 -0.894100 0.438900 +vn -0.264000 -0.793600 0.548100 +vn -0.408200 -0.734300 0.542300 +vn -0.267300 -0.728100 0.631100 +vn -0.400100 -0.675100 0.619700 +vn -0.088600 -0.866200 0.491800 +vn -0.100600 -0.824500 0.556900 +vn -0.116600 -0.756400 0.643600 +vn -0.535400 -0.650300 0.539000 +vn -0.543300 -0.684000 0.486800 +vn -0.518100 -0.599700 0.609900 +vn -0.545400 -0.703700 0.455300 +vn -0.553100 -0.703500 0.446200 +vn 0.377800 -0.584300 0.718100 +vn 0.481900 -0.524400 0.701900 +vn 0.479800 -0.530100 0.699100 +vn 0.375700 -0.589900 0.714700 +vn 0.504100 -0.595500 0.625400 +vn 0.263900 -0.630700 0.729700 +vn 0.264700 -0.625100 0.734200 +vn 0.139500 -0.653800 0.743600 +vn 0.138300 -0.647600 0.749300 +vn 0.389700 -0.666800 0.635200 +vn 0.265800 -0.716800 0.644600 +vn 0.126200 -0.745800 0.654100 +vn 0.266900 -0.660700 0.701600 +vn 0.387600 -0.615200 0.686500 +vn 0.130900 -0.685300 0.716400 +vn 0.496500 -0.549500 0.671900 +vn -0.377800 -0.584300 0.718100 +vn -0.387600 -0.615200 0.686500 +vn -0.266900 -0.660700 0.701600 +vn -0.264700 -0.625100 0.734200 +vn -0.130900 -0.685300 0.716400 +vn -0.263900 -0.630700 0.729700 +vn -0.375700 -0.589900 0.714700 +vn -0.265800 -0.716800 0.644600 +vn -0.389700 -0.666800 0.635200 +vn -0.138300 -0.647600 0.749300 +vn -0.139500 -0.653800 0.743600 +vn -0.126200 -0.745800 0.654100 +vn -0.479800 -0.530100 0.699100 +vn -0.481900 -0.524400 0.701900 +vn -0.504100 -0.595500 0.625400 +vn -0.496500 -0.549500 0.671900 +vn -0.128400 -0.622400 0.772100 +vn 0.004400 -0.647900 0.761700 +vn 0.006200 -0.655500 0.755200 +vn -0.127900 -0.630900 0.765200 +vn -0.024400 -0.747700 0.663600 +vn -0.260500 -0.576100 0.774700 +vn -0.258300 -0.567400 0.781800 +vn -0.387000 -0.486600 0.783200 +vn -0.381100 -0.478700 0.790900 +vn -0.175100 -0.717900 0.673800 +vn -0.322300 -0.654000 0.684300 +vn -0.462100 -0.553100 0.693200 +vn -0.284800 -0.596000 0.750800 +vn -0.151100 -0.655400 0.740000 +vn -0.332700 -0.652900 0.680400 +vn -0.409500 -0.502600 0.761300 +vn -0.463800 -0.551000 0.693700 +vn -0.012000 -0.684400 0.729000 +vn -0.190800 -0.719700 0.667600 +vn -0.040500 -0.754000 0.655600 +vn 0.128400 -0.622400 0.772100 +vn 0.151100 -0.655400 0.740000 +vn 0.284800 -0.596000 0.750800 +vn 0.258300 -0.567400 0.781800 +vn 0.409500 -0.502600 0.761300 +vn 0.190800 -0.719700 0.667600 +vn 0.332700 -0.652900 0.680400 +vn 0.463800 -0.551000 0.693700 +vn 0.260500 -0.576100 0.774700 +vn 0.127900 -0.630900 0.765200 +vn 0.322300 -0.654000 0.684300 +vn 0.175100 -0.717900 0.673800 +vn 0.381100 -0.478700 0.790900 +vn 0.387000 -0.486600 0.783200 +vn 0.462100 -0.553100 0.693200 +vn -0.006200 -0.655500 0.755200 +vn -0.004400 -0.647900 0.761700 +vn 0.024400 -0.747700 0.663600 +vn 0.012000 -0.684400 0.729000 +vn 0.040500 -0.754000 0.655600 +vn -0.251900 -0.819500 0.514700 +vn -0.086900 -0.860900 0.501200 +vn -0.068800 -0.820500 0.567500 +vn -0.228800 -0.781600 0.580300 +vn -0.378600 -0.708400 0.595600 +vn -0.406000 -0.743200 0.531800 +vn -0.516300 -0.598900 0.612200 +vn -0.547900 -0.629500 0.550800 +vn -0.419000 -0.762500 0.492900 +vn -0.263000 -0.839900 0.474700 +vn -0.419100 -0.771500 0.478600 +vn -0.563400 -0.647300 0.513300 +vn -0.565800 -0.656000 0.499500 +vn -0.096300 -0.882200 0.460900 +vn -0.260800 -0.848600 0.460300 +vn -0.091600 -0.889900 0.446800 +vn 0.251900 -0.819500 0.514700 +vn 0.263000 -0.839900 0.474700 +vn 0.419000 -0.762500 0.492900 +vn 0.406000 -0.743200 0.531800 +vn 0.563400 -0.647300 0.513300 +vn 0.260800 -0.848600 0.460300 +vn 0.419100 -0.771500 0.478600 +vn 0.565800 -0.656000 0.499500 +vn 0.378600 -0.708400 0.595600 +vn 0.228800 -0.781600 0.580300 +vn 0.547900 -0.629500 0.550800 +vn 0.516300 -0.598900 0.612200 +vn 0.068800 -0.820500 0.567500 +vn 0.086900 -0.860900 0.501200 +vn 0.096300 -0.882200 0.460900 +vn 0.091600 -0.889900 0.446800 +vn -0.743800 -0.332300 0.579900 +vn -0.663500 -0.487500 0.567400 +vn -0.627600 -0.462200 0.626500 +vn -0.703900 -0.313900 0.637200 +vn -0.569200 -0.423500 0.704700 +vn -0.747800 -0.160800 0.644200 +vn -0.790700 -0.170800 0.587900 +vn -0.762300 -0.000500 0.647200 +vn -0.640300 -0.286000 0.712800 +vn -0.680500 -0.145700 0.718100 +vn -0.693800 -0.000300 0.720100 +vn -0.813900 -0.176200 0.553500 +vn -0.764700 -0.343800 0.545000 +vn -0.821500 -0.178000 0.541600 +vn -0.806300 -0.000600 0.591400 +vn -0.830300 -0.000700 0.557300 +vn -0.837800 0.000000 0.546000 +vn -0.681900 -0.502600 0.531300 +vn -0.771200 -0.349100 0.532300 +vn -0.686700 -0.509900 0.518100 +vn 0.764700 -0.343800 0.545000 +vn 0.813900 -0.176200 0.553500 +vn 0.790700 -0.170800 0.587900 +vn 0.830300 -0.000700 0.557300 +vn 0.806300 -0.000600 0.591400 +vn 0.771200 -0.349100 0.532300 +vn 0.821500 -0.178000 0.541600 +vn 0.837800 0.000000 0.546000 +vn 0.743800 -0.332300 0.579900 +vn 0.747800 -0.160800 0.644200 +vn 0.703900 -0.313900 0.637200 +vn 0.680500 -0.145700 0.718100 +vn 0.762300 -0.000500 0.647200 +vn 0.693800 -0.000300 0.720100 +vn 0.627600 -0.462200 0.626500 +vn 0.663500 -0.487500 0.567400 +vn 0.640300 -0.286000 0.712800 +vn 0.569200 -0.423500 0.704700 +vn 0.681900 -0.502600 0.531300 +vn 0.686700 -0.509900 0.518100 +vn -0.546100 -0.244800 0.801100 +vn -0.480500 -0.365500 0.797200 +vn -0.490300 -0.371800 0.788200 +vn -0.559200 -0.249100 0.790700 +vn -0.577700 -0.425600 0.696500 +vn -0.597700 -0.124200 0.792000 +vn -0.582800 -0.122000 0.803400 +vn -0.610400 -0.000200 0.792000 +vn -0.594700 -0.000100 0.803900 +vn -0.657300 -0.287800 0.696500 +vn -0.703800 -0.144900 0.695400 +vn -0.719600 -0.000600 0.694400 +vn -0.614000 -0.130200 0.778500 +vn -0.626000 -0.000200 0.779800 +vn -0.576800 -0.258600 0.774800 +vn -0.509900 -0.384700 0.769400 +vn 0.546100 -0.244800 0.801100 +vn 0.576800 -0.258600 0.774800 +vn 0.614000 -0.130200 0.778500 +vn 0.626000 -0.000200 0.779800 +vn 0.594700 -0.000100 0.803900 +vn 0.582800 -0.122000 0.803400 +vn 0.597700 -0.124200 0.792000 +vn 0.559200 -0.249100 0.790700 +vn 0.703800 -0.144900 0.695400 +vn 0.657300 -0.287800 0.696500 +vn 0.610400 -0.000200 0.792000 +vn 0.719600 -0.000600 0.694400 +vn 0.490300 -0.371800 0.788200 +vn 0.480500 -0.365500 0.797200 +vn 0.577700 -0.425600 0.696500 +vn 0.509900 -0.384700 0.769400 +vn -0.553500 0.242000 0.796900 +vn -0.585100 0.121000 0.801900 +vn -0.601000 0.122000 0.789800 +vn -0.569500 0.243900 0.785000 +vn -0.707900 0.140900 0.692100 +vn -0.509900 0.367900 0.777600 +vn -0.495300 0.363000 0.789200 +vn -0.412300 0.492200 0.766600 +vn -0.669500 0.278200 0.688800 +vn -0.600600 0.416500 0.682500 +vn -0.492700 0.554100 0.671000 +vn -0.518700 0.382300 0.764600 +vn -0.572100 0.419000 0.705000 +vn -0.641200 0.282800 0.713300 +vn -0.399900 0.484300 0.778100 +vn -0.421400 0.505200 0.753100 +vn -0.467500 0.547200 0.694300 +vn -0.580800 0.255700 0.772800 +vn -0.615200 0.129200 0.777700 +vn -0.680500 0.143600 0.718500 +vn 0.553500 0.242000 0.796900 +vn 0.580800 0.255700 0.772800 +vn 0.518700 0.382300 0.764600 +vn 0.421400 0.505200 0.753100 +vn 0.399900 0.484300 0.778100 +vn 0.641200 0.282800 0.713300 +vn 0.572100 0.419000 0.705000 +vn 0.467500 0.547200 0.694300 +vn 0.495300 0.363000 0.789200 +vn 0.509900 0.367900 0.777600 +vn 0.569500 0.243900 0.785000 +vn 0.600600 0.416500 0.682500 +vn 0.412300 0.492200 0.766600 +vn 0.492700 0.554100 0.671000 +vn 0.601000 0.122000 0.789800 +vn 0.585100 0.121000 0.801900 +vn 0.669500 0.278200 0.688800 +vn 0.707900 0.140900 0.692100 +vn 0.615200 0.129200 0.777700 +vn 0.680500 0.143600 0.718500 +vn -0.789100 0.167700 0.590800 +vn -0.746900 0.158100 0.645900 +vn -0.701900 0.309700 0.641400 +vn -0.740300 0.326300 0.587700 +vn -0.625500 0.453900 0.634600 +vn -0.658900 0.475800 0.582500 +vn -0.512900 0.588300 0.625100 +vn -0.676900 0.488000 0.551000 +vn -0.761200 0.335500 0.555000 +vn -0.680000 0.495700 0.540300 +vn -0.766600 0.341200 0.543900 +vn -0.540900 0.614200 0.574500 +vn -0.555500 0.628700 0.544200 +vn -0.555700 0.637600 0.533500 +vn -0.812200 0.172900 0.557200 +vn -0.819100 0.176300 0.545900 +vn 0.761200 0.335500 0.555000 +vn 0.676900 0.488000 0.551000 +vn 0.658900 0.475800 0.582500 +vn 0.555500 0.628700 0.544200 +vn 0.540900 0.614200 0.574500 +vn 0.766600 0.341200 0.543900 +vn 0.680000 0.495700 0.540300 +vn 0.555700 0.637600 0.533500 +vn 0.740300 0.326300 0.587700 +vn 0.625500 0.453900 0.634600 +vn 0.701900 0.309700 0.641400 +vn 0.512900 0.588300 0.625100 +vn 0.746900 0.158100 0.645900 +vn 0.789100 0.167700 0.590800 +vn 0.812200 0.172900 0.557200 +vn 0.819100 0.176300 0.545900 +vn -0.396300 0.725400 0.562900 +vn -0.373400 0.696600 0.612600 +vn -0.222600 0.768500 0.599900 +vn -0.335500 0.650700 0.681100 +vn -0.192100 0.718600 0.668400 +vn -0.240900 0.799300 0.550400 +vn -0.062900 0.806100 0.588300 +vn -0.076800 0.838700 0.539200 +vn 0.104500 0.809500 0.577700 +vn -0.040700 0.753300 0.656400 +vn 0.116900 0.755600 0.644500 +vn -0.083900 0.856100 0.509800 +vn -0.249800 0.816200 0.520900 +vn -0.078000 0.862800 0.499500 +vn -0.245700 0.824200 0.510200 +vn 0.095600 0.842800 0.529700 +vn 0.090100 0.860700 0.501100 +vn 0.097300 0.865400 0.491500 +vn -0.407600 0.741400 0.533100 +vn -0.405600 0.750100 0.522400 +vn 0.249800 0.816200 0.520900 +vn 0.083900 0.856100 0.509800 +vn 0.076800 0.838700 0.539200 +vn -0.090100 0.860700 0.501100 +vn -0.095600 0.842800 0.529700 +vn 0.245700 0.824200 0.510200 +vn 0.078000 0.862800 0.499500 +vn -0.097300 0.865400 0.491500 +vn 0.240900 0.799300 0.550400 +vn 0.062900 0.806100 0.588300 +vn 0.222600 0.768400 0.599900 +vn 0.040700 0.753300 0.656400 +vn -0.104500 0.809500 0.577700 +vn -0.116900 0.755600 0.644500 +vn 0.373400 0.696600 0.612600 +vn 0.396300 0.725400 0.562900 +vn 0.192100 0.718600 0.668400 +vn 0.335500 0.650700 0.681100 +vn 0.407600 0.741400 0.533100 +vn 0.405600 0.750100 0.522400 +vn -0.277100 0.582000 0.764500 +vn -0.285700 0.593900 0.752100 +vn -0.147900 0.657900 0.738400 +vn -0.352400 0.669100 0.654200 +vn -0.198800 0.743900 0.638000 +vn -0.143500 0.643800 0.751600 +vn -0.006400 0.687300 0.726300 +vn -0.005200 0.673000 0.739600 +vn 0.133900 0.686600 0.714600 +vn -0.039700 0.779600 0.625000 +vn 0.118600 0.779300 0.615300 +vn -0.017900 0.698900 0.715000 +vn -0.160400 0.667700 0.726900 +vn 0.134200 0.673200 0.727100 +vn 0.128400 0.700100 0.702400 +vn -0.296500 0.604100 0.739700 +vn 0.160400 0.667700 0.726900 +vn 0.017900 0.698900 0.715000 +vn 0.005200 0.673000 0.739600 +vn -0.128400 0.700100 0.702400 +vn -0.134200 0.673200 0.727100 +vn 0.143500 0.643800 0.751600 +vn 0.006400 0.687300 0.726300 +vn 0.147900 0.657900 0.738400 +vn 0.039700 0.779600 0.625000 +vn -0.133900 0.686600 0.714600 +vn -0.118600 0.779300 0.615300 +vn 0.285700 0.593900 0.752100 +vn 0.277100 0.582000 0.764500 +vn 0.198800 0.743900 0.638000 +vn 0.352400 0.669100 0.654200 +vn 0.296500 0.604100 0.739700 +vn 0.266100 0.649200 0.712600 +vn 0.265600 0.661400 0.701400 +vn 0.384700 0.615100 0.688200 +vn 0.266200 0.748600 0.607200 +vn 0.399300 0.692500 0.600800 +vn 0.384700 0.604100 0.697900 +vn 0.494600 0.546100 0.676000 +vn 0.492900 0.537300 0.684300 +vn 0.594400 0.450400 0.666200 +vn 0.521400 0.610600 0.596000 +vn 0.631900 0.499000 0.593000 +vn 0.503300 0.556500 0.661000 +vn 0.391800 0.626500 0.673800 +vn 0.520500 0.597000 0.610400 +vn 0.401600 0.673600 0.620400 +vn 0.590200 0.445500 0.673100 +vn 0.602300 0.462600 0.650500 +vn 0.625100 0.496600 0.602100 +vn 0.267700 0.674600 0.687900 +vn 0.267900 0.727200 0.631900 +vn -0.391800 0.626500 0.673800 +vn -0.503300 0.556500 0.661000 +vn -0.492900 0.537300 0.684300 +vn -0.602300 0.462600 0.650500 +vn -0.590200 0.445500 0.673100 +vn -0.401600 0.673600 0.620400 +vn -0.520500 0.597000 0.610400 +vn -0.625100 0.496600 0.602100 +vn -0.384700 0.604100 0.697900 +vn -0.494600 0.546100 0.676000 +vn -0.384700 0.615100 0.688200 +vn -0.521400 0.610600 0.596000 +vn -0.594400 0.450400 0.666200 +vn -0.631900 0.499000 0.593000 +vn -0.265600 0.661400 0.701400 +vn -0.266100 0.649200 0.712600 +vn -0.399300 0.692500 0.600800 +vn -0.266200 0.748600 0.607200 +vn -0.267700 0.674600 0.687900 +vn -0.267900 0.727200 0.631900 +vn 0.262000 0.811500 0.522200 +vn 0.265600 0.779000 0.568000 +vn 0.408300 0.720700 0.560200 +vn 0.410200 0.751000 0.517300 +vn 0.534900 0.638000 0.553900 +vn 0.542200 0.664900 0.513700 +vn 0.646300 0.530800 0.548200 +vn 0.544800 0.680600 0.489900 +vn 0.409600 0.768300 0.491900 +vn 0.551300 0.680500 0.482700 +vn 0.416200 0.769600 0.484100 +vn 0.658600 0.553300 0.509900 +vn 0.664700 0.566300 0.487300 +vn 0.670800 0.565200 0.480100 +vn 0.258600 0.829400 0.495100 +vn 0.265500 0.832400 0.486400 +vn -0.409600 0.768300 0.491900 +vn -0.544800 0.680600 0.489900 +vn -0.542200 0.664900 0.513700 +vn -0.664700 0.566300 0.487300 +vn -0.658600 0.553300 0.509900 +vn -0.416200 0.769600 0.484100 +vn -0.551300 0.680500 0.482700 +vn -0.670800 0.565200 0.480100 +vn -0.410200 0.751000 0.517300 +vn -0.534900 0.638000 0.553900 +vn -0.408300 0.720700 0.560200 +vn -0.646300 0.530800 0.548200 +vn -0.265600 0.779000 0.568000 +vn -0.262000 0.811500 0.522200 +vn -0.258600 0.829400 0.495100 +vn -0.265500 0.832400 0.486400 +vn 0.751000 0.425100 0.505200 +vn 0.734200 0.407600 0.543000 +vn 0.794600 0.278900 0.539200 +vn 0.707200 0.380500 0.595900 +vn 0.762700 0.259700 0.592200 +vn 0.814800 0.291300 0.501200 +vn 0.831100 0.143900 0.537100 +vn 0.853700 0.150400 0.498500 +vn 0.795800 0.133500 0.590600 +vn 0.866200 0.153900 0.475400 +vn 0.826000 0.297900 0.478500 +vn 0.870400 0.153600 0.467800 +vn 0.830500 0.297200 0.471000 +vn 0.760100 0.434900 0.482800 +vn 0.765200 0.434000 0.475500 +vn -0.826000 0.297900 0.478500 +vn -0.866200 0.153900 0.475400 +vn -0.853700 0.150400 0.498500 +vn -0.830500 0.297200 0.471000 +vn -0.870400 0.153600 0.467800 +vn -0.814800 0.291300 0.501200 +vn -0.831100 0.143900 0.537100 +vn -0.794600 0.278900 0.539200 +vn -0.795800 0.133500 0.590600 +vn -0.734200 0.407600 0.543000 +vn -0.751000 0.425100 0.505200 +vn -0.762700 0.259700 0.592200 +vn -0.707200 0.380500 0.595900 +vn -0.760100 0.434900 0.482800 +vn -0.765200 0.434000 0.475500 +vn 0.665200 0.337900 0.665800 +vn 0.670600 0.338700 0.660000 +vn 0.718300 0.226200 0.657900 +vn 0.715300 0.371100 0.592100 +vn 0.765800 0.245500 0.594300 +vn 0.713600 0.227800 0.662500 +vn 0.744000 0.114200 0.658300 +vn 0.740600 0.115800 0.661900 +vn 0.792300 0.123100 0.597600 +vn 0.759700 0.122700 0.638600 +vn 0.730200 0.239600 0.639700 +vn 0.679300 0.353100 0.643300 +vn -0.730200 0.239600 0.639700 +vn -0.759700 0.122700 0.638600 +vn -0.740600 0.115800 0.661900 +vn -0.713600 0.227800 0.662500 +vn -0.744000 0.114200 0.658300 +vn -0.718300 0.226200 0.657900 +vn -0.792300 0.123100 0.597600 +vn -0.670600 0.338700 0.660000 +vn -0.665200 0.337900 0.665800 +vn -0.765800 0.245500 0.594300 +vn -0.715300 0.371100 0.592100 +vn -0.679300 0.353100 0.643300 +vn 0.858600 0.131100 0.495600 +vn 0.856000 0.135800 0.498700 +vn 0.828900 0.273600 0.487800 +vn 0.860600 -0.000500 0.509200 +vn 0.863000 -0.000500 0.505200 +vn 0.836500 0.271500 0.475900 +vn 0.771400 0.416500 0.481000 +vn 0.773600 0.416300 0.477600 +vn 0.676700 0.562100 0.475500 +vn 0.685400 0.337600 0.645100 +vn 0.745500 0.221000 0.628800 +vn 0.536100 0.217900 0.815500 +vn 0.581100 0.143200 0.801100 +vn 0.677900 0.557900 0.478700 +vn 0.607800 0.444500 0.658000 +vn 0.486200 0.278600 0.828200 +vn 0.757900 0.100200 0.644600 +vn 0.751400 -0.001900 0.659800 +vn 0.587700 0.059200 0.806800 +vn 0.582400 -0.002000 0.812900 +vn -0.745500 0.221000 0.628800 +vn -0.685400 0.337600 0.645100 +vn -0.773600 0.416300 0.477600 +vn -0.607800 0.444500 0.658000 +vn -0.677900 0.557900 0.478700 +vn -0.581100 0.143200 0.801100 +vn -0.536100 0.217800 0.815500 +vn -0.486200 0.278600 0.828200 +vn -0.836500 0.271500 0.475900 +vn -0.771400 0.416500 0.481000 +vn -0.828900 0.273600 0.487800 +vn -0.676700 0.562100 0.475500 +vn -0.856000 0.135800 0.498700 +vn -0.858600 0.131100 0.495600 +vn -0.863000 -0.000500 0.505200 +vn -0.860600 -0.000500 0.509200 +vn -0.757900 0.100200 0.644600 +vn -0.587700 0.059200 0.806800 +vn -0.751400 -0.001900 0.659800 +vn -0.582400 -0.002000 0.812900 +vn 0.561900 0.694100 0.449800 +vn 0.553100 0.692700 0.462800 +vn 0.413400 0.791900 0.449400 +vn 0.420400 0.806800 0.415100 +vn 0.258300 0.857100 0.445700 +vn 0.255700 0.873100 0.415000 +vn 0.088200 0.889200 0.448900 +vn 0.270500 0.734500 0.622300 +vn 0.416500 0.677800 0.605900 +vn 0.269100 0.468800 0.841300 +vn 0.372200 0.435200 0.819800 +vn 0.083100 0.900800 0.426000 +vn 0.127500 0.753300 0.645200 +vn 0.177700 0.475600 0.861500 +vn 0.528800 0.563300 0.634800 +vn 0.444000 0.353100 0.823500 +vn -0.416500 0.677800 0.605900 +vn -0.270500 0.734500 0.622300 +vn -0.255700 0.873100 0.415000 +vn -0.127500 0.753300 0.645200 +vn -0.083100 0.900800 0.426000 +vn -0.372200 0.435200 0.819800 +vn -0.269100 0.468800 0.841300 +vn -0.177700 0.475600 0.861500 +vn -0.420400 0.806800 0.415100 +vn -0.258300 0.857100 0.445700 +vn -0.413400 0.791900 0.449400 +vn -0.088200 0.889200 0.448900 +vn -0.553100 0.692700 0.462800 +vn -0.561900 0.694100 0.449800 +vn -0.528800 0.563300 0.634800 +vn -0.444000 0.353100 0.823500 +vn -0.100000 0.902600 0.418600 +vn -0.092300 0.886600 0.453200 +vn -0.272500 0.842600 0.464400 +vn -0.290500 0.860600 0.418200 +vn -0.441900 0.753200 0.487200 +vn -0.462900 0.760800 0.454800 +vn -0.593200 0.624100 0.508500 +vn -0.360800 0.645300 0.673300 +vn -0.206700 0.747600 0.631100 +vn -0.164300 0.408000 0.898100 +vn -0.054100 0.488800 0.870700 +vn -0.607600 0.628700 0.485200 +vn -0.476100 0.525600 0.705000 +vn -0.236600 0.325300 0.915500 +vn -0.027300 0.771200 0.636000 +vn 0.077800 0.496500 0.864500 +vn 0.206700 0.747600 0.631100 +vn 0.360800 0.645300 0.673300 +vn 0.462900 0.760800 0.454800 +vn 0.476100 0.525600 0.705000 +vn 0.607600 0.628700 0.485200 +vn 0.054100 0.488800 0.870700 +vn 0.164300 0.408000 0.898100 +vn 0.236600 0.325300 0.915500 +vn 0.290500 0.860600 0.418200 +vn 0.441900 0.753200 0.487200 +vn 0.272500 0.842600 0.464400 +vn 0.593200 0.624100 0.508500 +vn 0.092300 0.886600 0.453200 +vn 0.100000 0.902600 0.418600 +vn 0.027300 0.771200 0.636000 +vn -0.077800 0.496500 0.864500 +vn -0.733800 0.491700 0.468800 +vn -0.714000 0.476500 0.512900 +vn -0.796900 0.325000 0.509200 +vn -0.830500 0.341100 0.440200 +vn -0.843800 0.165700 0.510400 +vn -0.858100 -0.000500 0.513400 +vn -0.888700 -0.001800 0.458500 +vn -0.879000 0.170300 0.445400 +vn -0.737800 0.139100 0.660500 +vn -0.701800 0.299300 0.646400 +vn -0.419100 0.075500 0.904800 +vn -0.403800 0.187200 0.895500 +vn -0.735300 -0.002400 0.677700 +vn -0.407000 -0.000400 0.913400 +vn -0.595500 0.424900 0.681800 +vn -0.320700 0.268500 0.908300 +vn 0.701800 0.299300 0.646400 +vn 0.737800 0.139100 0.660500 +vn 0.879000 0.170300 0.445400 +vn 0.735300 -0.002400 0.677700 +vn 0.888700 -0.001800 0.458500 +vn 0.403800 0.187200 0.895500 +vn 0.419100 0.075500 0.904800 +vn 0.407000 -0.000400 0.913400 +vn 0.830500 0.341100 0.440200 +vn 0.843800 0.165700 0.510400 +vn 0.858100 -0.000500 0.513400 +vn 0.796900 0.325000 0.509200 +vn 0.714000 0.476500 0.512900 +vn 0.733800 0.491700 0.468800 +vn 0.595500 0.424900 0.681800 +vn 0.320700 0.268500 0.908300 +vn -0.822700 -0.358000 0.441600 +vn -0.877000 -0.175600 0.447200 +vn -0.840900 -0.172100 0.513100 +vn -0.786000 -0.339200 0.516800 +vn -0.692000 -0.493500 0.526800 +vn -0.717400 -0.513900 0.470300 +vn -0.564400 -0.633100 0.529700 +vn -0.587600 -0.646200 0.486900 +vn -0.588900 -0.449300 0.671800 +vn -0.699400 -0.316000 0.641100 +vn -0.323600 -0.290400 0.900500 +vn -0.469600 -0.550100 0.690600 +vn -0.238800 -0.349300 0.906000 +vn -0.739500 -0.143500 0.657600 +vn -0.407600 -0.199300 0.891100 +vn -0.420600 -0.079500 0.903700 +vn 0.822700 -0.358000 0.441600 +vn 0.699400 -0.316000 0.641100 +vn 0.588900 -0.449300 0.671800 +vn 0.717400 -0.513900 0.470300 +vn 0.469600 -0.550100 0.690600 +vn 0.407600 -0.199400 0.891100 +vn 0.323600 -0.290400 0.900500 +vn 0.238800 -0.349300 0.906000 +vn 0.692000 -0.493500 0.526800 +vn 0.786000 -0.339200 0.516800 +vn 0.587600 -0.646200 0.486900 +vn 0.564400 -0.633100 0.529700 +vn 0.840900 -0.172100 0.513100 +vn 0.877000 -0.175600 0.447200 +vn 0.739500 -0.143500 0.657600 +vn 0.420600 -0.079500 0.903700 +vn -0.280500 -0.861300 0.423600 +vn -0.446100 -0.768900 0.458100 +vn -0.414600 -0.749400 0.516100 +vn -0.251700 -0.829000 0.499300 +vn -0.079300 -0.867700 0.490700 +vn -0.096500 -0.900500 0.424000 +vn 0.095800 -0.868400 0.486400 +vn 0.084500 -0.898400 0.430900 +vn -0.033800 -0.789900 0.612300 +vn -0.209800 -0.765700 0.608000 +vn 0.070000 -0.525400 0.847900 +vn 0.122900 -0.773300 0.622000 +vn 0.174000 -0.504300 0.845800 +vn -0.358500 -0.666200 0.653900 +vn -0.062500 -0.516500 0.854000 +vn -0.170200 -0.432800 0.885300 +vn 0.280500 -0.861300 0.423600 +vn 0.209800 -0.765700 0.608000 +vn 0.033800 -0.789900 0.612300 +vn 0.096500 -0.900500 0.423900 +vn -0.122900 -0.773300 0.622000 +vn 0.062500 -0.516500 0.854000 +vn -0.070000 -0.525400 0.847900 +vn -0.174000 -0.504300 0.845800 +vn 0.079300 -0.867700 0.490700 +vn 0.251700 -0.829000 0.499300 +vn -0.084500 -0.898400 0.430900 +vn -0.095800 -0.868400 0.486400 +vn 0.414600 -0.749400 0.516100 +vn 0.446000 -0.768900 0.458100 +vn 0.358500 -0.666200 0.653900 +vn 0.170200 -0.432800 0.885300 +vn 0.413600 -0.807100 0.421400 +vn 0.254800 -0.870600 0.420800 +vn 0.259800 -0.836700 0.482100 +vn 0.405900 -0.776000 0.482800 +vn 0.537000 -0.686200 0.490600 +vn 0.549100 -0.701100 0.454800 +vn 0.656500 -0.568100 0.496100 +vn 0.662200 -0.573400 0.482400 +vn 0.523200 -0.584000 0.620500 +vn 0.412800 -0.696600 0.586800 +vn 0.445100 -0.375400 0.813000 +vn 0.601000 -0.467400 0.648200 +vn 0.486200 -0.298400 0.821300 +vn 0.268100 -0.753600 0.600100 +vn 0.373700 -0.460600 0.805000 +vn 0.269500 -0.496400 0.825200 +vn -0.413600 -0.807100 0.421400 +vn -0.412800 -0.696600 0.586800 +vn -0.523300 -0.584000 0.620500 +vn -0.549100 -0.701100 0.454800 +vn -0.601000 -0.467400 0.648200 +vn -0.373700 -0.460600 0.805000 +vn -0.445100 -0.375400 0.813000 +vn -0.486200 -0.298400 0.821300 +vn -0.537000 -0.686200 0.490600 +vn -0.405900 -0.776000 0.482800 +vn -0.662200 -0.573400 0.482400 +vn -0.656500 -0.568100 0.496100 +vn -0.259800 -0.836700 0.482100 +vn -0.254800 -0.870600 0.420800 +vn -0.268100 -0.753600 0.600100 +vn -0.269500 -0.496400 0.825200 +vn 0.831000 -0.285200 0.477600 +vn 0.761300 -0.435500 0.480400 +vn 0.755400 -0.430200 0.494200 +vn 0.821100 -0.284900 0.494600 +vn 0.853900 -0.140400 0.501100 +vn 0.857500 -0.136100 0.496100 +vn 0.756500 -0.104600 0.645600 +vn 0.743600 -0.232300 0.626900 +vn 0.590900 -0.063000 0.804200 +vn 0.680200 -0.359000 0.639000 +vn 0.584000 -0.151500 0.797500 +vn 0.536100 -0.234600 0.810900 +vn -0.831000 -0.285200 0.477600 +vn -0.743600 -0.232300 0.626900 +vn -0.756500 -0.104600 0.645600 +vn -0.857500 -0.136100 0.496100 +vn -0.584000 -0.151500 0.797500 +vn -0.590900 -0.063000 0.804200 +vn -0.853900 -0.140400 0.501100 +vn -0.821100 -0.284900 0.494600 +vn -0.755400 -0.430200 0.494200 +vn -0.761300 -0.435500 0.480400 +vn -0.680200 -0.359000 0.639000 +vn -0.536100 -0.234600 0.810900 +vn 0.398700 -0.032800 0.916500 +vn 0.346200 -0.045600 0.937000 +vn 0.376600 -0.094000 0.921600 +vn 0.301700 -0.072100 0.950700 +vn 0.347400 -0.118700 0.930200 +vn 0.391700 -0.000200 0.920100 +vn 0.332800 -0.000400 0.943000 +vn 0.194500 -0.001400 0.980900 +vn 0.410700 -0.072200 0.908900 +vn 0.431900 -0.148700 0.889600 +vn 0.462900 -0.096200 0.881200 +vn 0.399900 -0.183800 0.897900 +vn 0.469300 -0.037500 0.882200 +vn 0.464500 -0.000200 0.885600 +vn -0.410700 -0.072200 0.908900 +vn -0.398700 -0.032800 0.916500 +vn -0.469300 -0.037500 0.882200 +vn -0.462900 -0.096200 0.881200 +vn -0.391700 -0.000200 0.920100 +vn -0.464500 -0.000200 0.885600 +vn -0.431900 -0.148700 0.889600 +vn -0.376600 -0.094000 0.921600 +vn -0.399900 -0.183800 0.897900 +vn -0.346200 -0.045600 0.937000 +vn -0.332800 -0.000400 0.943000 +vn -0.347400 -0.118700 0.930200 +vn -0.301700 -0.072100 0.950700 +vn -0.194500 -0.001400 0.980900 +vn 0.302600 -0.199800 0.931900 +vn 0.257100 -0.189000 0.947700 +vn 0.257600 -0.295600 0.919900 +vn 0.326800 -0.276600 0.903700 +vn 0.206300 -0.182400 0.961300 +vn 0.198600 -0.297200 0.933900 +vn 0.376600 -0.226300 0.898300 +vn 0.327300 -0.153600 0.932300 +vn 0.271800 -0.117600 0.955100 +vn 0.203800 -0.106600 0.973200 +vn -0.257100 -0.189000 0.947700 +vn -0.271800 -0.117600 0.955100 +vn -0.327300 -0.153600 0.932300 +vn -0.206300 -0.182400 0.961300 +vn -0.203800 -0.106600 0.973200 +vn -0.302600 -0.199800 0.931900 +vn -0.376600 -0.226300 0.898300 +vn -0.326800 -0.276600 0.903700 +vn -0.257600 -0.295600 0.919900 +vn -0.198600 -0.297200 0.933900 +vn 0.148000 -0.191900 0.970200 +vn 0.129800 -0.112500 0.985100 +vn 0.054800 -0.155800 0.986200 +vn 0.083200 -0.063600 0.994500 +vn 0.019000 -0.111600 0.993600 +vn 0.086700 -0.206900 0.974500 +vn -0.027900 -0.244900 0.969100 +vn 0.046000 -0.298700 0.953200 +vn -0.070000 -0.194000 0.978500 +vn 0.133600 -0.309600 0.941400 +vn -0.086700 -0.206900 0.974500 +vn -0.148000 -0.191900 0.970200 +vn -0.133600 -0.309600 0.941400 +vn -0.046000 -0.298700 0.953200 +vn 0.027900 -0.244900 0.969100 +vn -0.054800 -0.155800 0.986200 +vn 0.070000 -0.194000 0.978500 +vn -0.129800 -0.112500 0.985100 +vn -0.019000 -0.111600 0.993600 +vn -0.083200 -0.063600 0.994500 +vn -0.029500 -0.083400 0.996100 +vn 0.015300 -0.031600 0.999400 +vn -0.062000 -0.020600 0.997900 +vn 0.033500 -0.000200 0.999400 +vn -0.051400 0.000000 0.998700 +vn -0.080500 -0.061200 0.994800 +vn -0.179100 -0.032900 0.983200 +vn -0.171000 -0.098500 0.980300 +vn -0.170100 -0.000100 0.985400 +vn -0.121100 -0.157400 0.980100 +vn 0.080500 -0.061200 0.994800 +vn 0.029500 -0.083400 0.996100 +vn 0.121100 -0.157400 0.980100 +vn 0.171000 -0.098500 0.980300 +vn 0.179100 -0.032900 0.983200 +vn 0.062000 -0.020600 0.997900 +vn 0.170100 -0.000100 0.985400 +vn -0.015300 -0.031600 0.999400 +vn 0.051400 0.000000 0.998700 +vn -0.033500 -0.000200 0.999400 +vn -0.061800 0.019400 0.997900 +vn 0.015100 0.029800 0.999400 +vn -0.029300 0.078000 0.996500 +vn 0.083500 0.060700 0.994600 +vn 0.019800 0.105000 0.994300 +vn -0.080200 0.057300 0.995100 +vn -0.122900 0.147800 0.981300 +vn -0.070400 0.181000 0.980900 +vn -0.170600 0.094900 0.980700 +vn -0.178500 0.031300 0.983400 +vn 0.080200 0.057300 0.995100 +vn 0.061800 0.019400 0.997900 +vn 0.178500 0.031300 0.983400 +vn 0.170600 0.094900 0.980700 +vn 0.122900 0.147800 0.981300 +vn 0.029400 0.078000 0.996500 +vn 0.070400 0.181000 0.980900 +vn -0.015100 0.029800 0.999400 +vn -0.019800 0.105000 0.994300 +vn -0.083500 0.060700 0.994600 +vn 0.056500 0.146900 0.987500 +vn 0.130200 0.106800 0.985700 +vn 0.149800 0.181000 0.972000 +vn 0.203900 0.101200 0.973700 +vn 0.206700 0.172200 0.963100 +vn 0.089300 0.195000 0.976700 +vn 0.137500 0.291000 0.946800 +vn 0.199900 0.279200 0.939100 +vn 0.050700 0.280700 0.958400 +vn -0.024200 0.229700 0.972900 +vn -0.089300 0.195000 0.976700 +vn -0.056500 0.146900 0.987500 +vn 0.024200 0.229700 0.972900 +vn -0.050700 0.280700 0.958400 +vn -0.137500 0.291000 0.946800 +vn -0.149800 0.181000 0.972000 +vn -0.199900 0.279200 0.939100 +vn -0.130200 0.106800 0.985700 +vn -0.206700 0.172200 0.963100 +vn -0.203900 0.101200 0.973700 +vn 0.256300 0.178500 0.949900 +vn 0.271600 0.111600 0.955900 +vn 0.324400 0.145000 0.934700 +vn 0.301500 0.068800 0.951000 +vn 0.346900 0.110300 0.931400 +vn 0.301300 0.188400 0.934700 +vn 0.375100 0.214100 0.901900 +vn 0.400800 0.171600 0.899900 +vn 0.324900 0.260200 0.909200 +vn 0.256400 0.277900 0.925700 +vn -0.301300 0.188400 0.934700 +vn -0.256300 0.178500 0.949900 +vn -0.256400 0.277900 0.925700 +vn -0.324900 0.260200 0.909200 +vn -0.375100 0.214100 0.901900 +vn -0.324400 0.145000 0.934700 +vn -0.400800 0.171600 0.899900 +vn -0.271600 0.111600 0.955900 +vn -0.346900 0.110300 0.931400 +vn -0.301500 0.068800 0.951000 +vn 0.376600 0.088100 0.922100 +vn 0.346400 0.043200 0.937100 +vn 0.398600 0.031200 0.916600 +vn 0.410400 0.067800 0.909300 +vn 0.468900 0.035700 0.882500 +vn 0.462400 0.090000 0.882100 +vn 0.431700 0.138300 0.891300 +vn -0.410400 0.067800 0.909300 +vn -0.376600 0.088100 0.922100 +vn -0.431700 0.138300 0.891300 +vn -0.462400 0.090000 0.882100 +vn -0.468900 0.035700 0.882500 +vn -0.398600 0.031200 0.916600 +vn -0.346400 0.043200 0.937100 +vn 0.098900 -0.954100 0.282500 +vn 0.132800 -0.953800 0.269600 +vn 0.134300 -0.900600 0.413300 +vn 0.157800 -0.953100 0.258300 +vn 0.162100 -0.899000 0.406700 +vn 0.095100 -0.987400 0.126100 +vn 0.125600 -0.986100 0.108700 +vn 0.146100 -0.984900 0.092900 +vn 0.099100 -0.901200 0.421900 +vn 0.130200 -0.839700 0.527200 +vn 0.095200 -0.841900 0.531100 +vn 0.122600 -0.784900 0.607300 +vn 0.159000 -0.835200 0.526400 +vn 0.151800 -0.774200 0.614500 +vn 0.052400 -0.842900 0.535500 +vn 0.054800 -0.901500 0.429300 +vn 0.000000 -0.843100 0.537600 +vn 0.000000 -0.901600 0.432500 +vn 0.088600 -0.790900 0.605500 +vn 0.048300 -0.793600 0.606400 +vn 0.000000 -0.794100 0.607700 +vn 0.054900 -0.954500 0.293100 +vn 0.053200 -0.988700 0.140300 +vn 0.000000 -0.954700 0.297500 +vn 0.000000 -0.989200 0.146200 +vn -0.054800 -0.901500 0.429300 +vn -0.052400 -0.842900 0.535500 +vn -0.095200 -0.841900 0.531200 +vn -0.048300 -0.793600 0.606400 +vn -0.088600 -0.790900 0.605500 +vn -0.099100 -0.901200 0.421900 +vn -0.130200 -0.839700 0.527200 +vn -0.134300 -0.900600 0.413300 +vn -0.159000 -0.835200 0.526400 +vn -0.122600 -0.784900 0.607300 +vn -0.151800 -0.774200 0.614500 +vn -0.132800 -0.953800 0.269600 +vn -0.098900 -0.954100 0.282500 +vn -0.125600 -0.986100 0.108700 +vn -0.095100 -0.987400 0.126100 +vn -0.162100 -0.899000 0.406700 +vn -0.157800 -0.953100 0.258300 +vn -0.146100 -0.984900 0.092900 +vn -0.054900 -0.954500 0.293100 +vn -0.053200 -0.988700 0.140300 +vn 0.259000 -0.927500 0.269700 +vn 0.365200 -0.880200 0.303100 +vn 0.355400 -0.802400 0.479300 +vn 0.513600 -0.783300 0.350000 +vn 0.466300 -0.705200 0.534000 +vn 0.246600 -0.965400 0.084500 +vn 0.370300 -0.924200 0.093500 +vn 0.564200 -0.818100 0.111100 +vn 0.263700 -0.861200 0.434600 +vn 0.340200 -0.711300 0.615000 +vn 0.259600 -0.780800 0.568300 +vn 0.320200 -0.622300 0.714300 +vn 0.424300 -0.614200 0.665300 +vn 0.386500 -0.532500 0.753000 +vn 0.197500 -0.819800 0.537400 +vn 0.199400 -0.889700 0.410500 +vn 0.249200 -0.699400 0.669900 +vn 0.190200 -0.749500 0.634100 +vn 0.192500 -0.947300 0.255900 +vn 0.177200 -0.980500 0.084700 +vn -0.199400 -0.889700 0.410500 +vn -0.197500 -0.819800 0.537400 +vn -0.259600 -0.780800 0.568300 +vn -0.190200 -0.749500 0.634100 +vn -0.249200 -0.699400 0.669900 +vn -0.263700 -0.861200 0.434600 +vn -0.340200 -0.711300 0.615000 +vn -0.355400 -0.802400 0.479300 +vn -0.424300 -0.614200 0.665300 +vn -0.320200 -0.622300 0.714300 +vn -0.386500 -0.532500 0.753000 +vn -0.365200 -0.880200 0.303100 +vn -0.259000 -0.927500 0.269700 +vn -0.370300 -0.924200 0.093500 +vn -0.246600 -0.965400 0.084500 +vn -0.466300 -0.705200 0.534000 +vn -0.513600 -0.783300 0.350000 +vn -0.564200 -0.818100 0.111100 +vn -0.192500 -0.947300 0.255900 +vn -0.177200 -0.980500 0.084700 +vn 0.673900 -0.429600 0.601000 +vn 0.804900 -0.429300 0.409600 +vn 0.878100 -0.257900 0.402900 +vn 0.742600 -0.299100 0.599200 +vn 0.915000 -0.130100 0.381800 +vn 0.920100 -0.370100 0.127800 +vn 0.978400 -0.168400 0.119500 +vn 0.993400 -0.041900 0.106200 +vn 0.605900 -0.306300 0.734200 +vn 0.556100 -0.402100 0.727300 +vn 0.496000 -0.293700 0.817100 +vn 0.465900 -0.362300 0.807200 +vn 0.787300 -0.188200 0.587000 +vn 0.647200 -0.217800 0.730500 +vn 0.525900 -0.226800 0.819700 +vn 0.493600 -0.509500 0.704800 +vn 0.432700 -0.442300 0.785500 +vn 0.576200 -0.575900 0.579900 +vn 0.676700 -0.623800 0.391000 +vn 0.778400 -0.615100 0.125600 +vn -0.673900 -0.429600 0.601000 +vn -0.576200 -0.575900 0.579900 +vn -0.493600 -0.509500 0.704800 +vn -0.556100 -0.402100 0.727300 +vn -0.432700 -0.442300 0.785500 +vn -0.605900 -0.306300 0.734200 +vn -0.742600 -0.299100 0.599200 +vn -0.647200 -0.217800 0.730500 +vn -0.787300 -0.188200 0.587000 +vn -0.465900 -0.362300 0.807200 +vn -0.496000 -0.293700 0.817100 +vn -0.525900 -0.226800 0.819700 +vn -0.878100 -0.257900 0.402900 +vn -0.978400 -0.168400 0.119500 +vn -0.920100 -0.370100 0.127800 +vn -0.915000 -0.130100 0.381800 +vn -0.993400 -0.041900 0.106200 +vn -0.804900 -0.429300 0.409600 +vn -0.676700 -0.623800 0.391000 +vn -0.778400 -0.615100 0.125600 +vn 0.823900 -0.034500 0.565700 +vn 0.934400 0.015600 0.355800 +vn 0.932800 0.056100 0.355800 +vn 0.929200 0.084400 0.359900 +vn 0.823300 0.049000 0.565400 +vn 0.992800 0.073200 0.094600 +vn 0.990100 0.101000 0.097000 +vn 0.987800 0.118400 0.101400 +vn 0.825600 0.013900 0.564000 +vn 0.701900 -0.019500 0.712000 +vn 0.695200 -0.070800 0.715200 +vn 0.583600 -0.044100 0.810800 +vn 0.574200 -0.094700 0.813200 +vn 0.703100 0.017400 0.710900 +vn 0.586000 -0.008800 0.810200 +vn 0.677600 -0.138000 0.722300 +vn 0.812800 -0.100900 0.573700 +vn 0.553900 -0.158700 0.817300 +vn 0.930400 -0.043600 0.363900 +vn 0.994900 0.029000 0.096700 +vn -0.823900 -0.034500 0.565700 +vn -0.812800 -0.100900 0.573700 +vn -0.677600 -0.138000 0.722300 +vn -0.695200 -0.070800 0.715200 +vn -0.553900 -0.158700 0.817300 +vn -0.701900 -0.019500 0.712000 +vn -0.825600 0.013900 0.564000 +vn -0.703100 0.017400 0.710900 +vn -0.823300 0.049000 0.565400 +vn -0.574200 -0.094700 0.813200 +vn -0.583600 -0.044100 0.810800 +vn -0.586000 -0.008800 0.810200 +vn -0.932800 0.056100 0.355800 +vn -0.990100 0.101000 0.097000 +vn -0.992800 0.073200 0.094600 +vn -0.929200 0.084400 0.359900 +vn -0.987800 0.118400 0.101400 +vn -0.934400 0.015600 0.355800 +vn -0.930400 -0.043600 0.363900 +vn -0.994900 0.029000 0.096700 +vn 0.921700 0.130300 0.365300 +vn 0.917400 0.158800 0.364900 +vn 0.824600 0.129100 0.550800 +vn 0.911000 0.199100 0.361200 +vn 0.827500 0.166900 0.536100 +vn 0.981700 0.150500 0.116400 +vn 0.975900 0.176100 0.128500 +vn 0.966000 0.215800 0.142300 +vn 0.720800 0.093000 0.686800 +vn 0.711100 0.067800 0.699800 +vn 0.603600 0.054800 0.795300 +vn 0.593500 0.034500 0.804100 +vn 0.733800 0.126100 0.667500 +vn 0.619000 0.080800 0.781200 +vn 0.822200 0.101000 0.560100 +vn 0.705300 0.044400 0.707400 +vn 0.821800 0.076000 0.564700 +vn 0.588100 0.015200 0.808600 +vn 0.925500 0.107000 0.363300 +vn 0.985400 0.132400 0.107000 +vn -0.822200 0.101000 0.560100 +vn -0.821800 0.076000 0.564700 +vn -0.705300 0.044400 0.707400 +vn -0.711100 0.067800 0.699800 +vn -0.588100 0.015200 0.808600 +vn -0.720800 0.093000 0.686800 +vn -0.824600 0.129100 0.550800 +vn -0.733800 0.126100 0.667500 +vn -0.827500 0.166900 0.536100 +vn -0.593500 0.034500 0.804100 +vn -0.603600 0.054800 0.795300 +vn -0.619000 0.080800 0.781200 +vn -0.917400 0.158800 0.364900 +vn -0.921700 0.130300 0.365300 +vn -0.975900 0.176100 0.128500 +vn -0.981700 0.150500 0.116400 +vn -0.911000 0.199100 0.361200 +vn -0.966000 0.215800 0.142300 +vn -0.925500 0.107000 0.363300 +vn -0.985400 0.132400 0.107000 +vn 0.371000 -0.778600 0.506000 +vn 0.422300 -0.759300 0.495000 +vn 0.399100 -0.829600 0.390400 +vn 0.346200 -0.846900 0.403600 +vn 0.377800 -0.887000 0.265700 +vn 0.579200 -0.651400 0.490000 +vn 0.561000 -0.734400 0.381800 +vn 0.540800 -0.796700 0.269800 +vn 0.338100 -0.841300 0.421700 +vn 0.359400 -0.771300 0.525300 +vn 0.358600 -0.823800 0.438900 +vn 0.370100 -0.751000 0.546800 +vn 0.326500 -0.907400 0.264400 +vn 0.323400 -0.909600 0.260700 +vn 0.354900 -0.899900 0.253300 +vn 0.379700 -0.686500 0.620000 +vn 0.393300 -0.685300 0.612900 +vn 0.389300 -0.554200 0.735700 +vn 0.383000 -0.673400 0.632300 +vn 0.386500 -0.569000 0.725800 +vn 0.439900 -0.654800 0.614500 +vn 0.583500 -0.528900 0.616200 +vn 0.403300 -0.517000 0.755000 +vn 0.406700 -0.431700 0.805100 +vn 0.310100 -0.206000 0.928100 +vn -0.371000 -0.778600 0.506000 +vn -0.393300 -0.685300 0.612900 +vn -0.379700 -0.686500 0.620000 +vn -0.359400 -0.771300 0.525300 +vn -0.383000 -0.673400 0.632300 +vn -0.403300 -0.517000 0.755000 +vn -0.389300 -0.554200 0.735700 +vn -0.386500 -0.569000 0.725800 +vn -0.338100 -0.841300 0.421700 +vn -0.346200 -0.846900 0.403600 +vn -0.323400 -0.909600 0.260700 +vn -0.326500 -0.907400 0.264400 +vn -0.370100 -0.751000 0.546800 +vn -0.358600 -0.823800 0.438900 +vn -0.354900 -0.899900 0.253300 +vn -0.399100 -0.829600 0.390400 +vn -0.422300 -0.759300 0.495000 +vn -0.561000 -0.734400 0.381800 +vn -0.377800 -0.887000 0.265700 +vn -0.540800 -0.796700 0.269800 +vn -0.439900 -0.654800 0.614500 +vn -0.406700 -0.431700 0.805100 +vn -0.579200 -0.651400 0.490000 +vn -0.583500 -0.528900 0.616200 +vn -0.310100 -0.206000 0.928100 +vn 0.438400 -0.686500 0.580100 +vn 0.396900 -0.721800 0.566900 +vn 0.399500 -0.798100 0.451000 +vn 0.454900 -0.766900 0.452600 +vn 0.409700 -0.879100 0.243600 +vn 0.524400 -0.727700 0.442100 +vn 0.496100 -0.644000 0.582400 +vn 0.600200 -0.677200 0.425600 +vn 0.568200 -0.590200 0.573400 +vn 0.474600 -0.850200 0.227900 +vn 0.545300 -0.811900 0.208300 +vn 0.614100 -0.765900 0.190600 +vn 0.472700 -0.568600 0.673200 +vn 0.428800 -0.612800 0.663700 +vn 0.449400 -0.493900 0.744400 +vn 0.532100 -0.514100 0.672700 +vn 0.491900 -0.440800 0.750800 +vn 0.399500 -0.648000 0.648400 +vn 0.418000 -0.535600 0.733700 +vn 0.396500 -0.562000 0.725900 +vn -0.438400 -0.686500 0.580100 +vn -0.428800 -0.612800 0.663700 +vn -0.472700 -0.568600 0.673200 +vn -0.496100 -0.644000 0.582400 +vn -0.532100 -0.514100 0.672700 +vn -0.418000 -0.535600 0.733700 +vn -0.449400 -0.493900 0.744400 +vn -0.491900 -0.440800 0.750800 +vn -0.524400 -0.727700 0.442100 +vn -0.454900 -0.766900 0.452600 +vn -0.545300 -0.811900 0.208300 +vn -0.474600 -0.850200 0.227900 +vn -0.568200 -0.590200 0.573400 +vn -0.600200 -0.677200 0.425600 +vn -0.614100 -0.765900 0.190600 +vn -0.399500 -0.798100 0.451000 +vn -0.396900 -0.721800 0.566900 +vn -0.409700 -0.879100 0.243600 +vn -0.399500 -0.648000 0.648400 +vn -0.396500 -0.562000 0.725900 +vn 0.683700 -0.476500 0.552700 +vn 0.635600 -0.531800 0.559700 +vn 0.670700 -0.616600 0.412200 +vn 0.726400 -0.551800 0.409600 +vn 0.681900 -0.709100 0.179100 +vn 0.767800 -0.486500 0.416900 +vn 0.712700 -0.427000 0.556400 +vn 0.801300 -0.416300 0.429500 +vn 0.749700 -0.637300 0.178300 +vn 0.815300 -0.549400 0.183000 +vn 0.881200 -0.433600 0.188300 +vn 0.658100 -0.378200 0.651100 +vn 0.633100 -0.415200 0.653200 +vn 0.593100 -0.334000 0.732600 +vn 0.726100 -0.388200 0.567500 +vn 0.661200 -0.355900 0.660400 +vn 0.596400 -0.318100 0.737000 +vn 0.591100 -0.460200 0.662400 +vn 0.571800 -0.359600 0.737400 +vn 0.537200 -0.393600 0.745900 +vn -0.683700 -0.476500 0.552700 +vn -0.633100 -0.415200 0.653200 +vn -0.658100 -0.378200 0.651100 +vn -0.712700 -0.427000 0.556400 +vn -0.661200 -0.355900 0.660400 +vn -0.571800 -0.359600 0.737400 +vn -0.593100 -0.334000 0.732600 +vn -0.596400 -0.318100 0.737000 +vn -0.767800 -0.486500 0.416900 +vn -0.726400 -0.551800 0.409600 +vn -0.815300 -0.549400 0.183000 +vn -0.749700 -0.637300 0.178300 +vn -0.726100 -0.388200 0.567500 +vn -0.801300 -0.416300 0.429500 +vn -0.881200 -0.433600 0.188300 +vn -0.670700 -0.616600 0.412200 +vn -0.635600 -0.531800 0.559700 +vn -0.681900 -0.709100 0.179100 +vn -0.591100 -0.460200 0.662400 +vn -0.537200 -0.393600 0.745900 +vn 0.727600 -0.354100 0.587500 +vn 0.821900 -0.351400 0.448300 +vn 0.838700 -0.278600 0.467800 +vn 0.928500 -0.313800 0.198300 +vn 0.960200 -0.188700 0.205800 +vn 0.724500 -0.315600 0.612800 +vn 0.855900 -0.172400 0.487500 +vn 0.724300 -0.256700 0.639900 +vn 0.860000 0.025400 0.509600 +vn 0.977000 -0.027900 0.211300 +vn 0.950000 0.223100 0.218200 +vn 0.608400 -0.300900 0.734400 +vn 0.626300 -0.323300 0.709300 +vn 0.504300 -0.312300 0.805100 +vn 0.728300 -0.134400 0.672000 +vn 0.602300 -0.246600 0.759200 +vn 0.476400 -0.315600 0.820600 +vn 0.647000 -0.338700 0.683100 +vn 0.542600 -0.307800 0.781500 +vn 0.576600 -0.310300 0.755800 +vn -0.626300 -0.323300 0.709300 +vn -0.608400 -0.300900 0.734400 +vn -0.724300 -0.256700 0.639900 +vn -0.602300 -0.246600 0.759200 +vn -0.728300 -0.134400 0.672000 +vn -0.542600 -0.307800 0.781500 +vn -0.504300 -0.312300 0.805100 +vn -0.476400 -0.315600 0.820600 +vn -0.724500 -0.315600 0.612800 +vn -0.855900 -0.172400 0.487500 +vn -0.838700 -0.278600 0.467800 +vn -0.977000 -0.027900 0.211300 +vn -0.860000 0.025400 0.509600 +vn -0.950000 0.223100 0.218200 +vn -0.821900 -0.351400 0.448300 +vn -0.727600 -0.354100 0.587500 +vn -0.960200 -0.188700 0.205800 +vn -0.928500 -0.313800 0.198300 +vn -0.647000 -0.338700 0.683100 +vn -0.576600 -0.310300 0.755800 +vn 0.710400 0.076500 0.699500 +vn 0.797900 0.309800 0.517100 +vn 0.662900 0.563500 0.493000 +vn 0.822000 0.526100 0.217700 +vn 0.631100 0.749200 0.201000 +vn 0.640400 0.312900 0.701400 +vn 0.545800 0.708900 0.446700 +vn 0.560600 0.489300 0.668100 +vn 0.509100 0.760200 0.403500 +vn 0.497300 0.849500 0.176200 +vn 0.466800 0.871100 0.152800 +vn 0.521700 0.205300 0.828000 +vn 0.568400 0.043500 0.821600 +vn 0.397100 -0.116700 0.910300 +vn 0.438400 -0.205200 0.875000 +vn 0.523200 0.580900 0.623500 +vn 0.487900 0.318200 0.812800 +vn 0.357100 -0.041100 0.933100 +vn 0.598900 -0.126400 0.790800 +vn 0.464300 -0.284600 0.838700 +vn -0.568400 0.043500 0.821600 +vn -0.521700 0.205300 0.828000 +vn -0.560600 0.489300 0.668100 +vn -0.487900 0.318200 0.812800 +vn -0.523200 0.580900 0.623500 +vn -0.438400 -0.205200 0.875000 +vn -0.397100 -0.116700 0.910300 +vn -0.357100 -0.041100 0.933100 +vn -0.640400 0.312900 0.701400 +vn -0.545800 0.708900 0.446700 +vn -0.662900 0.563500 0.493000 +vn -0.497300 0.849500 0.176200 +vn -0.509100 0.760200 0.403500 +vn -0.466800 0.871100 0.152800 +vn -0.797900 0.309800 0.517100 +vn -0.710400 0.076500 0.699500 +vn -0.631100 0.749200 0.201000 +vn -0.822000 0.526100 0.217700 +vn -0.598900 -0.126400 0.790800 +vn -0.464300 -0.284600 0.838700 +vn 0.517300 0.615400 0.594700 +vn 0.517600 0.767800 0.377500 +vn 0.533500 0.761400 0.368300 +vn 0.486600 0.862800 0.137400 +vn 0.511800 0.849000 0.131500 +vn 0.521300 0.621700 0.584600 +vn 0.556400 0.742600 0.372700 +vn 0.532300 0.608600 0.588400 +vn 0.587700 0.710700 0.386500 +vn 0.544400 0.827800 0.135200 +vn 0.586500 0.796300 0.147600 +vn 0.460600 0.393400 0.795700 +vn 0.461700 0.397200 0.793100 +vn 0.307000 0.046700 0.950600 +vn 0.315000 0.034700 0.948400 +vn 0.551200 0.580100 0.599700 +vn 0.465500 0.373500 0.802300 +vn 0.304600 0.048300 0.951200 +vn 0.469300 0.377200 0.798400 +vn 0.328500 0.009200 0.944500 +vn -0.461700 0.397200 0.793100 +vn -0.460600 0.393400 0.795700 +vn -0.532300 0.608600 0.588400 +vn -0.465500 0.373500 0.802300 +vn -0.551200 0.580100 0.599700 +vn -0.315000 0.034700 0.948400 +vn -0.307000 0.046700 0.950600 +vn -0.304600 0.048300 0.951200 +vn -0.521300 0.621700 0.584600 +vn -0.556400 0.742600 0.372700 +vn -0.533500 0.761400 0.368300 +vn -0.544400 0.827800 0.135200 +vn -0.587700 0.710700 0.386500 +vn -0.586500 0.796300 0.147600 +vn -0.517600 0.767800 0.377500 +vn -0.517300 0.615400 0.594700 +vn -0.511800 0.849000 0.131500 +vn -0.486600 0.862800 0.137400 +vn -0.469300 0.377200 0.798400 +vn -0.328500 0.009200 0.944500 +vn 0.558800 0.557000 0.614300 +vn 0.605500 0.686800 0.402000 +vn 0.586000 0.693900 0.418300 +vn 0.612300 0.773900 0.161600 +vn 0.594200 0.785700 0.171900 +vn 0.539500 0.556000 0.632300 +vn 0.514000 0.735900 0.440600 +vn 0.362700 0.805000 0.469400 +vn 0.351100 0.626900 0.695500 +vn 0.516900 0.837100 0.179100 +vn 0.345900 0.920000 0.184000 +vn 0.476100 0.583200 0.658100 +vn 0.402400 0.363900 0.840000 +vn 0.450400 0.354000 0.819600 +vn 0.277600 0.056000 0.959000 +vn 0.312100 0.382400 0.869600 +vn 0.241400 0.063700 0.968300 +vn 0.466800 0.357600 0.808800 +vn 0.297400 0.050400 0.953400 +vn 0.305600 0.048200 0.950900 +vn -0.539500 0.556000 0.632300 +vn -0.450400 0.354000 0.819600 +vn -0.402400 0.363900 0.840000 +vn -0.476100 0.583200 0.658100 +vn -0.312100 0.382400 0.869600 +vn -0.297400 0.050400 0.953400 +vn -0.277600 0.056000 0.959000 +vn -0.241400 0.063700 0.968300 +vn -0.514000 0.735900 0.440600 +vn -0.586000 0.693900 0.418300 +vn -0.516900 0.837100 0.179100 +vn -0.351100 0.626900 0.695500 +vn -0.362700 0.805000 0.469400 +vn -0.345900 0.920000 0.184000 +vn -0.605500 0.686800 0.402000 +vn -0.558800 0.557000 0.614300 +vn -0.594200 0.785700 0.171900 +vn -0.612300 0.773900 0.161600 +vn -0.466800 0.357600 0.808800 +vn -0.305600 0.048200 0.950900 +vn 0.162000 0.660000 0.733500 +vn 0.130800 0.858900 0.495000 +vn -0.139100 0.851700 0.505100 +vn 0.088600 0.978600 0.185300 +vn -0.204400 0.962200 0.179800 +vn -0.058300 0.653900 0.754300 +vn -0.403100 0.771800 0.491700 +vn -0.280600 0.599600 0.749500 +vn -0.617800 0.641600 0.454700 +vn -0.480900 0.861100 0.164700 +vn -0.695500 0.704800 0.139800 +vn -0.119600 0.357600 0.926100 +vn 0.033300 0.387800 0.921100 +vn 0.067400 0.057700 0.996000 +vn 0.128400 0.066000 0.989500 +vn -0.473500 0.510900 0.717500 +vn -0.261800 0.312000 0.913300 +vn 0.008000 0.045100 0.998900 +vn 0.183000 0.394800 0.900300 +vn 0.189300 0.067700 0.979600 +vn -0.033300 0.387800 0.921100 +vn 0.119600 0.357600 0.926100 +vn 0.280600 0.599600 0.749500 +vn 0.261800 0.312000 0.913300 +vn 0.473500 0.510900 0.717500 +vn -0.128400 0.066000 0.989500 +vn -0.067400 0.057700 0.996000 +vn -0.008000 0.045100 0.998900 +vn 0.058300 0.653900 0.754300 +vn 0.403100 0.771800 0.491700 +vn 0.139100 0.851700 0.505100 +vn 0.480900 0.861100 0.164700 +vn 0.617800 0.641600 0.454700 +vn 0.695500 0.704800 0.139800 +vn -0.130800 0.858900 0.495000 +vn -0.162000 0.660000 0.733500 +vn 0.204400 0.962200 0.179800 +vn -0.088600 0.978600 0.185300 +vn -0.183000 0.394800 0.900300 +vn -0.189300 0.067700 0.979600 +vn -0.603000 0.430000 0.671800 +vn -0.748500 0.522800 0.407900 +vn -0.808100 0.459400 0.368600 +vn -0.817900 0.564600 0.110900 +vn -0.866800 0.490600 0.088600 +vn -0.673400 0.387800 0.629400 +vn -0.828800 0.444700 0.339700 +vn -0.707300 0.381100 0.595400 +vn -0.810300 0.484900 0.328900 +vn -0.877900 0.472800 0.075300 +vn -0.851400 0.518700 0.078000 +vn -0.483700 0.253500 0.837700 +vn -0.438600 0.253300 0.862200 +vn -0.119300 0.025000 0.992500 +vn -0.081300 0.027900 0.996300 +vn -0.702500 0.414700 0.578400 +vn -0.497700 0.273100 0.823200 +vn -0.148800 0.020300 0.988600 +vn -0.367700 0.272000 0.889200 +vn -0.039100 0.033400 0.998700 +vn 0.438600 0.253300 0.862200 +vn 0.483700 0.253500 0.837700 +vn 0.707300 0.381100 0.595400 +vn 0.497700 0.273100 0.823200 +vn 0.702500 0.414700 0.578400 +vn 0.081300 0.027900 0.996300 +vn 0.119300 0.025000 0.992500 +vn 0.148800 0.020300 0.988600 +vn 0.673400 0.387800 0.629400 +vn 0.828800 0.444700 0.339700 +vn 0.808100 0.459400 0.368600 +vn 0.877900 0.472800 0.075300 +vn 0.810300 0.484900 0.328900 +vn 0.851400 0.518700 0.078000 +vn 0.748500 0.522800 0.407900 +vn 0.603000 0.430000 0.671800 +vn 0.866800 0.490600 0.088600 +vn 0.817900 0.564600 0.110900 +vn 0.367700 0.272000 0.889200 +vn 0.039100 0.033400 0.998700 +vn -0.653600 0.481900 0.583500 +vn -0.746200 0.571700 0.341000 +vn -0.617100 0.692000 0.374500 +vn -0.779200 0.618500 0.101700 +vn -0.640400 0.754800 0.142000 +vn -0.546400 0.572300 0.611400 +vn -0.373800 0.823200 0.427300 +vn -0.339700 0.659800 0.670200 +vn 0.000000 0.888300 0.459200 +vn -0.382600 0.904500 0.188400 +vn 0.000000 0.977500 0.210900 +vn -0.249200 0.398100 0.882800 +vn -0.403100 0.359000 0.841800 +vn -0.077900 0.020000 0.996700 +vn -0.143500 0.027500 0.989300 +vn 0.000000 0.700900 0.713200 +vn 0.000000 0.384700 0.923000 +vn 0.000000 -0.087800 0.996100 +vn -0.474500 0.310000 0.823800 +vn -0.161900 0.021500 0.986500 +vn 0.403100 0.359000 0.841800 +vn 0.249200 0.398100 0.882800 +vn 0.339700 0.659800 0.670200 +vn 0.143500 0.027500 0.989300 +vn 0.077900 0.020000 0.996700 +vn 0.546400 0.572300 0.611400 +vn 0.373800 0.823200 0.427300 +vn 0.617100 0.692000 0.374500 +vn 0.382600 0.904500 0.188400 +vn 0.746200 0.571700 0.341000 +vn 0.653600 0.481900 0.583500 +vn 0.640400 0.754800 0.142000 +vn 0.779200 0.618500 0.101700 +vn 0.474500 0.310000 0.823800 +vn 0.161900 0.021500 0.986500 +vn 0.371900 -0.304900 0.876800 +vn 0.403600 -0.325100 0.855200 +vn 0.393400 -0.329000 0.858400 +vn 0.335800 -0.248700 0.908500 +vn 0.329300 -0.259900 0.907700 +vn 0.354100 -0.309500 0.882500 +vn 0.394900 -0.314800 0.863100 +vn 0.323500 -0.236000 0.916300 +vn 0.370500 -0.298000 0.879700 +vn 0.367800 -0.331900 0.868600 +vn 0.351800 -0.297000 0.887700 +vn 0.327600 -0.340700 0.881300 +vn 0.313800 -0.266100 0.911400 +vn 0.288100 -0.274300 0.917500 +vn 0.238100 -0.184800 0.953500 +vn 0.234700 -0.187600 0.953800 +vn 0.315400 -0.304600 0.898700 +vn 0.227500 -0.187500 0.955500 +vn 0.218300 -0.196900 0.955800 +vn 0.186300 -0.211200 0.959500 +vn -0.234700 -0.187600 0.953800 +vn -0.238100 -0.184800 0.953500 +vn -0.351800 -0.297000 0.887700 +vn -0.227500 -0.187500 0.955500 +vn -0.315400 -0.304600 0.898700 +vn -0.370500 -0.298000 0.879700 +vn -0.367800 -0.331900 0.868600 +vn -0.393400 -0.329000 0.858400 +vn -0.313800 -0.266100 0.911400 +vn -0.327600 -0.340700 0.881300 +vn -0.288100 -0.274300 0.917500 +vn -0.403600 -0.325100 0.855200 +vn -0.371900 -0.304900 0.876800 +vn -0.394900 -0.314800 0.863100 +vn -0.354100 -0.309500 0.882500 +vn -0.329300 -0.259900 0.907700 +vn -0.335800 -0.248700 0.908500 +vn -0.323500 -0.236000 0.916300 +vn -0.218300 -0.196900 0.955800 +vn -0.186300 -0.211200 0.959500 +vn 0.217000 -0.333300 0.917500 +vn 0.267800 -0.318900 0.909100 +vn 0.276500 -0.355200 0.892900 +vn 0.219400 -0.370600 0.902500 +vn 0.254200 -0.286300 0.923800 +vn 0.162200 -0.383400 0.909200 +vn 0.167000 -0.345600 0.923400 +vn 0.107900 -0.391500 0.913800 +vn 0.119900 -0.355600 0.926900 +vn 0.211900 -0.299300 0.930300 +vn 0.165500 -0.307600 0.937000 +vn 0.120500 -0.305600 0.944500 +vn 0.178000 -0.209700 0.961400 +vn 0.193900 -0.202200 0.959900 +vn 0.161900 -0.216700 0.962700 +vn 0.211200 -0.194500 0.957900 +vn -0.217000 -0.333300 0.917500 +vn -0.193900 -0.202200 0.959900 +vn -0.178000 -0.209700 0.961400 +vn -0.167000 -0.345600 0.923400 +vn -0.161900 -0.216700 0.962700 +vn -0.162200 -0.383400 0.909200 +vn -0.219400 -0.370600 0.902500 +vn -0.165500 -0.307600 0.937000 +vn -0.211900 -0.299300 0.930300 +vn -0.119900 -0.355600 0.926900 +vn -0.107900 -0.391500 0.913800 +vn -0.120500 -0.305600 0.944500 +vn -0.276500 -0.355200 0.892900 +vn -0.254200 -0.286300 0.923800 +vn -0.267800 -0.318900 0.909100 +vn -0.211200 -0.194500 0.957900 +vn 0.054800 -0.392000 0.918300 +vn 0.058400 -0.394600 0.917000 +vn 0.058200 -0.381200 0.922600 +vn 0.068900 -0.409600 0.909600 +vn 0.059200 -0.400800 0.914200 +vn 0.105200 -0.259900 0.959900 +vn 0.135800 -0.235000 0.962400 +vn 0.169600 -0.230700 0.958100 +vn 0.064000 -0.370300 0.926700 +vn 0.135900 -0.242600 0.960500 +vn 0.139400 -0.232200 0.962600 +vn 0.134700 -0.258100 0.956700 +vn 0.147900 -0.224300 0.963200 +vn 0.083300 -0.363200 0.927900 +vn 0.068800 -0.393300 0.916800 +vn 0.095700 -0.287900 0.952800 +vn -0.083300 -0.363200 0.927900 +vn -0.147900 -0.224300 0.963200 +vn -0.139400 -0.232200 0.962600 +vn -0.064000 -0.370300 0.926700 +vn -0.135900 -0.242600 0.960500 +vn -0.058200 -0.381200 0.922600 +vn -0.134700 -0.258100 0.956700 +vn -0.058400 -0.394600 0.917000 +vn -0.054800 -0.392000 0.918300 +vn -0.135800 -0.235000 0.962400 +vn -0.105200 -0.259900 0.959900 +vn -0.059200 -0.400800 0.914200 +vn -0.068900 -0.409600 0.909600 +vn -0.169600 -0.230700 0.958100 +vn -0.068800 -0.393300 0.916800 +vn -0.095700 -0.287900 0.952800 +vn 0.052600 -0.467500 0.882400 +vn 0.058900 -0.487700 0.871000 +vn 0.032300 -0.508300 0.860500 +vn 0.042900 -0.486100 0.872800 +vn 0.024800 -0.494600 0.868700 +vn 0.171900 -0.326400 0.929400 +vn 0.140000 -0.367400 0.919400 +vn 0.112900 -0.369400 0.922400 +vn 0.145000 -0.329800 0.932800 +vn 0.138600 -0.307700 0.941300 +vn 0.057500 -0.483600 0.873300 +vn 0.174500 -0.349400 0.920500 +vn 0.135100 -0.280100 0.950400 +vn 0.057300 -0.432000 0.900000 +vn 0.069200 -0.444500 0.893100 +vn 0.185100 -0.269900 0.944900 +vn -0.057300 -0.432000 0.900000 +vn -0.135100 -0.280100 0.950400 +vn -0.138600 -0.307700 0.941300 +vn -0.145000 -0.329800 0.932800 +vn -0.042900 -0.486100 0.872800 +vn -0.174500 -0.349400 0.920500 +vn -0.057500 -0.483600 0.873300 +vn -0.052600 -0.467500 0.882400 +vn -0.032300 -0.508300 0.860500 +vn -0.058900 -0.487700 0.871000 +vn -0.140000 -0.367400 0.919400 +vn -0.024800 -0.494600 0.868700 +vn -0.112900 -0.369400 0.922400 +vn -0.069200 -0.444500 0.893100 +vn -0.171900 -0.326400 0.929400 +vn -0.185100 -0.269900 0.944900 +vn 0.096500 -0.378100 0.920700 +vn 0.142100 -0.283000 0.948500 +vn 0.198800 -0.349800 0.915500 +vn 0.204500 -0.221000 0.953600 +vn 0.252400 -0.290400 0.923000 +vn 0.154000 -0.249900 0.955900 +vn 0.204100 -0.181500 0.961900 +vn 0.256200 -0.139600 0.956500 +vn 0.155800 -0.421200 0.893500 +vn 0.311600 -0.365900 0.876900 +vn 0.352000 -0.328200 0.876600 +vn 0.272100 -0.377900 0.884900 +vn 0.223500 -0.362800 0.904600 +vn 0.097300 -0.454100 0.885600 +vn 0.044800 -0.439000 0.897400 +vn 0.112800 -0.318200 0.941300 +vn -0.155800 -0.421200 0.893500 +vn -0.097300 -0.454100 0.885600 +vn -0.223500 -0.362800 0.904600 +vn -0.272100 -0.377900 0.884900 +vn -0.311600 -0.365900 0.876900 +vn -0.198800 -0.349800 0.915500 +vn -0.352000 -0.328200 0.876600 +vn -0.142100 -0.283000 0.948500 +vn -0.096500 -0.378100 0.920700 +vn -0.204100 -0.181500 0.961900 +vn -0.154000 -0.249900 0.955900 +vn -0.252400 -0.290400 0.923000 +vn -0.204500 -0.221000 0.953600 +vn -0.256200 -0.139600 0.956500 +vn -0.044800 -0.439000 0.897400 +vn -0.112800 -0.318200 0.941300 +vn 0.338600 -0.171400 0.925200 +vn 0.393400 -0.180500 0.901500 +vn 0.430200 -0.225200 0.874200 +vn 0.427700 -0.203700 0.880600 +vn 0.460900 -0.237000 0.855200 +vn 0.370200 -0.132100 0.919500 +vn 0.415500 -0.157500 0.895800 +vn 0.445800 -0.197400 0.873100 +vn 0.377600 -0.225300 0.898100 +vn 0.498000 -0.271900 0.823400 +vn 0.453400 -0.274400 0.848000 +vn 0.523000 -0.278200 0.805600 +vn 0.399900 -0.292400 0.868600 +vn 0.313900 -0.245500 0.917100 +vn 0.272300 -0.183600 0.944500 +vn 0.315300 -0.125600 0.940600 +vn -0.313900 -0.245500 0.917100 +vn -0.399900 -0.292400 0.868600 +vn -0.453400 -0.274400 0.848000 +vn -0.377600 -0.225300 0.898100 +vn -0.498000 -0.271900 0.823400 +vn -0.430200 -0.225200 0.874200 +vn -0.523000 -0.278200 0.805600 +vn -0.393400 -0.180500 0.901500 +vn -0.338600 -0.171400 0.925200 +vn -0.415500 -0.157500 0.895800 +vn -0.370200 -0.132100 0.919500 +vn -0.460900 -0.237000 0.855200 +vn -0.427700 -0.203700 0.880600 +vn -0.445800 -0.197400 0.873100 +vn -0.272300 -0.183600 0.944500 +vn -0.315300 -0.125600 0.940600 +vn 0.436500 -0.257000 0.862200 +vn 0.427000 -0.283100 0.858700 +vn 0.439400 -0.292800 0.849200 +vn 0.413600 -0.316900 0.853500 +vn 0.419500 -0.326600 0.846900 +vn 0.461400 -0.283200 0.840800 +vn 0.455000 -0.322500 0.830000 +vn 0.441200 -0.364000 0.820200 +vn 0.456200 -0.271200 0.847500 +vn 0.480600 -0.333900 0.810900 +vn 0.506100 -0.308700 0.805300 +vn 0.449700 -0.373800 0.811200 +vn 0.522600 -0.290700 0.801400 +vn 0.466000 -0.253600 0.847600 +vn 0.439200 -0.231500 0.868000 +vn 0.459600 -0.242200 0.854500 +vn -0.466000 -0.253600 0.847600 +vn -0.522600 -0.290700 0.801400 +vn -0.506100 -0.308700 0.805300 +vn -0.456200 -0.271200 0.847500 +vn -0.480600 -0.333900 0.810900 +vn -0.439400 -0.292800 0.849200 +vn -0.449700 -0.373800 0.811200 +vn -0.427000 -0.283100 0.858700 +vn -0.436500 -0.257000 0.862200 +vn -0.455000 -0.322500 0.830000 +vn -0.461400 -0.283200 0.840800 +vn -0.419500 -0.326600 0.846900 +vn -0.413600 -0.316900 0.853500 +vn -0.441200 -0.364000 0.820200 +vn -0.439200 -0.231500 0.868000 +vn -0.459600 -0.242200 0.854500 +vn 0.369500 -0.384000 0.846200 +vn 0.333300 -0.373000 0.865900 +vn 0.356500 -0.389300 0.849300 +vn 0.305000 -0.305300 0.902100 +vn 0.341900 -0.336800 0.877300 +vn 0.371000 -0.444300 0.815400 +vn 0.311600 -0.444100 0.840000 +vn 0.258100 -0.389000 0.884300 +vn 0.380100 -0.395000 0.836300 +vn 0.380200 -0.464600 0.799700 +vn 0.399400 -0.455400 0.795600 +vn 0.371400 -0.440100 0.817500 +vn 0.422500 -0.421100 0.802600 +vn 0.401400 -0.367300 0.839000 +vn 0.397100 -0.356200 0.845800 +vn 0.416000 -0.408800 0.812300 +vn -0.401400 -0.367300 0.839000 +vn -0.422500 -0.421100 0.802600 +vn -0.399400 -0.455400 0.795600 +vn -0.380100 -0.395000 0.836300 +vn -0.380200 -0.464600 0.799700 +vn -0.356500 -0.389300 0.849300 +vn -0.371400 -0.440100 0.817500 +vn -0.333300 -0.373000 0.865900 +vn -0.369500 -0.384000 0.846200 +vn -0.311600 -0.444100 0.840000 +vn -0.371000 -0.444300 0.815400 +vn -0.341900 -0.336800 0.877300 +vn -0.305000 -0.305300 0.902100 +vn -0.258100 -0.389000 0.884300 +vn -0.397100 -0.356200 0.845800 +vn -0.416000 -0.408800 0.812300 +vn 0.305400 -0.144000 0.941300 +vn 0.266300 -0.094100 0.959300 +vn 0.257500 -0.025900 0.965900 +vn 0.291000 -0.048200 0.955500 +vn 0.189200 0.025000 0.981600 +vn 0.205800 -0.196400 0.958600 +vn 0.192800 -0.128300 0.972800 +vn 0.148900 -0.080800 0.985500 +vn 0.317700 -0.186400 0.929700 +vn 0.364400 -0.285400 0.886400 +vn 0.193200 0.034000 0.980500 +vn 0.219300 -0.032500 0.975100 +vn 0.372900 -0.382500 0.845300 +vn 0.335700 -0.245800 0.909300 +vn 0.290700 -0.195300 0.936700 +vn 0.227000 -0.288700 0.930100 +vn -0.335700 -0.245800 0.909300 +vn -0.372900 -0.382500 0.845300 +vn -0.364400 -0.285400 0.886400 +vn -0.317700 -0.186400 0.929700 +vn -0.291000 -0.048200 0.955500 +vn -0.219300 -0.032500 0.975100 +vn -0.193200 0.034000 0.980500 +vn -0.305400 -0.144000 0.941300 +vn -0.257500 -0.025900 0.965900 +vn -0.266300 -0.094100 0.959300 +vn -0.192800 -0.128300 0.972800 +vn -0.189100 0.025000 0.981600 +vn -0.148900 -0.080800 0.985500 +vn -0.290700 -0.195300 0.936700 +vn -0.205800 -0.196400 0.958600 +vn -0.227000 -0.288700 0.930100 +vn -0.052700 0.117300 0.991700 +vn 0.044500 0.097600 0.994200 +vn 0.069300 0.063000 0.995600 +vn -0.017700 0.065600 0.997700 +vn 0.049900 -0.050600 0.997500 +vn -0.074900 0.047600 0.996000 +vn -0.096700 0.100200 0.990200 +vn -0.081200 -0.015800 0.996600 +vn -0.079200 0.025100 0.996500 +vn -0.033600 -0.045700 0.998400 +vn -0.099100 -0.050200 0.993800 +vn -0.123300 -0.087700 0.988500 +vn -0.128600 0.112300 0.985300 +vn -0.101300 0.123800 0.987100 +vn -0.151600 0.080500 0.985100 +vn -0.078400 0.037300 0.996200 +vn 0.000000 0.042200 0.999100 +vn 0.010400 0.087800 0.996100 +vn -0.164100 0.080800 0.983100 +vn -0.044900 0.021500 0.998700 +vn 0.052700 0.117300 0.991700 +vn 0.101300 0.123800 0.987100 +vn 0.128600 0.112300 0.985300 +vn 0.096700 0.100200 0.990200 +vn 0.078400 0.037300 0.996200 +vn 0.164100 0.080800 0.983100 +vn 0.151600 0.080500 0.985100 +vn 0.074900 0.047600 0.996000 +vn 0.017700 0.065600 0.997700 +vn 0.099100 -0.050200 0.993800 +vn 0.033600 -0.045700 0.998400 +vn 0.079200 0.025100 0.996500 +vn 0.081200 -0.015800 0.996600 +vn 0.123300 -0.087700 0.988500 +vn -0.069300 0.063000 0.995600 +vn -0.049900 -0.050600 0.997500 +vn -0.044500 0.097600 0.994200 +vn -0.010400 0.087800 0.996100 +vn 0.044900 0.021500 0.998700 +vn 0.312700 -0.273500 0.909600 +vn 0.359400 -0.300600 0.883400 +vn 0.307900 -0.311900 0.898800 +vn 0.251500 -0.193700 0.948200 +vn 0.292800 -0.217500 0.931100 +vn 0.258600 -0.298000 0.918800 +vn 0.142900 -0.222600 0.964400 +vn 0.111000 -0.224400 0.968100 +vn 0.089200 -0.224600 0.970300 +vn 0.216400 -0.278000 0.935900 +vn 0.100200 -0.248700 0.963400 +vn 0.181600 -0.268200 0.946100 +vn 0.266700 -0.252000 0.930200 +vn 0.197900 -0.181700 0.963200 +vn 0.218300 -0.224000 0.949800 +vn 0.150200 -0.149700 0.977200 +vn -0.216400 -0.278000 0.935900 +vn -0.089200 -0.224600 0.970300 +vn -0.111000 -0.224400 0.968100 +vn -0.181600 -0.268200 0.946100 +vn -0.100200 -0.248700 0.963400 +vn -0.258600 -0.298000 0.918800 +vn -0.142900 -0.222600 0.964400 +vn -0.307900 -0.311900 0.898800 +vn -0.359400 -0.300600 0.883400 +vn -0.312700 -0.273500 0.909600 +vn -0.292800 -0.217500 0.931100 +vn -0.251500 -0.193700 0.948200 +vn -0.266700 -0.252000 0.930200 +vn -0.218300 -0.224000 0.949800 +vn -0.197900 -0.181700 0.963200 +vn -0.150200 -0.149700 0.977200 +vn 0.113800 -0.237700 0.964600 +vn 0.112300 -0.189100 0.975500 +vn 0.165000 -0.197600 0.966200 +vn 0.153300 -0.246800 0.956800 +vn 0.040600 -0.136700 0.989800 +vn 0.097000 -0.137900 0.985700 +vn 0.096000 -0.272500 0.957300 +vn 0.075400 -0.270500 0.959700 +vn 0.000000 -0.272700 0.962100 +vn 0.000000 -0.282500 0.959200 +vn 0.053300 -0.260400 0.964000 +vn 0.074500 -0.229500 0.970400 +vn 0.031900 -0.240600 0.970100 +vn 0.000000 -0.272500 0.962100 +vn 0.000000 -0.252100 0.967700 +vn 0.060800 -0.186400 0.980600 +vn -0.014300 -0.143200 0.989600 +vn 0.042900 -0.215800 0.975500 +vn 0.015700 -0.184800 0.982600 +vn -0.061500 -0.153600 0.986200 +vn -0.113800 -0.237700 0.964600 +vn -0.074500 -0.229500 0.970400 +vn -0.053300 -0.260400 0.964000 +vn -0.075400 -0.270500 0.959700 +vn -0.042900 -0.215800 0.975500 +vn -0.031900 -0.240600 0.970100 +vn -0.096000 -0.272500 0.957300 +vn -0.153300 -0.246800 0.956800 +vn -0.165000 -0.197600 0.966200 +vn -0.112300 -0.189100 0.975500 +vn -0.097000 -0.137900 0.985700 +vn -0.060800 -0.186400 0.980600 +vn -0.015700 -0.184800 0.982600 +vn -0.040600 -0.136700 0.989800 +vn 0.014300 -0.143200 0.989600 +vn 0.061500 -0.153600 0.986200 +vn -0.001000 -0.150400 0.988600 +vn -0.024800 -0.064400 0.997600 +vn -0.039600 -0.073300 0.996500 +vn 0.000000 -0.151900 0.988400 +vn 0.000000 -0.065200 0.997900 +vn -0.012900 -0.146700 0.989100 +vn -0.062300 -0.093200 0.993700 +vn -0.043700 -0.146000 0.988300 +vn -0.121100 -0.132500 0.983700 +vn -0.020800 -0.168500 0.985500 +vn 0.013100 -0.192400 0.981200 +vn -0.112300 -0.156100 0.981300 +vn -0.095400 -0.154600 0.983300 +vn 0.014100 -0.211000 0.977400 +vn 0.000000 -0.216600 0.976300 +vn -0.013100 -0.192400 0.981200 +vn 0.020800 -0.168500 0.985500 +vn 0.043700 -0.146000 0.988300 +vn 0.095400 -0.154600 0.983300 +vn 0.112300 -0.156100 0.981300 +vn 0.012900 -0.146700 0.989100 +vn 0.062300 -0.093200 0.993700 +vn 0.039600 -0.073300 0.996500 +vn 0.121100 -0.132500 0.983700 +vn 0.024800 -0.064400 0.997600 +vn 0.001000 -0.150400 0.988600 +vn -0.014100 -0.211000 0.977400 +vn 0.070700 -0.708900 0.701700 +vn 0.104000 -0.695000 0.711400 +vn 0.089900 -0.644600 0.759200 +vn 0.057800 -0.660000 0.749000 +vn 0.062600 -0.566200 0.821900 +vn 0.135900 -0.670300 0.729500 +vn 0.122200 -0.616900 0.777500 +vn 0.090600 -0.544300 0.833900 +vn 0.027100 -0.664400 0.746800 +vn 0.035900 -0.713900 0.699300 +vn 0.000000 -0.663400 0.748300 +vn 0.000000 -0.713700 0.700400 +vn 0.036800 -0.578000 0.815200 +vn 0.015100 -0.580600 0.814100 +vn 0.000000 -0.579100 0.815200 +vn 0.042800 -0.754100 0.655400 +vn 0.080400 -0.749700 0.656800 +vn 0.000000 -0.754500 0.656300 +vn 0.114000 -0.739200 0.663800 +vn 0.144300 -0.720800 0.678000 +vn -0.070700 -0.708900 0.701700 +vn -0.080400 -0.749700 0.656800 +vn -0.042800 -0.754100 0.655400 +vn -0.035900 -0.713900 0.699300 +vn -0.027100 -0.664400 0.746800 +vn -0.057800 -0.660000 0.749000 +vn -0.015100 -0.580600 0.814100 +vn -0.036800 -0.578000 0.815200 +vn -0.089900 -0.644600 0.759200 +vn -0.104000 -0.695000 0.711400 +vn -0.122200 -0.616900 0.777500 +vn -0.062600 -0.566200 0.821900 +vn -0.090600 -0.544300 0.833900 +vn -0.114000 -0.739200 0.663700 +vn -0.135900 -0.670300 0.729500 +vn -0.144300 -0.720800 0.678000 +vn 0.225400 -0.564300 0.794200 +vn 0.280100 -0.487300 0.827100 +vn 0.251100 -0.439400 0.862500 +vn 0.205700 -0.511100 0.834500 +vn 0.209900 -0.398700 0.892700 +vn 0.323300 -0.405000 0.855200 +vn 0.293900 -0.359300 0.885700 +vn 0.252400 -0.328700 0.910100 +vn 0.159600 -0.573200 0.803700 +vn 0.174500 -0.628500 0.758000 +vn 0.165900 -0.460100 0.872200 +vn 0.124200 -0.509700 0.851300 +vn 0.182700 -0.686000 0.704200 +vn 0.237900 -0.626200 0.742400 +vn 0.299500 -0.545300 0.782900 +vn 0.347500 -0.461800 0.816000 +vn -0.225400 -0.564300 0.794200 +vn -0.237900 -0.626200 0.742400 +vn -0.182700 -0.686000 0.704200 +vn -0.174500 -0.628500 0.758000 +vn -0.159600 -0.573200 0.803700 +vn -0.205700 -0.511100 0.834500 +vn -0.124200 -0.509700 0.851300 +vn -0.165900 -0.460100 0.872200 +vn -0.251100 -0.439400 0.862500 +vn -0.293900 -0.359300 0.885700 +vn -0.323300 -0.405000 0.855200 +vn -0.209900 -0.398700 0.892700 +vn -0.252400 -0.328700 0.910100 +vn -0.280100 -0.487300 0.827100 +vn -0.299500 -0.545300 0.782900 +vn -0.347500 -0.461800 0.816000 +vn 0.357200 -0.238500 0.903000 +vn 0.317300 -0.200800 0.926800 +vn 0.324900 -0.239100 0.915000 +vn 0.276700 -0.166000 0.946500 +vn 0.289400 -0.211200 0.933600 +vn 0.356100 -0.204500 0.911800 +vn 0.302500 -0.172000 0.937500 +vn 0.221200 -0.114000 0.968500 +vn 0.357400 -0.279300 0.891200 +vn 0.318900 -0.291200 0.901900 +vn 0.348600 -0.331400 0.876700 +vn 0.281300 -0.263600 0.922700 +vn 0.383200 -0.380500 0.841600 +vn 0.401200 -0.321400 0.857800 +vn 0.414500 -0.271000 0.868700 +vn 0.429400 -0.222300 0.875300 +vn -0.401200 -0.321400 0.857800 +vn -0.383200 -0.380500 0.841600 +vn -0.348600 -0.331400 0.876700 +vn -0.357400 -0.279300 0.891200 +vn -0.318900 -0.291200 0.901900 +vn -0.324900 -0.239100 0.915000 +vn -0.281300 -0.263600 0.922700 +vn -0.317300 -0.200800 0.926800 +vn -0.357200 -0.238500 0.903000 +vn -0.302500 -0.172000 0.937500 +vn -0.356100 -0.204500 0.911800 +vn -0.289400 -0.211200 0.933600 +vn -0.276700 -0.166000 0.946500 +vn -0.221200 -0.114000 0.968500 +vn -0.414500 -0.271000 0.868700 +vn -0.429400 -0.222300 0.875300 +vn 0.362700 -0.013700 0.931800 +vn 0.474200 0.006700 0.880300 +vn 0.479500 0.021900 0.877300 +vn 0.363100 -0.001800 0.931700 +vn 0.490300 0.041400 0.870500 +vn 0.262000 -0.017300 0.964900 +vn 0.264300 -0.027600 0.964000 +vn 0.179300 -0.027000 0.983400 +vn 0.180600 -0.037200 0.982800 +vn 0.367600 0.013900 0.929900 +vn 0.261800 -0.002300 0.965100 +vn 0.178500 -0.010500 0.983900 +vn 0.269200 -0.036300 0.962400 +vn 0.365900 -0.025100 0.930300 +vn 0.276800 -0.046500 0.959800 +vn 0.184000 -0.043900 0.981900 +vn 0.190700 -0.048600 0.980400 +vn 0.473400 -0.008300 0.880800 +vn 0.371200 -0.040700 0.927600 +vn 0.475100 -0.028400 0.879500 +vn -0.362700 -0.013700 0.931800 +vn -0.365900 -0.025100 0.930300 +vn -0.269200 -0.036300 0.962400 +vn -0.264300 -0.027600 0.964000 +vn -0.184000 -0.043900 0.981900 +vn -0.371200 -0.040700 0.927600 +vn -0.276800 -0.046500 0.959800 +vn -0.190700 -0.048600 0.980400 +vn -0.262000 -0.017300 0.964900 +vn -0.363100 -0.001800 0.931700 +vn -0.261800 -0.002300 0.965100 +vn -0.367600 0.013900 0.929900 +vn -0.180600 -0.037200 0.982800 +vn -0.179300 -0.027000 0.983400 +vn -0.178500 -0.010500 0.983900 +vn -0.479500 0.021900 0.877300 +vn -0.474200 0.006700 0.880300 +vn -0.490300 0.041400 0.870500 +vn -0.473400 -0.008300 0.880800 +vn -0.475100 -0.028400 0.879500 +vn 0.373300 -0.110100 0.921100 +vn 0.291700 -0.095100 0.951800 +vn 0.294600 -0.136900 0.945700 +vn 0.363600 -0.162100 0.917300 +vn 0.210100 -0.069500 0.975200 +vn 0.218500 -0.085400 0.972100 +vn 0.449000 -0.167300 0.877700 +vn 0.466800 -0.108600 0.877700 +vn 0.474800 -0.060600 0.878000 +vn 0.375300 -0.067600 0.924400 +vn 0.285200 -0.064500 0.956300 +vn 0.200200 -0.055700 0.978100 +vn -0.373300 -0.110100 0.921100 +vn -0.375300 -0.067600 0.924400 +vn -0.474800 -0.060600 0.878000 +vn -0.466800 -0.108600 0.877700 +vn -0.449000 -0.167300 0.877700 +vn -0.363600 -0.162100 0.917300 +vn -0.294600 -0.136900 0.945700 +vn -0.291700 -0.095100 0.951800 +vn -0.218500 -0.085400 0.972100 +vn -0.285200 -0.064500 0.956300 +vn -0.210100 -0.069500 0.975200 +vn -0.200200 -0.055700 0.978100 +vn 0.109000 -0.010900 0.994000 +vn 0.124600 -0.068500 0.989800 +vn 0.064000 -0.066100 0.995800 +vn 0.196100 -0.233600 0.952300 +vn 0.115400 -0.232500 0.965700 +vn 0.183600 -0.007000 0.982900 +vn 0.211700 -0.056000 0.975700 +vn 0.315600 -0.245300 0.916600 +vn 0.051600 -0.009600 0.998600 +vn 0.021600 -0.065600 0.997600 +vn 0.013900 -0.008800 0.999800 +vn 0.000000 -0.065500 0.997800 +vn 0.051600 -0.231800 0.971400 +vn 0.000000 -0.233200 0.972400 +vn 0.013600 -0.001100 0.999900 +vn 0.051900 -0.000900 0.998600 +vn 0.014200 -0.019900 0.999700 +vn 0.053200 -0.018300 0.998400 +vn 0.000000 -0.008500 0.999900 +vn 0.000000 -0.001200 1.000000 +vn 0.000000 -0.020400 0.999800 +vn 0.108900 -0.000400 0.994000 +vn 0.177700 0.002300 0.984100 +vn 0.110600 -0.015200 0.993700 +vn -0.051900 -0.000900 0.998600 +vn -0.013600 -0.001100 0.999900 +vn -0.013900 -0.008800 0.999800 +vn -0.053200 -0.018300 0.998400 +vn -0.014200 -0.019900 0.999700 +vn -0.051600 -0.009600 0.998600 +vn -0.021600 -0.065600 0.997600 +vn -0.064000 -0.066100 0.995800 +vn -0.051600 -0.231800 0.971400 +vn -0.124600 -0.068500 0.989800 +vn -0.109000 -0.010900 0.994000 +vn -0.211700 -0.056000 0.975700 +vn -0.183600 -0.007000 0.982900 +vn -0.115400 -0.232500 0.965700 +vn -0.196100 -0.233600 0.952300 +vn -0.315600 -0.245300 0.916600 +vn -0.108900 -0.000400 0.994000 +vn -0.110600 -0.015200 0.993700 +vn -0.177700 0.002300 0.984100 +vn 0.051000 -0.054200 0.997200 +vn 0.110200 -0.050100 0.992600 +vn 0.109300 -0.044700 0.993000 +vn 0.053300 -0.050500 0.997300 +vn 0.114400 -0.049800 0.992200 +vn 0.051000 -0.050000 0.997400 +vn 0.110200 -0.034100 0.993300 +vn 0.052300 -0.039100 0.997800 +vn 0.013700 -0.041500 0.999000 +vn 0.012900 -0.052400 0.998500 +vn 0.000000 -0.042100 0.999100 +vn 0.000000 -0.053000 0.998600 +vn 0.012800 -0.055900 0.998300 +vn 0.013700 -0.050500 0.998600 +vn 0.000000 -0.056300 0.998400 +vn 0.000000 -0.050500 0.998700 +vn -0.012900 -0.052400 0.998500 +vn -0.013700 -0.041500 0.999000 +vn -0.052300 -0.039100 0.997900 +vn -0.051000 -0.050000 0.997400 +vn -0.110200 -0.034100 0.993300 +vn -0.109300 -0.044700 0.993000 +vn -0.110200 -0.050100 0.992600 +vn -0.051000 -0.054200 0.997200 +vn -0.114400 -0.049800 0.992200 +vn -0.053300 -0.050500 0.997300 +vn -0.012800 -0.055900 0.998300 +vn -0.013700 -0.050500 0.998600 +vn 0.017500 -0.045100 0.998800 +vn 0.019600 -0.077200 0.996800 +vn 0.061700 -0.074900 0.995300 +vn 0.025800 -0.169800 0.985100 +vn 0.059000 -0.153700 0.986400 +vn 0.000000 -0.044600 0.999000 +vn 0.000000 -0.077600 0.997000 +vn 0.000000 -0.173700 0.984800 +vn 0.060900 -0.047800 0.997000 +vn 0.130600 -0.073400 0.988700 +vn 0.111500 -0.118900 0.986600 +vn 0.128300 -0.053200 0.990300 +vn 0.121600 -0.048700 0.991400 +vn 0.057300 -0.045000 0.997300 +vn 0.015700 -0.042700 0.999000 +vn 0.000000 -0.041700 0.999100 +vn -0.060900 -0.047800 0.997000 +vn -0.057300 -0.045000 0.997300 +vn -0.121600 -0.048700 0.991400 +vn -0.128300 -0.053200 0.990300 +vn -0.130600 -0.073400 0.988700 +vn -0.061700 -0.074900 0.995300 +vn -0.111500 -0.118900 0.986600 +vn -0.019600 -0.077200 0.996800 +vn -0.017500 -0.045100 0.998800 +vn -0.059000 -0.153700 0.986400 +vn -0.025800 -0.169800 0.985100 +vn -0.015700 -0.042700 0.999000 +vn 0.805800 -0.538700 0.245800 +vn 0.772400 -0.467200 0.430200 +vn 0.899600 -0.305800 0.311700 +vn 0.929200 -0.341500 0.141300 +vn 0.969400 -0.093400 0.227100 +vn 0.661800 -0.345400 0.665300 +vn 0.779600 -0.250700 0.573900 +vn 0.847700 -0.102100 0.520500 +vn 0.925500 -0.363000 0.108200 +vn 0.796500 -0.574400 0.188700 +vn 0.896400 -0.382400 0.224100 +vn 0.750200 -0.594800 0.288600 +vn 0.991500 -0.108100 0.072300 +vn 0.991100 -0.118800 0.060500 +vn 0.974300 -0.125700 0.187000 +vn 0.646900 -0.703800 0.293700 +vn 0.651800 -0.650300 0.390100 +vn 0.526100 -0.756600 0.388100 +vn 0.594900 -0.714600 0.368000 +vn 0.487700 -0.755500 0.437400 +vn 0.606500 -0.540700 0.582900 +vn 0.509800 -0.323300 0.797200 +vn 0.509100 -0.687800 0.517500 +vn 0.439500 -0.517000 0.734500 +vn -0.805800 -0.538700 0.245800 +vn -0.651800 -0.650300 0.390100 +vn -0.646900 -0.703700 0.293700 +vn -0.796500 -0.574400 0.188700 +vn -0.594900 -0.714600 0.368000 +vn -0.509100 -0.687800 0.517500 +vn -0.526100 -0.756600 0.388100 +vn -0.487700 -0.755500 0.437400 +vn -0.925500 -0.363000 0.108200 +vn -0.929200 -0.341500 0.141300 +vn -0.991100 -0.118800 0.060500 +vn -0.991500 -0.108100 0.072300 +vn -0.750200 -0.594800 0.288600 +vn -0.896400 -0.382400 0.224100 +vn -0.974300 -0.125700 0.187000 +vn -0.899600 -0.305800 0.311700 +vn -0.772400 -0.467200 0.430200 +vn -0.779600 -0.250700 0.573900 +vn -0.969400 -0.093400 0.227100 +vn -0.847700 -0.102100 0.520500 +vn -0.606500 -0.540700 0.582900 +vn -0.439500 -0.517000 0.734500 +vn -0.661800 -0.345400 0.665300 +vn -0.509800 -0.323300 0.797200 +vn 0.935100 0.316500 0.159000 +vn 0.833900 0.515700 0.196600 +vn 0.855300 0.512500 -0.075500 +vn 0.620400 0.692000 0.369100 +vn 0.669000 0.734900 -0.111000 +vn 0.688700 0.199700 0.697000 +vn 0.403000 0.200200 0.893000 +vn 0.074800 0.148300 0.986100 +vn 0.952800 0.303000 -0.019700 +vn 0.868100 0.493200 -0.055400 +vn 0.956500 0.291700 0.000600 +vn 0.865000 0.487400 0.118900 +vn 0.685500 0.717900 -0.120900 +vn 0.699100 0.711300 0.071900 +vn 0.994600 0.097600 0.033900 +vn 0.993900 0.106900 0.026200 +vn 0.944100 0.292300 0.152400 +vn 0.980200 0.102200 0.169500 +vn 0.976800 0.116400 0.179400 +vn 0.835200 0.051500 0.547500 +vn -0.993900 0.106900 0.026200 +vn -0.994600 0.097600 0.033900 +vn -0.956500 0.291700 0.000600 +vn -0.980200 0.102200 0.169500 +vn -0.944100 0.292300 0.152400 +vn -0.952800 0.303000 -0.019700 +vn -0.868100 0.493200 -0.055400 +vn -0.855300 0.512500 -0.075500 +vn -0.685500 0.717900 -0.120900 +vn -0.865000 0.487400 0.118900 +vn -0.699100 0.711300 0.071900 +vn -0.833900 0.515700 0.196600 +vn -0.935100 0.316500 0.159000 +vn -0.403000 0.200200 0.893000 +vn -0.688700 0.199700 0.697000 +vn -0.669000 0.734900 -0.111000 +vn -0.620400 0.692000 0.369100 +vn -0.074800 0.148300 0.986100 +vn -0.976800 0.116400 0.179400 +vn -0.835200 0.051500 0.547500 +vn 0.065800 0.732500 0.677500 +vn -0.110900 0.660000 0.743000 +vn -0.120700 0.958000 0.260000 +vn -0.249900 0.538300 0.804800 +vn -0.364600 0.818800 0.443500 +vn -0.066000 0.167000 0.983700 +vn -0.081900 0.209700 0.974300 +vn 0.000000 0.243900 0.969800 +vn 0.142700 0.985500 0.091200 +vn -0.129900 0.990000 0.055100 +vn 0.142700 0.986500 -0.079800 +vn -0.125600 0.980400 0.151600 +vn -0.394700 0.890800 0.225100 +vn -0.385900 0.885800 0.257600 +vn 0.419300 0.896800 -0.140900 +vn 0.413400 0.909400 -0.046200 +vn 0.153000 0.985800 0.068600 +vn 0.438900 0.897500 0.042400 +vn 0.312100 0.759700 0.570500 +vn -0.044300 0.127000 0.990900 +vn -0.413400 0.909400 -0.046200 +vn -0.419300 0.896800 -0.140900 +vn -0.142700 0.986500 -0.079800 +vn -0.438900 0.897500 0.042400 +vn -0.153000 0.985800 0.068600 +vn -0.142700 0.985500 0.091200 +vn 0.129900 0.990000 0.055100 +vn 0.120700 0.958000 0.260000 +vn 0.394700 0.890800 0.225100 +vn 0.125600 0.980400 0.151600 +vn 0.385900 0.885800 0.257600 +vn 0.110900 0.660000 0.743000 +vn -0.065800 0.732500 0.677500 +vn 0.081900 0.209700 0.974300 +vn 0.066000 0.167000 0.983700 +vn 0.364600 0.818800 0.443500 +vn 0.249900 0.538300 0.804800 +vn -0.312100 0.759700 0.570500 +vn 0.044300 0.127000 0.990900 +vn -0.683200 0.603900 0.410300 +vn -0.502100 0.538200 0.676900 +vn -0.484000 0.664600 0.569200 +vn -0.574500 0.742200 0.344900 +vn 0.000000 0.934000 0.357200 +vn 0.000000 0.531500 0.847000 +vn 0.000000 0.522100 0.852900 +vn 0.000000 0.825100 0.565000 +vn -0.609300 0.781200 0.135500 +vn -0.682300 0.686500 0.251300 +vn -0.527600 0.828900 0.185900 +vn 0.000000 0.943300 0.332000 +vn 0.000000 0.998000 0.062800 +vn 0.000000 0.991600 0.129500 +vn -0.594100 0.743400 0.307100 +vn -0.568700 0.651200 0.502500 +vn -0.624300 0.730100 0.277900 +vn -0.565900 0.763400 0.311300 +vn -0.404900 0.458700 0.790900 +vn 0.000000 0.347800 0.937500 +vn 0.568700 0.651200 0.502500 +vn 0.594100 0.743400 0.307100 +vn 0.682300 0.686500 0.251300 +vn 0.565900 0.763400 0.311300 +vn 0.624300 0.730100 0.277900 +vn 0.683200 0.603900 0.410300 +vn 0.609300 0.781200 0.135500 +vn 0.527600 0.828900 0.185900 +vn 0.574500 0.742200 0.344900 +vn 0.484000 0.664600 0.569200 +vn 0.502100 0.538200 0.676900 +vn 0.404900 0.458700 0.790900 +vn 0.159300 -0.740200 0.653200 +vn 0.106900 -0.502600 0.857800 +vn 0.206900 -0.500400 0.840700 +vn 0.000000 -0.751600 0.659600 +vn 0.000000 -0.507500 0.861700 +vn 0.287000 -0.722400 0.629100 +vn 0.310700 -0.506200 0.804400 +vn 0.395400 -0.705700 0.587800 +vn 0.429900 -0.785800 0.444500 +vn 0.322800 -0.817300 0.477200 +vn 0.409200 -0.774900 0.481800 +vn 0.312500 -0.802200 0.508700 +vn 0.184700 -0.848400 0.496000 +vn 0.000000 -0.865000 0.501800 +vn 0.181300 -0.832400 0.523700 +vn 0.000000 -0.849000 0.528300 +vn -0.322800 -0.817300 0.477200 +vn -0.429900 -0.785800 0.444500 +vn -0.395400 -0.705700 0.587800 +vn -0.312500 -0.802200 0.508700 +vn -0.409200 -0.774900 0.481800 +vn -0.287000 -0.722400 0.629100 +vn -0.310700 -0.506200 0.804400 +vn -0.206900 -0.500400 0.840700 +vn -0.106900 -0.502600 0.857800 +vn -0.159300 -0.740200 0.653200 +vn -0.184700 -0.848400 0.496000 +vn -0.181300 -0.832400 0.523700 +vn 0.131400 -0.636000 0.760400 +vn 0.159000 -0.739500 0.654100 +vn 0.270900 -0.721800 0.636900 +vn 0.000000 -0.638200 0.769900 +vn 0.000000 -0.751000 0.660200 +vn 0.223600 -0.638800 0.736100 +vn 0.350400 -0.711200 0.609400 +vn 0.287700 -0.654300 0.699300 +vn 0.409000 -0.713000 0.569500 +vn 0.220800 -0.600800 0.768200 +vn 0.173800 -0.564000 0.807200 +vn 0.168200 -0.547400 0.819700 +vn 0.125900 -0.501400 0.856000 +vn 0.337000 -0.673300 0.658000 +vn 0.269100 -0.625300 0.732400 +vn 0.213400 -0.580200 0.786000 +vn 0.101400 -0.540600 0.835100 +vn 0.000000 -0.532700 0.846300 +vn 0.071800 -0.464300 0.882700 +vn 0.000000 -0.449000 0.893500 +vn -0.173800 -0.564000 0.807200 +vn -0.220800 -0.600800 0.768200 +vn -0.287700 -0.654300 0.699300 +vn -0.269100 -0.625300 0.732400 +vn -0.125900 -0.501400 0.856000 +vn -0.168200 -0.547400 0.819700 +vn -0.213400 -0.580200 0.786000 +vn -0.223600 -0.638800 0.736100 +vn -0.350400 -0.711200 0.609400 +vn -0.270900 -0.721800 0.636900 +vn -0.337000 -0.673300 0.658000 +vn -0.409000 -0.713000 0.569500 +vn -0.159000 -0.739500 0.654100 +vn -0.131400 -0.636000 0.760400 +vn -0.101400 -0.540600 0.835100 +vn -0.071800 -0.464300 0.882700 +vn -0.433300 0.683500 0.587400 +vn -0.548400 0.736100 0.396800 +vn -0.430200 0.828000 0.359700 +vn -0.323500 0.736700 0.593800 +vn 0.000000 0.932200 0.361900 +vn -0.196000 0.574000 0.795000 +vn -0.292000 0.558400 0.776500 +vn -0.090100 0.325600 0.941200 +vn 0.000000 0.783200 0.621700 +vn 0.000000 0.543200 0.839600 +vn 0.000000 0.146300 0.989200 +vn -0.284900 0.588100 0.756900 +vn -0.411300 0.690000 0.595500 +vn -0.199500 0.627400 0.752700 +vn -0.147100 0.398400 0.905300 +vn -0.147700 0.466100 0.872300 +vn -0.130200 0.517700 0.845600 +vn -0.507500 0.747200 0.429000 +vn -0.281100 0.750700 0.597900 +vn -0.346200 0.837700 0.422300 +vn 0.433300 0.683500 0.587400 +vn 0.411300 0.690000 0.595500 +vn 0.284900 0.588100 0.756900 +vn 0.292000 0.558400 0.776500 +vn 0.147700 0.466100 0.872300 +vn 0.281100 0.750700 0.597900 +vn 0.199500 0.627400 0.752700 +vn 0.130200 0.517700 0.845600 +vn 0.196000 0.574000 0.795000 +vn 0.323500 0.736700 0.593800 +vn 0.147100 0.398400 0.905300 +vn 0.090100 0.325600 0.941200 +vn 0.430200 0.828000 0.359700 +vn 0.548400 0.736100 0.396800 +vn 0.507500 0.747200 0.429000 +vn 0.346200 0.837700 0.422300 +vn 0.153500 0.924500 0.348900 +vn -0.106700 0.917400 0.383300 +vn -0.078800 0.799700 0.595100 +vn -0.048600 0.648600 0.759500 +vn 0.110000 0.630500 0.768300 +vn -0.036700 0.499300 0.865600 +vn 0.085400 0.470200 0.878400 +vn 0.271800 0.579600 0.768200 +vn 0.355300 0.725900 0.588900 +vn 0.413000 0.487700 0.769100 +vn 0.549900 0.590300 0.590900 +vn 0.210900 0.437000 0.874400 +vn 0.305000 0.407500 0.860800 +vn 0.136400 0.794900 0.591200 +vn 0.419400 0.842700 0.337500 +vn 0.654900 0.670800 0.348000 +vn -0.355300 0.725900 0.588900 +vn -0.271800 0.579600 0.768200 +vn -0.110000 0.630500 0.768300 +vn -0.210900 0.437000 0.874400 +vn -0.085400 0.470200 0.878400 +vn -0.549900 0.590300 0.590900 +vn -0.413000 0.487700 0.769100 +vn -0.305000 0.407500 0.860800 +vn 0.048600 0.648600 0.759500 +vn 0.078800 0.799700 0.595100 +vn 0.036700 0.499300 0.865600 +vn 0.106700 0.917400 0.383300 +vn -0.153500 0.924500 0.348900 +vn -0.136400 0.794900 0.591200 +vn -0.419400 0.842700 0.337500 +vn -0.654900 0.670800 0.348000 +vn 0.876100 0.289100 0.385800 +vn 0.803600 0.468700 0.366900 +vn 0.674400 0.427000 0.602300 +vn 0.731400 0.269700 0.626300 +vn 0.498200 0.369400 0.784400 +vn 0.523300 0.232000 0.819900 +vn 0.316400 0.325800 0.890900 +vn 0.515600 0.085600 0.852500 +vn 0.748500 0.098900 0.655700 +vn 0.481600 -0.126200 0.867200 +vn 0.725500 -0.134500 0.674900 +vn 0.314300 0.200000 0.928000 +vn 0.292600 0.073500 0.953400 +vn 0.256200 -0.113600 0.959900 +vn 0.909500 0.105700 0.402100 +vn 0.897300 -0.133700 0.420600 +vn -0.748500 0.098900 0.655700 +vn -0.515600 0.085600 0.852500 +vn -0.523300 0.232000 0.819900 +vn -0.292600 0.073500 0.953400 +vn -0.725500 -0.134500 0.674900 +vn -0.481600 -0.126200 0.867200 +vn -0.256200 -0.113600 0.959900 +vn -0.731400 0.269700 0.626300 +vn -0.498200 0.369400 0.784400 +vn -0.674400 0.427000 0.602300 +vn -0.314300 0.200000 0.928000 +vn -0.316400 0.325800 0.890900 +vn -0.803600 0.468700 0.366900 +vn -0.876100 0.289100 0.385800 +vn -0.909500 0.105700 0.402100 +vn -0.897300 -0.133700 0.420600 +vn 0.506200 -0.572800 0.644600 +vn 0.648900 -0.596600 0.472100 +vn 0.805500 -0.395200 0.441500 +vn 0.636600 -0.388400 0.666200 +vn 0.413100 -0.354600 0.838800 +vn 0.342000 -0.516900 0.784700 +vn 0.220600 -0.307600 0.925600 +vn 0.295400 -0.603400 0.740700 +vn 0.397200 -0.661100 0.636500 +vn 0.202800 -0.450300 0.869500 +vn 0.202900 -0.541100 0.816100 +vn 0.498500 -0.695100 0.518000 +vn -0.397200 -0.661100 0.636500 +vn -0.295400 -0.603400 0.740700 +vn -0.342000 -0.516900 0.784700 +vn -0.202900 -0.541100 0.816100 +vn -0.202800 -0.450300 0.869500 +vn -0.506300 -0.572800 0.644600 +vn -0.413100 -0.354600 0.838800 +vn -0.636600 -0.388400 0.666200 +vn -0.220600 -0.307600 0.925600 +vn -0.805500 -0.395200 0.441500 +vn -0.648900 -0.596600 0.472100 +vn -0.498500 -0.695100 0.518000 +vn -0.036800 0.278700 0.959700 +vn -0.032800 0.159700 0.986600 +vn 0.039600 0.089800 0.995100 +vn -0.008600 -0.003600 0.999900 +vn 0.027700 -0.063900 0.997600 +vn 0.049500 0.214100 0.975500 +vn 0.132800 0.068600 0.988700 +vn 0.105800 -0.093600 0.990000 +vn 0.157600 0.193800 0.968300 +vn 0.179100 0.314600 0.932200 +vn 0.065000 0.334600 0.940100 +vn -0.039200 0.379800 0.924200 +vn -0.049500 0.214100 0.975500 +vn -0.065000 0.334600 0.940100 +vn -0.179100 0.314600 0.932200 +vn -0.157600 0.193800 0.968300 +vn -0.132800 0.068600 0.988700 +vn -0.039600 0.089800 0.995100 +vn -0.105800 -0.093600 0.990000 +vn 0.032800 0.159700 0.986600 +vn 0.036800 0.278700 0.959700 +vn -0.027700 -0.063900 0.997600 +vn 0.008600 -0.003600 0.999900 +vn 0.039200 0.379800 0.924200 +vn 0.046100 -0.334800 0.941100 +vn 0.026300 -0.216100 0.976000 +vn 0.001500 -0.180700 0.983500 +vn 0.016400 -0.293500 0.955800 +vn 0.000000 -0.154400 0.988000 +vn 0.042700 -0.389100 0.920200 +vn 0.081700 -0.431400 0.898400 +vn 0.000000 -0.275500 0.961300 +vn 0.000000 -0.370700 0.928700 +vn 0.132500 -0.483000 0.865500 +vn 0.104900 -0.386600 0.916300 +vn 0.093800 -0.259300 0.961200 +vn -0.046100 -0.334800 0.941100 +vn -0.104900 -0.386600 0.916300 +vn -0.132500 -0.483000 0.865500 +vn -0.081700 -0.431400 0.898400 +vn -0.042700 -0.389100 0.920200 +vn -0.016400 -0.293500 0.955800 +vn -0.001500 -0.180700 0.983500 +vn -0.026300 -0.216100 0.976000 +vn -0.093800 -0.259300 0.961200 +vn -0.219300 -0.051300 0.974300 +vn -0.151200 -0.005400 0.988500 +vn -0.105400 0.003200 0.994400 +vn -0.166000 -0.061600 0.984200 +vn 0.000000 0.012500 0.999900 +vn 0.000000 0.025400 0.999700 +vn -0.214700 -0.148800 0.965300 +vn -0.227300 -0.111500 0.967400 +vn -0.216000 -0.218700 0.951600 +vn -0.138400 -0.184500 0.973000 +vn -0.137600 -0.034500 0.989900 +vn -0.215900 0.009400 0.976300 +vn 0.071000 -0.128000 0.989200 +vn -0.167500 0.029700 0.985400 +vn 0.000000 0.036900 0.999300 +vn 0.219300 -0.051300 0.974300 +vn 0.215900 0.009400 0.976300 +vn 0.137600 -0.034500 0.989900 +vn 0.227300 -0.111500 0.967400 +vn -0.071000 -0.128000 0.989200 +vn 0.214700 -0.148800 0.965300 +vn 0.166000 -0.061600 0.984200 +vn 0.138400 -0.184500 0.973000 +vn 0.216000 -0.218700 0.951600 +vn 0.105400 0.003200 0.994400 +vn 0.151200 -0.005500 0.988500 +vn 0.167500 0.029700 0.985400 +vn 0.412200 -0.139500 0.900300 +vn 0.050600 -0.185900 0.981200 +vn -0.056400 -0.175800 0.982800 +vn 0.338700 -0.013400 0.940800 +vn 0.630600 -0.062300 0.773600 +vn 0.638200 -0.122500 0.760000 +vn 0.693900 -0.129000 0.708400 +vn 0.749800 -0.110000 0.652400 +vn 0.651000 -0.164100 0.741100 +vn 0.850800 -0.049200 0.523200 +vn 0.811500 -0.138700 0.567700 +vn 0.684900 -0.133100 0.716300 +vn 0.762400 -0.088000 0.641100 +vn 0.846900 -0.001700 0.531700 +vn 0.412400 -0.112000 0.904100 +vn 0.655900 -0.230500 0.718800 +vn -0.651000 -0.164100 0.741100 +vn -0.749800 -0.110000 0.652400 +vn -0.638200 -0.122500 0.760000 +vn -0.762400 -0.088000 0.641100 +vn -0.684900 -0.133100 0.716300 +vn -0.811500 -0.138700 0.567700 +vn -0.850800 -0.049200 0.523200 +vn -0.846900 -0.001700 0.531700 +vn -0.412200 -0.139500 0.900300 +vn -0.630600 -0.062300 0.773600 +vn -0.338700 -0.013400 0.940800 +vn -0.693900 -0.129000 0.708400 +vn 0.056400 -0.175800 0.982800 +vn -0.050600 -0.185900 0.981200 +vn -0.412400 -0.112000 0.904100 +vn -0.655900 -0.230500 0.718800 +vn 0.662900 -0.140700 0.735300 +vn 0.656500 -0.192900 0.729200 +vn 0.572300 -0.219300 0.790200 +vn 0.607900 -0.123500 0.784300 +vn 0.495800 -0.156700 0.854200 +vn 0.538100 -0.070800 0.839900 +vn 0.376000 -0.100700 0.921100 +vn 0.657700 0.020100 0.753000 +vn 0.705000 -0.029300 0.708500 +vn 0.767100 0.112800 0.631600 +vn 0.800300 0.070700 0.595400 +vn 0.475100 -0.010400 0.879800 +vn 0.609000 0.060500 0.790800 +vn 0.731300 0.139900 0.667600 +vn 0.741900 -0.067900 0.667100 +vn 0.827400 0.027900 0.560900 +vn -0.705000 -0.029300 0.708500 +vn -0.657700 0.020100 0.753000 +vn -0.538100 -0.070800 0.839900 +vn -0.609000 0.060500 0.790800 +vn -0.475100 -0.010400 0.879800 +vn -0.800300 0.070700 0.595400 +vn -0.767100 0.112800 0.631600 +vn -0.731300 0.139900 0.667600 +vn -0.607900 -0.123500 0.784300 +vn -0.495800 -0.156700 0.854200 +vn -0.572300 -0.219300 0.790200 +vn -0.376000 -0.100700 0.921100 +vn -0.656500 -0.192900 0.729200 +vn -0.662900 -0.140700 0.735300 +vn -0.741900 -0.067900 0.667100 +vn -0.827400 0.027900 0.560900 +vn 0.428900 0.024600 0.903000 +vn 0.304100 -0.014700 0.952500 +vn 0.276400 0.008100 0.961000 +vn 0.396300 0.034700 0.917400 +vn 0.264400 0.010600 0.964300 +vn 0.377200 0.029100 0.925600 +vn 0.507700 0.061900 0.859300 +vn 0.533000 0.075800 0.842600 +vn 0.640000 0.109400 0.760500 +vn 0.666300 0.132700 0.733700 +vn 0.567100 0.078000 0.819900 +vn 0.697200 0.145500 0.701900 +vn -0.533000 0.075800 0.842600 +vn -0.507700 0.061900 0.859300 +vn -0.377200 0.029100 0.925600 +vn -0.666300 0.132700 0.733700 +vn -0.640000 0.109400 0.760500 +vn -0.396300 0.034700 0.917400 +vn -0.264400 0.010600 0.964300 +vn -0.276400 0.008100 0.961000 +vn -0.304100 -0.014700 0.952500 +vn -0.428900 0.024600 0.903000 +vn -0.567100 0.078000 0.819900 +vn -0.697200 0.145500 0.701900 +vn 0.836500 0.251800 0.486700 +vn 0.830700 0.210500 0.515300 +vn 0.902600 0.246700 0.352600 +vn 0.893500 0.290500 0.342400 +vn 0.952100 0.263800 0.154500 +vn 0.890000 0.313900 0.330800 +vn 0.846400 0.274400 0.456400 +vn 0.894300 0.315300 0.317400 +vn 0.938400 0.305200 0.161600 +vn 0.928800 0.331900 0.164700 +vn 0.928500 0.332300 0.165900 +vn 0.788600 0.216300 0.575500 +vn 0.768000 0.195400 0.609900 +vn 0.858500 0.276700 0.431800 +vn 0.811400 0.217100 0.542600 +vn 0.749600 0.163300 0.641400 +vn -0.768000 0.195400 0.609900 +vn -0.788600 0.216300 0.575500 +vn -0.846400 0.274400 0.456400 +vn -0.811400 0.217100 0.542600 +vn -0.858500 0.276700 0.431800 +vn -0.836500 0.251800 0.486700 +vn -0.890000 0.313900 0.330800 +vn -0.928800 0.331900 0.164700 +vn -0.938400 0.305200 0.161600 +vn -0.894300 0.315300 0.317400 +vn -0.928400 0.332300 0.165900 +vn -0.893500 0.290500 0.342400 +vn -0.902600 0.246700 0.352600 +vn -0.830700 0.210500 0.515300 +vn -0.952100 0.263800 0.154500 +vn -0.749600 0.163300 0.641400 +vn 0.873500 0.259500 0.411800 +vn 0.903600 0.297900 0.307700 +vn 0.912600 0.277000 0.300600 +vn 0.934000 0.315000 0.168100 +vn 0.938500 0.297900 0.174400 +vn 0.889100 0.232200 0.394500 +vn 0.920800 0.257300 0.293000 +vn 0.904000 0.201500 0.377000 +vn 0.927800 0.244400 0.281900 +vn 0.939600 0.289800 0.181900 +vn 0.939200 0.288800 0.185800 +vn 0.879500 0.124200 0.459200 +vn 0.858900 0.163000 0.485400 +vn 0.917100 0.176900 0.357300 +vn 0.896200 0.093800 0.433500 +vn 0.835500 0.197300 0.512700 +vn -0.858900 0.163000 0.485400 +vn -0.879500 0.124200 0.459200 +vn -0.904000 0.201500 0.377000 +vn -0.896200 0.093800 0.433500 +vn -0.917100 0.176900 0.357300 +vn -0.889100 0.232200 0.394500 +vn -0.920800 0.257300 0.293000 +vn -0.912600 0.277000 0.300600 +vn -0.939600 0.289800 0.181900 +vn -0.927800 0.244400 0.281900 +vn -0.939200 0.288800 0.185800 +vn -0.903600 0.297900 0.307700 +vn -0.873500 0.259500 0.411800 +vn -0.938500 0.297900 0.174400 +vn -0.934000 0.315000 0.168100 +vn -0.835500 0.197300 0.512700 +vn 0.914000 -0.133600 0.383100 +vn 0.939500 -0.130300 0.316900 +vn 0.796400 -0.487100 0.358300 +vn 0.787600 -0.425400 0.445800 +vn 0.965100 -0.124900 0.230200 +vn 0.807600 -0.527400 0.263800 +vn 0.774800 -0.349900 0.526400 +vn 0.882700 -0.126300 0.452500 +vn 0.906100 0.022100 0.422500 +vn 0.932100 0.086900 0.351600 +vn 0.947700 0.147600 0.283000 +vn 0.961600 0.191500 0.196400 +vn -0.914000 -0.133600 0.383100 +vn -0.932100 0.086900 0.351600 +vn -0.906100 0.022100 0.422500 +vn -0.882700 -0.126300 0.452500 +vn -0.774800 -0.349900 0.526400 +vn -0.787600 -0.425400 0.445800 +vn -0.796400 -0.487100 0.358300 +vn -0.939500 -0.130300 0.316900 +vn -0.807600 -0.527400 0.263800 +vn -0.947700 0.147600 0.283000 +vn -0.965100 -0.124900 0.230200 +vn -0.961600 0.191500 0.196400 +vn 0.019100 -0.574000 0.818600 +vn 0.061300 -0.264400 0.962400 +vn 0.062300 -0.346800 0.935800 +vn -0.078600 -0.421600 0.903300 +vn 0.109600 -0.166000 0.980000 +vn 0.085100 -0.645300 0.759200 +vn 0.045300 -0.377700 0.924800 +vn 0.077900 -0.672500 0.735900 +vn 0.000000 -0.387800 0.921700 +vn 0.101300 -0.880100 0.463900 +vn 0.108300 -0.865700 0.488700 +vn 0.108600 -0.955000 0.275800 +vn 0.116900 -0.949800 0.290200 +vn 0.000000 -0.686600 0.727000 +vn 0.000000 -0.891600 0.452700 +vn 0.000000 -0.963100 0.269100 +vn -0.003500 -0.841300 0.540600 +vn -0.276400 -0.718800 0.637900 +vn -0.014400 -0.948900 0.315000 +vn -0.399900 -0.847500 0.349000 +vn -0.108300 -0.865700 0.488700 +vn -0.101300 -0.880100 0.463900 +vn -0.077900 -0.672500 0.735900 +vn -0.116900 -0.949800 0.290200 +vn -0.108600 -0.955000 0.275800 +vn -0.085100 -0.645300 0.759200 +vn -0.045300 -0.377700 0.924800 +vn -0.062300 -0.346800 0.935800 +vn -0.061300 -0.264400 0.962400 +vn -0.019100 -0.574000 0.818600 +vn -0.109600 -0.166000 0.980000 +vn 0.078600 -0.421600 0.903300 +vn 0.003500 -0.841300 0.540600 +vn 0.014400 -0.948900 0.315000 +vn 0.276400 -0.718800 0.637900 +vn 0.399900 -0.847500 0.349000 +vn -0.172400 -0.209500 0.962500 +vn 0.172100 -0.246800 0.953600 +vn 0.180200 -0.208300 0.961300 +vn -0.182500 -0.197000 0.963300 +vn 0.140700 -0.295300 0.945000 +vn -0.169500 -0.240800 0.955600 +vn 0.159600 -0.181100 0.970400 +vn -0.149200 -0.306200 0.940200 +vn -0.559100 -0.479800 0.676100 +vn -0.684500 -0.254000 0.683300 +vn -0.788500 -0.524000 0.321900 +vn -0.701000 -0.093600 0.706900 +vn -0.673300 0.024500 0.738900 +vn -0.936700 -0.186700 0.296100 +vn -0.948200 0.048300 0.313900 +vn -0.904900 0.228100 0.359200 +vn 0.684500 -0.254000 0.683300 +vn 0.559100 -0.479800 0.676100 +vn 0.149200 -0.306200 0.940200 +vn 0.936700 -0.186700 0.296100 +vn 0.788500 -0.524000 0.321900 +vn 0.169500 -0.240800 0.955600 +vn -0.159600 -0.181100 0.970400 +vn -0.180200 -0.208300 0.961300 +vn -0.172100 -0.246800 0.953600 +vn 0.172400 -0.209500 0.962500 +vn -0.140700 -0.295300 0.945000 +vn 0.182500 -0.197000 0.963300 +vn 0.701000 -0.093600 0.707000 +vn 0.948200 0.048300 0.313900 +vn 0.673300 0.024500 0.738900 +vn 0.904900 0.228100 0.359200 +vn -0.217400 -0.144400 0.965300 +vn -0.220200 -0.089400 0.971300 +vn 0.023300 -0.388600 0.921100 +vn 0.056800 -0.373000 0.926100 +vn -0.202800 -0.018200 0.979000 +vn 0.001800 -0.389200 0.921100 +vn 0.099400 -0.339200 0.935500 +vn -0.199800 -0.176400 0.963800 +vn -0.630900 0.136900 0.763700 +vn -0.578400 0.246600 0.777600 +vn -0.828900 0.384800 0.405900 +vn -0.509400 0.359700 0.781700 +vn -0.417900 0.471400 0.776600 +vn -0.732300 0.517300 0.442700 +vn -0.621100 0.629500 0.466800 +vn -0.493400 0.725000 0.480500 +vn 0.217400 -0.144400 0.965300 +vn 0.578400 0.246600 0.777600 +vn 0.630900 0.136900 0.763700 +vn 0.732300 0.517300 0.442700 +vn 0.828900 0.384800 0.405900 +vn 0.199800 -0.176400 0.963800 +vn -0.099400 -0.339200 0.935500 +vn -0.056800 -0.373000 0.926100 +vn -0.023300 -0.388600 0.921100 +vn 0.220200 -0.089400 0.971300 +vn -0.001700 -0.389200 0.921100 +vn 0.509400 0.359700 0.781700 +vn 0.621100 0.629500 0.466800 +vn 0.202800 -0.018200 0.979000 +vn 0.417900 0.471400 0.776600 +vn 0.493400 0.725000 0.480500 +vn -0.105100 0.098300 0.989600 +vn -0.047800 0.121000 0.991500 +vn -0.007000 -0.368400 0.929600 +vn -0.010400 -0.374100 0.927300 +vn 0.000000 0.125400 0.992100 +vn 0.000000 -0.366200 0.930500 +vn -0.007900 -0.382100 0.924100 +vn -0.160600 0.049900 0.985700 +vn -0.303800 0.561500 0.769700 +vn -0.185600 0.618300 0.763700 +vn -0.351400 0.798200 0.489200 +vn -0.080400 0.642800 0.761800 +vn 0.000000 0.647000 0.762500 +vn -0.211800 0.843700 0.493300 +vn -0.091100 0.864100 0.495000 +vn 0.000000 0.868500 0.495700 +vn 0.105100 0.098300 0.989600 +vn 0.185600 0.618300 0.763700 +vn 0.303800 0.561500 0.769700 +vn 0.160600 0.049900 0.985700 +vn 0.211800 0.843700 0.493300 +vn 0.351400 0.798200 0.489200 +vn 0.007900 -0.382100 0.924100 +vn 0.010400 -0.374100 0.927300 +vn 0.007000 -0.368400 0.929600 +vn 0.047800 0.121000 0.991500 +vn 0.080400 0.642800 0.761800 +vn 0.091100 0.864100 0.495000 +vn -0.087400 0.840500 0.534700 +vn -0.091800 0.899600 0.426900 +vn -0.212200 0.881900 0.420900 +vn 0.000000 0.844100 0.536100 +vn 0.000000 0.904000 0.427400 +vn -0.199900 0.829400 0.521600 +vn -0.351300 0.844400 0.404300 +vn -0.330800 0.808300 0.487000 +vn -0.493800 0.784800 0.374500 +vn -0.273100 0.661700 0.698200 +vn -0.164400 0.655400 0.737200 +vn -0.157400 0.328500 0.931300 +vn -0.094700 0.304800 0.947700 +vn -0.468500 0.773600 0.426700 +vn -0.398400 0.673000 0.623100 +vn -0.243300 0.377800 0.893300 +vn -0.073600 0.655800 0.751300 +vn 0.000000 0.658300 0.752700 +vn -0.044500 0.299600 0.953000 +vn 0.000000 0.302000 0.953300 +vn 0.164400 0.655400 0.737200 +vn 0.273100 0.661700 0.698200 +vn 0.330800 0.808300 0.487000 +vn 0.398400 0.673000 0.623100 +vn 0.468500 0.773600 0.426700 +vn 0.094700 0.304800 0.947700 +vn 0.157400 0.328500 0.931300 +vn 0.243300 0.377800 0.893300 +vn 0.199900 0.829400 0.521600 +vn 0.351300 0.844400 0.404300 +vn 0.493800 0.784800 0.374500 +vn 0.212200 0.881900 0.420900 +vn 0.091800 0.899600 0.426900 +vn 0.087400 0.840500 0.534700 +vn 0.073600 0.655800 0.751300 +vn 0.044500 0.299600 0.953000 +vn -0.728500 0.630200 0.268500 +vn -0.598300 0.721400 0.348700 +vn -0.622300 0.709200 0.331200 +vn -0.743300 0.606500 0.282100 +vn -0.847900 0.474200 0.236900 +vn -0.842200 0.496300 0.210400 +vn -0.932100 0.307400 0.191300 +vn -0.927700 0.332300 0.170100 +vn -0.838100 0.478500 0.261800 +vn -0.829700 0.384500 0.404700 +vn -0.623500 0.461400 0.631100 +vn -0.917000 0.331200 0.222100 +vn -0.901500 0.312900 0.298900 +vn -0.700600 0.614900 0.361800 +vn -0.540400 0.673900 0.503800 +vn -0.389100 0.442200 0.808100 +vn 0.728500 0.630200 0.268500 +vn 0.700600 0.614900 0.361800 +vn 0.838100 0.478500 0.261800 +vn 0.842200 0.496300 0.210400 +vn 0.917000 0.331200 0.222100 +vn 0.623500 0.461400 0.631100 +vn 0.829700 0.384500 0.404700 +vn 0.901500 0.312900 0.298900 +vn 0.847900 0.474200 0.236900 +vn 0.743300 0.606500 0.282100 +vn 0.927700 0.332300 0.170100 +vn 0.932100 0.307400 0.191300 +vn 0.622300 0.709200 0.331200 +vn 0.598300 0.721400 0.348700 +vn 0.540400 0.673900 0.503800 +vn 0.389100 0.442200 0.808100 +vn -0.972700 -0.174400 0.152700 +vn -0.983000 0.124400 0.135100 +vn -0.982300 0.106800 0.153600 +vn -0.975800 -0.160300 0.148700 +vn -0.820200 -0.539200 0.190800 +vn -0.786300 -0.572200 0.233000 +vn -0.411600 -0.879600 0.238300 +vn -0.369500 -0.879100 0.301000 +vn -0.684700 -0.611900 0.395700 +vn -0.935300 -0.223600 0.274200 +vn -0.473800 -0.599500 0.645000 +vn -0.277800 -0.838900 0.468000 +vn -0.130300 -0.713600 0.688300 +vn -0.971800 0.121400 0.202200 +vn -0.798800 -0.265600 0.539800 +vn -0.919800 0.119800 0.373500 +vn 0.972700 -0.174400 0.152700 +vn 0.935300 -0.223600 0.274200 +vn 0.684700 -0.611900 0.395700 +vn 0.786300 -0.572200 0.233100 +vn 0.277800 -0.838900 0.468000 +vn 0.798800 -0.265600 0.539800 +vn 0.473800 -0.599500 0.645000 +vn 0.130300 -0.713600 0.688300 +vn 0.820200 -0.539200 0.190800 +vn 0.975800 -0.160300 0.148700 +vn 0.369500 -0.879100 0.301000 +vn 0.411600 -0.879600 0.238300 +vn 0.982300 0.106800 0.153600 +vn 0.983000 0.124400 0.135100 +vn 0.971800 0.121400 0.202200 +vn 0.919800 0.119800 0.373500 +vn 0.006000 -0.947500 0.319600 +vn -0.010300 -0.969800 0.243400 +vn 0.117900 -0.963500 0.240300 +vn 0.115400 -0.938900 0.324200 +vn 0.107900 -0.965200 0.237900 +vn 0.101600 -0.939200 0.327800 +vn 0.000000 -0.971500 0.236900 +vn 0.087600 -0.859800 0.502900 +vn 0.106800 -0.862000 0.495500 +vn 0.062600 -0.691700 0.719400 +vn 0.085400 -0.697700 0.711200 +vn 0.000000 -0.943900 0.330100 +vn 0.000000 -0.861500 0.507700 +vn 0.000000 -0.689400 0.724400 +vn 0.030700 -0.872600 0.487400 +vn 0.054000 -0.710300 0.701800 +vn -0.106800 -0.862000 0.495500 +vn -0.087600 -0.859800 0.502900 +vn -0.101600 -0.939200 0.327800 +vn -0.085400 -0.697700 0.711200 +vn -0.062600 -0.691700 0.719400 +vn -0.115400 -0.938900 0.324200 +vn -0.107900 -0.965200 0.237900 +vn -0.117900 -0.963500 0.240300 +vn 0.010300 -0.969800 0.243400 +vn -0.006000 -0.947500 0.319600 +vn -0.030700 -0.872600 0.487400 +vn -0.054000 -0.710300 0.701800 +vn 0.024200 -0.293800 0.955500 +vn 0.055500 -0.488400 0.870800 +vn 0.034800 -0.481700 0.875600 +vn 0.012300 -0.288400 0.957400 +vn 0.000000 -0.478300 0.878200 +vn -0.011200 -0.058200 0.998200 +vn -0.020000 -0.059100 0.998000 +vn 0.000000 -0.284900 0.958500 +vn 0.000000 -0.055100 0.998500 +vn -0.028400 -0.050400 0.998300 +vn 0.036900 -0.300000 0.953200 +vn -0.041200 -0.022300 0.998900 +vn 0.059000 -0.499700 0.864100 +vn 0.051600 -0.307000 0.950300 +vn 0.011700 -0.521500 0.853100 +vn -0.024200 -0.293800 0.955500 +vn -0.036900 -0.300000 0.953200 +vn 0.028400 -0.050400 0.998300 +vn 0.020000 -0.059100 0.998000 +vn -0.051600 -0.307000 0.950300 +vn 0.041200 -0.022300 0.998900 +vn 0.011200 -0.058200 0.998200 +vn -0.012300 -0.288400 0.957400 +vn -0.034800 -0.481700 0.875600 +vn -0.055500 -0.488400 0.870800 +vn -0.059000 -0.499700 0.864100 +vn -0.011700 -0.521500 0.853100 +vn -0.568900 -0.217100 0.793200 +vn -0.190600 -0.497400 0.846300 +vn -0.066000 -0.268700 0.960900 +vn -0.167900 0.062000 0.983800 +vn -0.494500 0.142100 0.857400 +vn -0.367200 -0.135300 0.920200 +vn -0.772600 0.146100 0.617800 +vn 0.367200 -0.135300 0.920200 +vn 0.568900 -0.217100 0.793200 +vn 0.772600 0.146100 0.617800 +vn 0.494500 0.142100 0.857400 +vn 0.167900 0.062000 0.983800 +vn 0.066000 -0.268700 0.960900 +vn 0.190600 -0.497400 0.846300 +vn -0.258200 -0.176700 0.949800 +vn -0.241500 -0.149800 0.958700 +vn -0.192500 -0.155600 0.968900 +vn -0.202700 -0.173200 0.963800 +vn -0.221300 -0.111700 0.968800 +vn -0.165400 -0.130700 0.977500 +vn -0.207300 -0.175100 0.962500 +vn -0.270100 -0.195000 0.942900 +vn -0.206000 -0.161700 0.965100 +vn -0.281300 -0.203800 0.937700 +vn -0.226700 -0.160300 0.960700 +vn -0.197400 -0.142500 0.969900 +vn 0.051400 -0.032700 0.998100 +vn -0.265100 -0.175300 0.948100 +vn -0.013500 -0.018100 0.999700 +vn -0.174700 -0.119400 0.977300 +vn -0.154200 -0.089900 0.983900 +vn 0.094400 -0.047000 0.994400 +vn 0.117800 -0.055000 0.991500 +vn 0.127600 -0.053000 0.990400 +vn 0.258200 -0.176700 0.949800 +vn 0.197400 -0.142500 0.969900 +vn 0.226700 -0.160300 0.960700 +vn 0.270100 -0.195000 0.942900 +vn 0.265100 -0.175300 0.948100 +vn -0.094400 -0.047000 0.994400 +vn -0.051400 -0.032700 0.998100 +vn 0.013500 -0.018100 0.999700 +vn 0.207300 -0.175100 0.962500 +vn 0.202700 -0.173200 0.963800 +vn 0.281300 -0.203800 0.937700 +vn 0.206000 -0.161700 0.965100 +vn 0.192500 -0.155600 0.968900 +vn 0.241500 -0.149800 0.958700 +vn 0.165400 -0.130700 0.977500 +vn 0.174700 -0.119400 0.977300 +vn -0.117800 -0.055000 0.991500 +vn 0.221300 -0.111700 0.968800 +vn 0.154200 -0.089900 0.983900 +vn -0.127600 -0.053000 0.990400 +vn -0.103900 -0.044500 0.993600 +vn -0.018300 -0.084800 0.996200 +vn -0.069900 -0.093100 0.993200 +vn -0.059400 -0.036900 0.997500 +vn 0.029900 -0.086200 0.995800 +vn -0.147600 -0.059100 0.987300 +vn -0.120100 -0.110000 0.986600 +vn -0.189400 -0.083100 0.978400 +vn -0.131100 -0.060900 0.989500 +vn -0.105100 -0.040300 0.993600 +vn 0.130600 -0.047700 0.990300 +vn -0.078000 -0.027600 0.996600 +vn -0.052900 -0.018800 0.998400 +vn 0.131600 -0.047000 0.990200 +vn 0.132300 -0.050100 0.989900 +vn 0.133900 -0.057100 0.989300 +vn 0.147600 -0.059100 0.987300 +vn 0.105100 -0.040300 0.993600 +vn 0.131000 -0.060900 0.989500 +vn 0.189400 -0.083100 0.978400 +vn -0.131600 -0.047000 0.990200 +vn -0.130600 -0.047700 0.990300 +vn 0.120100 -0.110000 0.986600 +vn 0.069900 -0.093100 0.993200 +vn 0.018300 -0.084800 0.996200 +vn 0.103900 -0.044500 0.993600 +vn -0.029900 -0.086200 0.995800 +vn 0.059400 -0.036900 0.997500 +vn 0.078000 -0.027600 0.996600 +vn -0.132300 -0.050100 0.989900 +vn 0.052900 -0.018800 0.998400 +vn -0.133900 -0.057100 0.989300 +vn 0.045600 -0.029400 0.998500 +vn 0.153600 -0.108300 0.982100 +vn 0.121100 -0.101600 0.987400 +vn 0.077500 -0.012800 0.996900 +vn 0.184200 -0.107600 0.977000 +vn 0.013900 -0.034600 0.999300 +vn 0.075700 -0.098600 0.992200 +vn -0.020100 -0.038800 0.999000 +vn -0.026800 -0.018100 0.999500 +vn -0.000300 -0.014700 0.999900 +vn 0.137300 -0.062700 0.988500 +vn 0.138200 -0.068900 0.988000 +vn 0.021300 -0.003600 0.999800 +vn 0.041500 0.018100 0.999000 +vn 0.134000 -0.069200 0.988600 +vn 0.128200 -0.052900 0.990300 +vn 0.000300 -0.014700 0.999900 +vn 0.026800 -0.018100 0.999500 +vn 0.020100 -0.038800 0.999000 +vn -0.138200 -0.068900 0.988000 +vn -0.137300 -0.062700 0.988500 +vn -0.013900 -0.034600 0.999300 +vn -0.075700 -0.098600 0.992200 +vn -0.121100 -0.101600 0.987400 +vn -0.153600 -0.108300 0.982100 +vn -0.045600 -0.029400 0.998500 +vn -0.184300 -0.107600 0.977000 +vn -0.021300 -0.003600 0.999800 +vn -0.134000 -0.069200 0.988600 +vn -0.077500 -0.012800 0.996900 +vn -0.041500 0.018100 0.999000 +vn -0.128200 -0.052900 0.990300 +vn -0.219000 -0.297000 0.929400 +vn -0.267500 -0.234600 0.934500 +vn -0.180200 -0.159100 0.970600 +vn -0.118300 -0.186800 0.975200 +vn -0.036000 -0.223100 0.974100 +vn -0.145500 -0.376000 0.915100 +vn 0.043900 -0.267500 0.962600 +vn -0.205700 -0.432800 0.877700 +vn -0.263000 -0.321100 0.909800 +vn -0.066000 -0.230100 0.970900 +vn -0.092500 -0.125200 0.987800 +vn -0.072700 -0.440500 0.894800 +vn -0.130500 -0.518600 0.845000 +vn -0.012300 -0.319500 0.947500 +vn -0.280700 -0.230500 0.931700 +vn -0.069400 -0.051600 0.996200 +vn 0.263000 -0.321100 0.909800 +vn 0.205700 -0.432800 0.877700 +vn 0.145500 -0.376000 0.915100 +vn 0.130500 -0.518600 0.845000 +vn 0.072700 -0.440500 0.894800 +vn 0.092500 -0.125200 0.987800 +vn 0.066000 -0.230100 0.970900 +vn 0.012300 -0.319500 0.947500 +vn 0.219000 -0.297000 0.929400 +vn 0.036000 -0.223100 0.974100 +vn -0.043900 -0.267500 0.962600 +vn 0.118300 -0.186800 0.975200 +vn 0.180200 -0.159100 0.970600 +vn 0.267500 -0.234600 0.934500 +vn 0.280700 -0.230500 0.931700 +vn 0.069400 -0.051600 0.996200 +vn 0.039000 -0.519800 0.853400 +vn -0.010700 -0.486700 0.873500 +vn 0.086300 -0.317200 0.944400 +vn 0.117900 -0.378100 0.918200 +vn 0.148300 -0.449500 0.880900 +vn 0.094800 -0.547100 0.831700 +vn 0.204900 -0.511000 0.834800 +vn 0.173500 -0.564100 0.807200 +vn 0.091400 -0.548600 0.831000 +vn 0.018900 -0.559200 0.828800 +vn 0.157000 -0.355400 0.921400 +vn 0.175400 -0.532900 0.827800 +vn 0.210400 -0.337100 0.917600 +vn -0.050800 -0.556700 0.829100 +vn 0.104400 -0.369400 0.923400 +vn 0.051400 -0.365100 0.929500 +vn -0.039000 -0.519800 0.853400 +vn -0.018900 -0.559200 0.828800 +vn -0.091400 -0.548600 0.831000 +vn -0.094800 -0.547100 0.831700 +vn -0.175400 -0.532900 0.827800 +vn -0.104400 -0.369400 0.923400 +vn -0.157000 -0.355400 0.921400 +vn -0.210400 -0.337100 0.917600 +vn -0.148300 -0.449500 0.880900 +vn -0.117900 -0.378100 0.918200 +vn -0.173500 -0.564100 0.807200 +vn -0.204900 -0.511000 0.834800 +vn -0.086300 -0.317200 0.944400 +vn 0.010700 -0.486700 0.873500 +vn 0.050800 -0.556700 0.829100 +vn -0.051400 -0.365100 0.929500 +vn 0.367500 -0.533300 0.761900 +vn 0.272200 -0.560700 0.781900 +vn 0.287700 -0.534200 0.794900 +vn 0.371800 -0.516500 0.771300 +vn 0.436700 -0.473100 0.765100 +vn 0.442500 -0.488800 0.751800 +vn 0.475100 -0.423000 0.771500 +vn 0.489000 -0.437400 0.754700 +vn 0.431300 -0.440900 0.787100 +vn 0.359100 -0.483000 0.798600 +vn 0.372400 -0.257200 0.891700 +vn 0.477200 -0.389000 0.788000 +vn 0.399600 -0.208700 0.892600 +vn 0.269700 -0.513300 0.814700 +vn 0.325900 -0.293400 0.898700 +vn 0.268900 -0.318500 0.909000 +vn -0.367500 -0.533300 0.761900 +vn -0.359100 -0.483000 0.798600 +vn -0.431300 -0.440900 0.787100 +vn -0.442500 -0.488800 0.751800 +vn -0.477200 -0.389000 0.788000 +vn -0.325900 -0.293400 0.898700 +vn -0.372400 -0.257200 0.891700 +vn -0.399600 -0.208700 0.892600 +vn -0.436700 -0.473100 0.765100 +vn -0.371800 -0.516500 0.771300 +vn -0.489000 -0.437400 0.754700 +vn -0.475100 -0.423000 0.771500 +vn -0.287700 -0.534200 0.794900 +vn -0.272200 -0.560700 0.781900 +vn -0.269700 -0.513300 0.814700 +vn -0.268900 -0.318500 0.909000 +vn 0.514300 -0.318100 0.796400 +vn 0.510100 -0.380700 0.771300 +vn 0.493700 -0.372800 0.785600 +vn 0.500200 -0.319900 0.804600 +vn 0.496500 -0.263800 0.827000 +vn 0.505500 -0.253600 0.824700 +vn 0.483400 -0.205400 0.850900 +vn 0.489500 -0.192900 0.850400 +vn 0.470000 -0.194700 0.860900 +vn 0.489700 -0.258700 0.832600 +vn 0.347400 -0.055600 0.936100 +vn 0.445500 -0.145700 0.883300 +vn 0.311700 -0.041200 0.949300 +vn 0.494300 -0.326600 0.805600 +vn 0.380500 -0.096600 0.919700 +vn 0.400900 -0.151500 0.903500 +vn -0.514300 -0.318100 0.796400 +vn -0.489700 -0.258700 0.832600 +vn -0.470000 -0.194700 0.860900 +vn -0.505500 -0.253600 0.824700 +vn -0.445500 -0.145700 0.883300 +vn -0.380500 -0.096600 0.919700 +vn -0.347400 -0.055600 0.936100 +vn -0.311700 -0.041200 0.949300 +vn -0.496500 -0.263800 0.827000 +vn -0.500200 -0.319900 0.804600 +vn -0.489500 -0.192900 0.850400 +vn -0.483400 -0.205400 0.850900 +vn -0.493700 -0.372800 0.785600 +vn -0.510100 -0.380700 0.771300 +vn -0.494300 -0.326600 0.805600 +vn -0.400900 -0.151500 0.903500 +vn 0.457700 -0.103000 0.883100 +vn 0.473300 -0.142200 0.869300 +vn 0.461900 -0.152900 0.873600 +vn 0.432800 -0.114000 0.894200 +vn 0.398700 -0.088700 0.912800 +vn 0.441900 -0.071200 0.894200 +vn 0.360500 -0.076400 0.929600 +vn 0.423800 -0.044000 0.904700 +vn 0.415700 -0.066000 0.907100 +vn 0.419800 -0.089200 0.903200 +vn 0.264900 -0.064900 0.962100 +vn 0.411000 -0.039100 0.910800 +vn 0.261500 -0.059500 0.963300 +vn 0.428600 -0.113100 0.896400 +vn 0.270600 -0.058900 0.960900 +vn 0.284900 -0.047600 0.957400 +vn -0.457700 -0.103000 0.883100 +vn -0.419800 -0.089200 0.903200 +vn -0.415700 -0.066000 0.907100 +vn -0.441900 -0.071200 0.894200 +vn -0.411000 -0.039100 0.910800 +vn -0.270600 -0.058900 0.960900 +vn -0.264900 -0.064900 0.962100 +vn -0.261500 -0.059600 0.963300 +vn -0.398700 -0.088700 0.912800 +vn -0.432800 -0.114000 0.894200 +vn -0.423800 -0.044000 0.904700 +vn -0.360500 -0.076400 0.929600 +vn -0.461900 -0.152900 0.873600 +vn -0.473300 -0.142200 0.869300 +vn -0.428600 -0.113100 0.896400 +vn -0.284900 -0.047600 0.957400 +vn 0.400300 -0.029600 0.915900 +vn 0.318400 -0.091000 0.943600 +vn 0.277300 -0.105700 0.954900 +vn 0.380000 -0.017100 0.924800 +vn 0.264700 -0.136900 0.954600 +vn 0.378900 -0.009800 0.925400 +vn 0.276400 -0.147500 0.949600 +vn 0.410500 0.013300 0.911800 +vn 0.399800 -0.001600 0.916600 +vn 0.306600 -0.114700 0.944900 +vn 0.277600 -0.083700 0.957000 +vn 0.396300 0.019900 0.917900 +vn 0.435900 0.051400 0.898500 +vn 0.352100 -0.126300 0.927400 +vn 0.403100 -0.016900 0.915000 +vn 0.264200 -0.061200 0.962500 +vn -0.399800 -0.001600 0.916600 +vn -0.410500 0.013300 0.911800 +vn -0.378900 -0.009800 0.925400 +vn -0.435900 0.051400 0.898500 +vn -0.396300 0.019900 0.917900 +vn -0.277600 -0.083700 0.957000 +vn -0.306600 -0.114700 0.944900 +vn -0.352100 -0.126300 0.927400 +vn -0.380000 -0.017100 0.924800 +vn -0.264700 -0.136900 0.954600 +vn -0.277300 -0.105700 0.954900 +vn -0.276400 -0.147500 0.949600 +vn -0.318400 -0.091000 0.943600 +vn -0.400300 -0.029600 0.915900 +vn -0.403100 -0.016900 0.915000 +vn -0.264200 -0.061200 0.962500 +vn 0.423200 0.102200 0.900200 +vn 0.308700 -0.102100 0.945600 +vn 0.331700 -0.020500 0.943100 +vn 0.431500 0.209700 0.877400 +vn 0.334900 0.050500 0.940900 +vn 0.415800 0.296800 0.859600 +vn 0.297400 0.087700 0.950700 +vn 0.445000 0.374800 0.813300 +vn 0.469100 0.267800 0.841500 +vn 0.412900 0.153900 0.897700 +vn 0.423600 0.031000 0.905300 +vn 0.366600 0.343000 0.864800 +vn 0.387300 0.442600 0.808700 +vn 0.351300 0.253800 0.901200 +vn 0.464600 0.143700 0.873700 +vn 0.399600 -0.077400 0.913400 +vn -0.469100 0.267800 0.841500 +vn -0.445000 0.374800 0.813300 +vn -0.415800 0.296800 0.859600 +vn -0.387300 0.442600 0.808700 +vn -0.366700 0.343000 0.864800 +vn -0.423600 0.031000 0.905300 +vn -0.412900 0.153900 0.897700 +vn -0.351300 0.253800 0.901200 +vn -0.431500 0.209700 0.877400 +vn -0.334900 0.050500 0.940900 +vn -0.331700 -0.020500 0.943100 +vn -0.297400 0.087700 0.950700 +vn -0.308700 -0.102100 0.945600 +vn -0.423200 0.102200 0.900200 +vn -0.464700 0.143700 0.873700 +vn -0.399600 -0.077400 0.913400 +vn 0.212200 0.258500 0.942400 +vn 0.292300 0.324300 0.899700 +vn 0.238300 0.064400 0.969100 +vn 0.176200 0.005300 0.984300 +vn 0.135200 -0.062000 0.988900 +vn 0.150300 0.171100 0.973700 +vn 0.139000 -0.112100 0.983900 +vn 0.140600 0.097000 0.985300 +vn 0.137200 0.312800 0.939800 +vn 0.208000 0.394800 0.894900 +vn 0.083300 0.225600 0.970600 +vn 0.125300 0.236500 0.963500 +vn 0.091500 0.177600 0.979800 +vn 0.297300 0.447300 0.843500 +vn 0.146700 0.277500 0.949400 +vn 0.243500 0.296900 0.923300 +vn -0.212200 0.258500 0.942400 +vn -0.208000 0.394800 0.894900 +vn -0.137200 0.312800 0.939800 +vn -0.150300 0.171100 0.973700 +vn -0.125300 0.236500 0.963500 +vn -0.146700 0.277500 0.949400 +vn -0.083300 0.225600 0.970600 +vn -0.091500 0.177600 0.979800 +vn -0.135200 -0.062000 0.988900 +vn -0.176200 0.005300 0.984300 +vn -0.140600 0.097000 0.985300 +vn -0.139000 -0.112100 0.983900 +vn -0.238300 0.064400 0.969100 +vn -0.292300 0.324300 0.899700 +vn -0.297300 0.447300 0.843500 +vn -0.243500 0.296900 0.923300 +vn 0.176600 0.049900 0.983000 +vn 0.161700 0.060700 0.985000 +vn 0.169000 -0.132700 0.976600 +vn 0.197700 -0.133800 0.971100 +vn 0.216500 -0.125200 0.968200 +vn 0.176200 0.050300 0.983100 +vn 0.222300 -0.115600 0.968100 +vn 0.159600 0.049000 0.986000 +vn 0.143100 0.172800 0.974500 +vn 0.155100 0.181400 0.971100 +vn 0.148900 0.141300 0.978700 +vn 0.112900 0.155200 0.981400 +vn 0.121600 0.113300 0.986100 +vn 0.146500 0.197000 0.969400 +vn 0.154800 0.150500 0.976400 +vn 0.133800 0.156900 0.978500 +vn -0.176600 0.049900 0.983000 +vn -0.155100 0.181400 0.971100 +vn -0.143100 0.172800 0.974500 +vn -0.176200 0.050300 0.983100 +vn -0.112900 0.155200 0.981400 +vn -0.154800 0.150500 0.976400 +vn -0.148900 0.141300 0.978700 +vn -0.121600 0.113300 0.986100 +vn -0.216500 -0.125200 0.968200 +vn -0.197700 -0.133800 0.971100 +vn -0.159600 0.049000 0.986000 +vn -0.222300 -0.115600 0.968100 +vn -0.169000 -0.132700 0.976600 +vn -0.161700 0.060700 0.985000 +vn -0.146500 0.197000 0.969400 +vn -0.133800 0.156900 0.978500 +vn 0.120600 0.023700 0.992400 +vn 0.139300 0.038700 0.989500 +vn 0.221600 -0.110500 0.968800 +vn 0.215400 -0.108300 0.970500 +vn 0.205500 -0.106200 0.972900 +vn 0.101200 0.006500 0.994800 +vn 0.055500 0.048700 0.997300 +vn 0.067400 0.084900 0.994100 +vn 0.112600 -0.028300 0.993200 +vn 0.084900 0.122600 0.988800 +vn 0.100700 0.014100 0.994800 +vn 0.101200 0.065100 0.992700 +vn -0.120600 0.023700 0.992400 +vn -0.067400 0.084900 0.994100 +vn -0.055500 0.048700 0.997300 +vn -0.101200 0.006500 0.994800 +vn -0.100700 0.014100 0.994800 +vn -0.112600 -0.028300 0.993200 +vn -0.205500 -0.106200 0.972900 +vn -0.215400 -0.108300 0.970500 +vn -0.221600 -0.110500 0.968800 +vn -0.139300 0.038700 0.989500 +vn -0.084900 0.122600 0.988800 +vn -0.101200 0.065100 0.992700 +vn 0.257600 -0.292500 0.920900 +vn 0.223700 -0.234500 0.946000 +vn 0.167400 -0.093400 0.981400 +vn 0.191400 -0.146000 0.970600 +vn 0.211200 -0.184100 0.959900 +vn 0.171100 -0.039000 0.984500 +vn 0.233100 -0.182500 0.955200 +vn 0.328700 -0.320500 0.888400 +vn 0.284900 -0.195000 0.938500 +vn 0.401500 -0.320800 0.857800 +vn 0.372700 -0.411500 0.831700 +vn 0.394200 -0.464000 0.793200 +vn 0.313300 -0.446900 0.837900 +vn 0.457800 -0.411100 0.788300 +vn 0.469300 -0.457000 0.755500 +vn 0.293700 -0.397200 0.869400 +vn 0.252300 -0.341400 0.905400 +vn 0.233800 -0.283400 0.930100 +vn 0.255100 -0.399600 0.880400 +vn 0.243200 -0.328400 0.912700 +vn -0.257600 -0.292500 0.920900 +vn -0.293700 -0.397200 0.869400 +vn -0.372700 -0.411500 0.831700 +vn -0.328700 -0.320500 0.888400 +vn -0.457800 -0.411100 0.788300 +vn -0.313300 -0.446900 0.837900 +vn -0.394200 -0.464000 0.793200 +vn -0.469300 -0.457000 0.755500 +vn -0.233100 -0.182500 0.955200 +vn -0.191400 -0.146000 0.970600 +vn -0.401500 -0.320800 0.857800 +vn -0.284900 -0.195000 0.938500 +vn -0.167400 -0.093400 0.981400 +vn -0.223700 -0.234500 0.946000 +vn -0.171100 -0.039000 0.984500 +vn -0.252300 -0.341400 0.905400 +vn -0.255100 -0.399600 0.880400 +vn -0.211200 -0.184100 0.959900 +vn -0.233800 -0.283400 0.930100 +vn -0.243200 -0.328400 0.912700 +vn 0.191700 -0.156400 0.968900 +vn 0.132200 -0.191700 0.972500 +vn 0.128100 -0.022700 0.991500 +vn 0.173100 -0.007300 0.984900 +vn 0.035500 -0.249800 0.967600 +vn 0.051900 -0.042000 0.997700 +vn 0.184800 -0.008700 0.982700 +vn 0.214700 -0.151500 0.964800 +vn 0.231900 -0.254100 0.939000 +vn 0.207600 -0.261500 0.942600 +vn 0.235100 -0.290500 0.927500 +vn 0.146300 -0.308600 0.939800 +vn 0.045900 -0.390700 0.919300 +vn 0.215000 -0.302200 0.928600 +vn 0.155500 -0.357600 0.920800 +vn 0.056400 -0.451200 0.890700 +vn -0.191700 -0.156400 0.968900 +vn -0.207600 -0.261500 0.942600 +vn -0.231900 -0.254100 0.939000 +vn -0.214700 -0.151500 0.964800 +vn -0.215000 -0.302200 0.928600 +vn -0.235100 -0.290500 0.927500 +vn -0.184800 -0.008700 0.982700 +vn -0.173100 -0.007300 0.984900 +vn -0.128100 -0.022700 0.991500 +vn -0.132200 -0.191700 0.972500 +vn -0.051900 -0.042000 0.997700 +vn -0.146300 -0.308600 0.939800 +vn -0.155500 -0.357600 0.920800 +vn -0.035500 -0.249800 0.967600 +vn -0.045900 -0.390700 0.919300 +vn -0.056400 -0.451200 0.890700 +vn -0.046500 -0.420300 0.906200 +vn -0.012000 -0.526500 0.850100 +vn 0.119700 -0.125000 0.984900 +vn 0.039800 -0.074000 0.996400 +vn 0.023300 -0.614600 0.788500 +vn 0.205900 -0.213000 0.955100 +vn 0.007500 -0.054400 0.998500 +vn -0.037600 -0.325900 0.944600 +vn -0.039200 -0.502900 0.863400 +vn -0.076100 -0.625500 0.776500 +vn -0.028700 -0.574700 0.817800 +vn -0.081900 -0.728100 0.680500 +vn -0.079100 -0.789100 0.609100 +vn -0.075600 -0.698000 0.712100 +vn -0.102100 -0.788700 0.606200 +vn -0.106500 -0.834900 0.540000 +vn 0.046500 -0.420300 0.906200 +vn 0.076100 -0.625500 0.776500 +vn 0.039200 -0.502900 0.863400 +vn 0.037600 -0.325900 0.944600 +vn 0.075600 -0.698000 0.712100 +vn 0.028700 -0.574700 0.817800 +vn -0.007500 -0.054400 0.998500 +vn -0.039800 -0.074000 0.996400 +vn -0.119700 -0.125000 0.984900 +vn -0.205900 -0.213000 0.955100 +vn -0.023300 -0.614600 0.788500 +vn 0.012000 -0.526500 0.850100 +vn 0.081900 -0.728100 0.680500 +vn 0.102100 -0.788700 0.606200 +vn 0.079100 -0.789100 0.609100 +vn 0.106500 -0.834900 0.540000 +vn 0.055600 -0.649500 0.758300 +vn 0.222500 -0.431600 0.874200 +vn 0.253400 -0.393100 0.883800 +vn 0.008400 -0.579600 0.814800 +vn 0.167300 -0.406100 0.898300 +vn 0.068600 -0.673200 0.736300 +vn 0.256300 -0.314500 0.914000 +vn 0.060600 -0.664700 0.744700 +vn -0.059800 -0.803400 0.592400 +vn -0.045500 -0.783300 0.620000 +vn -0.098800 -0.835300 0.540800 +vn -0.085600 -0.810800 0.579100 +vn -0.047600 -0.738500 0.672500 +vn -0.089100 -0.655700 0.749700 +vn -0.084300 -0.763400 0.640300 +vn -0.120900 -0.680900 0.722300 +vn 0.045500 -0.783300 0.619900 +vn 0.059800 -0.803400 0.592400 +vn -0.060600 -0.664700 0.744700 +vn 0.085600 -0.810800 0.579100 +vn 0.098800 -0.835300 0.540800 +vn -0.068600 -0.673200 0.736300 +vn -0.256300 -0.314500 0.914000 +vn -0.253400 -0.393100 0.883800 +vn -0.222500 -0.431600 0.874200 +vn -0.055600 -0.649500 0.758300 +vn -0.167300 -0.406100 0.898300 +vn -0.008400 -0.579600 0.814800 +vn 0.047600 -0.738500 0.672500 +vn 0.084300 -0.763400 0.640300 +vn 0.089100 -0.655700 0.749700 +vn 0.120900 -0.680900 0.722300 +vn -0.194700 -0.186300 0.963000 +vn 0.018800 -0.133200 0.990900 +vn 0.049100 -0.218800 0.974500 +vn -0.210700 -0.105700 0.971800 +vn 0.011200 -0.087800 0.996100 +vn -0.144400 -0.316600 0.937500 +vn 0.102000 -0.321700 0.941300 +vn -0.069000 -0.460100 0.885100 +vn -0.172600 -0.527500 0.831800 +vn -0.262200 -0.368800 0.891700 +vn -0.204900 -0.551600 0.808500 +vn -0.301100 -0.386900 0.871500 +vn -0.327200 -0.216000 0.919900 +vn -0.351300 -0.112400 0.929500 +vn -0.372400 -0.224100 0.900600 +vn -0.397000 -0.110300 0.911100 +vn 0.262200 -0.368800 0.891700 +vn 0.172600 -0.527500 0.831800 +vn 0.069000 -0.460100 0.885100 +vn 0.301100 -0.386900 0.871500 +vn 0.204900 -0.551600 0.808500 +vn 0.144400 -0.316600 0.937500 +vn -0.102000 -0.321700 0.941300 +vn -0.049100 -0.218800 0.974500 +vn -0.018800 -0.133200 0.990900 +vn 0.194700 -0.186300 0.963000 +vn -0.011200 -0.087800 0.996100 +vn 0.327200 -0.216000 0.919900 +vn 0.372400 -0.224100 0.900600 +vn 0.210700 -0.105700 0.971800 +vn 0.351300 -0.112400 0.929500 +vn 0.397000 -0.110300 0.911100 +vn -0.169400 0.036700 0.984800 +vn -0.121200 0.124300 0.984800 +vn 0.068300 0.044400 0.996700 +vn 0.037800 -0.011000 0.999200 +vn -0.064500 0.207300 0.976100 +vn 0.110500 0.097400 0.989100 +vn 0.020100 -0.058100 0.998100 +vn -0.199300 -0.044500 0.978900 +vn -0.339800 -0.028100 0.940000 +vn -0.301400 0.075000 0.950500 +vn -0.383200 -0.015700 0.923500 +vn -0.240300 0.178700 0.954100 +vn -0.174700 0.272500 0.946100 +vn -0.339800 0.094600 0.935700 +vn -0.274700 0.201300 0.940200 +vn -0.209500 0.295600 0.932000 +vn 0.169400 0.036700 0.984800 +vn 0.301400 0.075000 0.950500 +vn 0.339800 -0.028100 0.940000 +vn 0.199300 -0.044500 0.978900 +vn 0.339800 0.094600 0.935700 +vn 0.383200 -0.015700 0.923500 +vn -0.020100 -0.058100 0.998100 +vn -0.037800 -0.011000 0.999200 +vn -0.068300 0.044400 0.996700 +vn 0.121200 0.124300 0.984800 +vn -0.110500 0.097400 0.989100 +vn 0.240300 0.178700 0.954100 +vn 0.274700 0.201300 0.940200 +vn 0.064500 0.207300 0.976100 +vn 0.174700 0.272500 0.946100 +vn 0.209500 0.295600 0.932000 +vn 0.002100 0.313500 0.949600 +vn 0.017500 0.345900 0.938100 +vn 0.212700 0.119700 0.969700 +vn 0.187000 0.132900 0.973300 +vn 0.033300 0.369000 0.928800 +vn 0.227700 0.095900 0.969000 +vn 0.152500 0.129200 0.979800 +vn -0.022300 0.271100 0.962300 +vn -0.134600 0.348500 0.927600 +vn -0.119600 0.409800 0.904300 +vn -0.174000 0.373200 0.911300 +vn -0.116300 0.466900 0.876600 +vn -0.099300 0.520000 0.848400 +vn -0.164300 0.439300 0.883200 +vn -0.163300 0.498600 0.851300 +vn -0.141300 0.561000 0.815700 +vn -0.002100 0.313500 0.949600 +vn 0.119600 0.409800 0.904300 +vn 0.134600 0.348500 0.927600 +vn 0.022300 0.271100 0.962300 +vn 0.164300 0.439300 0.883200 +vn 0.174000 0.373200 0.911300 +vn -0.152500 0.129200 0.979800 +vn -0.187000 0.132900 0.973300 +vn -0.212700 0.119700 0.969700 +vn -0.017500 0.345900 0.938100 +vn -0.227700 0.095900 0.969000 +vn 0.116300 0.466900 0.876600 +vn 0.163300 0.498600 0.851300 +vn -0.033300 0.369000 0.928800 +vn 0.099300 0.520000 0.848400 +vn 0.141300 0.561000 0.815700 +vn 0.179000 0.370700 0.911300 +vn 0.238700 0.033500 0.970500 +vn 0.237200 0.047200 0.970300 +vn 0.236400 0.363400 0.901200 +vn 0.241900 0.027200 0.969900 +vn 0.119600 0.377800 0.918100 +vn 0.234000 0.068300 0.969800 +vn 0.067200 0.378200 0.923200 +vn -0.047500 0.554000 0.831100 +vn 0.034600 0.570300 0.820700 +vn -0.087800 0.604700 0.791600 +vn 0.002500 0.628300 0.777900 +vn 0.129200 0.567500 0.813100 +vn 0.216900 0.555000 0.803000 +vn 0.107700 0.628100 0.770600 +vn 0.204500 0.613400 0.762800 +vn -0.034600 0.570300 0.820700 +vn 0.047500 0.554000 0.831100 +vn -0.067200 0.378200 0.923200 +vn -0.002500 0.628300 0.777900 +vn 0.087800 0.604700 0.791600 +vn -0.119600 0.377800 0.918100 +vn -0.234000 0.068300 0.969800 +vn -0.237200 0.047200 0.970300 +vn -0.238700 0.033500 0.970500 +vn -0.179000 0.370700 0.911300 +vn -0.241900 0.027200 0.969900 +vn -0.236400 0.363400 0.901200 +vn -0.129200 0.567500 0.813100 +vn -0.107700 0.628100 0.770600 +vn -0.216900 0.555000 0.803000 +vn -0.204500 0.613400 0.762800 +vn 0.328500 0.400700 0.855300 +vn 0.228900 0.065400 0.971200 +vn 0.238500 0.040800 0.970300 +vn 0.342800 0.422000 0.839300 +vn 0.212200 0.110800 0.970900 +vn 0.308200 0.380800 0.871700 +vn 0.243800 0.029800 0.969400 +vn 0.280300 0.367400 0.886800 +vn 0.282400 0.550600 0.785500 +vn 0.325600 0.554500 0.765800 +vn 0.276500 0.604600 0.747000 +vn 0.324100 0.604000 0.728000 +vn 0.359400 0.560700 0.746000 +vn 0.389500 0.561400 0.730100 +vn 0.361900 0.605500 0.708800 +vn 0.397400 0.600500 0.693800 +vn -0.325600 0.554500 0.765800 +vn -0.282400 0.550600 0.785500 +vn -0.280300 0.367400 0.886800 +vn -0.324100 0.604000 0.728000 +vn -0.276500 0.604600 0.747000 +vn -0.308200 0.380800 0.871700 +vn -0.243800 0.029800 0.969400 +vn -0.238500 0.040800 0.970300 +vn -0.228900 0.065400 0.971200 +vn -0.328500 0.400700 0.855300 +vn -0.212200 0.110800 0.970900 +vn -0.342800 0.422000 0.839300 +vn -0.359400 0.560700 0.746000 +vn -0.361900 0.605500 0.708800 +vn -0.389500 0.561400 0.730100 +vn -0.397400 0.600500 0.693800 +vn 0.405700 0.414000 0.814800 +vn 0.470400 0.370000 0.801100 +vn 0.266300 0.214900 0.939600 +vn 0.551100 0.293100 0.781200 +vn 0.344000 0.182500 0.921000 +vn 0.215200 0.208400 0.954000 +vn 0.201100 0.168300 0.965000 +vn 0.361900 0.431500 0.826300 +vn 0.426500 0.544500 0.722300 +vn 0.484200 0.501300 0.717100 +vn 0.441100 0.576500 0.687800 +vn 0.506200 0.524900 0.684300 +vn 0.558500 0.432700 0.707600 +vn 0.639800 0.339600 0.689400 +vn 0.587100 0.446200 0.675300 +vn 0.669900 0.345400 0.657200 +vn -0.484200 0.501300 0.717100 +vn -0.426500 0.544500 0.722300 +vn -0.361900 0.431500 0.826300 +vn -0.506200 0.524900 0.684300 +vn -0.441100 0.576500 0.687800 +vn -0.405700 0.414000 0.814800 +vn -0.201000 0.168300 0.965000 +vn -0.215200 0.208400 0.954000 +vn -0.266300 0.214900 0.939600 +vn -0.470400 0.370000 0.801100 +vn -0.344000 0.182500 0.921000 +vn -0.558500 0.432700 0.707600 +vn -0.587100 0.446200 0.675300 +vn -0.551100 0.293100 0.781200 +vn -0.639800 0.339600 0.689400 +vn -0.669900 0.345400 0.657200 +vn 0.533800 -0.275600 0.799500 +vn 0.471000 -0.302400 0.828600 +vn 0.327100 -0.193600 0.924900 +vn 0.364300 -0.176400 0.914400 +vn 0.396100 -0.155500 0.904900 +vn 0.590000 -0.239300 0.771100 +vn 0.423100 -0.128700 0.896900 +vn 0.635900 -0.191700 0.747600 +vn 0.689700 -0.299700 0.659100 +vn 0.621800 -0.348000 0.701600 +vn 0.710500 -0.327000 0.623100 +vn 0.743600 -0.233300 0.626500 +vn 0.769900 -0.252900 0.585800 +vn 0.546500 -0.383000 0.744700 +vn 0.637400 -0.378700 0.671000 +vn 0.557600 -0.415000 0.718900 +vn -0.533800 -0.275600 0.799500 +vn -0.621800 -0.348000 0.701600 +vn -0.689700 -0.299700 0.659100 +vn -0.590000 -0.239300 0.771100 +vn -0.743600 -0.233300 0.626500 +vn -0.637400 -0.378700 0.671000 +vn -0.710500 -0.327000 0.623100 +vn -0.769900 -0.252900 0.585800 +vn -0.396100 -0.155500 0.904900 +vn -0.364300 -0.176400 0.914400 +vn -0.635900 -0.191700 0.747600 +vn -0.423100 -0.128700 0.896900 +vn -0.327100 -0.193600 0.924900 +vn -0.471000 -0.302400 0.828600 +vn -0.546500 -0.383000 0.744700 +vn -0.557600 -0.415000 0.718900 +vn 0.682400 -0.087500 0.725700 +vn 0.666200 -0.138500 0.732700 +vn 0.445900 -0.099000 0.889500 +vn 0.463300 -0.070200 0.883400 +vn 0.475100 -0.042800 0.878800 +vn 0.689000 -0.040900 0.723600 +vn 0.481300 -0.017700 0.876300 +vn 0.689700 0.001200 0.724100 +vn 0.785900 -0.037600 0.617200 +vn 0.785600 -0.094100 0.611500 +vn 0.813000 -0.036700 0.581000 +vn 0.781400 0.010400 0.624000 +vn 0.806800 0.011800 0.590700 +vn 0.774300 -0.160100 0.612300 +vn 0.814300 -0.096800 0.572300 +vn 0.803200 -0.169800 0.570900 +vn -0.682400 -0.087500 0.725700 +vn -0.785600 -0.094100 0.611500 +vn -0.785900 -0.037600 0.617200 +vn -0.689000 -0.040900 0.723600 +vn -0.781400 0.010400 0.624000 +vn -0.814300 -0.096800 0.572300 +vn -0.813000 -0.036700 0.581000 +vn -0.806800 0.011800 0.590700 +vn -0.475100 -0.042800 0.878800 +vn -0.463300 -0.070200 0.883400 +vn -0.689700 0.001200 0.724100 +vn -0.481300 -0.017700 0.876300 +vn -0.446000 -0.099000 0.889500 +vn -0.666200 -0.138500 0.732700 +vn -0.774300 -0.160100 0.612300 +vn -0.803200 -0.169800 0.570900 +vn 0.661700 0.120200 0.740000 +vn 0.683000 0.051500 0.728500 +vn 0.477800 0.014500 0.878300 +vn 0.457500 0.061900 0.887100 +vn 0.413800 0.120200 0.902400 +vn 0.618800 0.203300 0.758700 +vn 0.706600 0.237900 0.666400 +vn 0.749500 0.143700 0.646200 +vn 0.734900 0.239900 0.634300 +vn 0.771900 0.066400 0.632300 +vn 0.775700 0.143700 0.614400 +vn 0.797000 0.066400 0.600300 +vn -0.661700 0.120200 0.740000 +vn -0.749500 0.143700 0.646200 +vn -0.706600 0.237900 0.666400 +vn -0.618800 0.203300 0.758700 +vn -0.775700 0.143700 0.614400 +vn -0.734900 0.239900 0.634300 +vn -0.413800 0.120200 0.902400 +vn -0.457500 0.061900 0.887100 +vn -0.477800 0.014500 0.878300 +vn -0.683000 0.051500 0.728500 +vn -0.771900 0.066400 0.632300 +vn -0.797000 0.066400 0.600300 +vn -0.319500 0.947500 0.009400 +vn -0.360200 0.932500 0.025500 +vn -0.612600 0.790000 -0.023500 +vn 0.000000 0.999200 0.039200 +vn 0.000000 0.999000 0.044500 +vn -0.553200 0.830200 -0.068800 +vn -0.755500 0.649300 -0.086700 +vn -0.690800 0.698600 -0.186400 +vn -0.831300 0.537300 -0.142000 +vn -0.608400 0.780800 -0.142200 +vn -0.467800 0.883500 0.023800 +vn -0.466300 0.867300 0.173900 +vn -0.352200 0.870200 0.344600 +vn -0.766300 0.566500 -0.303000 +vn -0.657700 0.642500 -0.393200 +vn -0.459600 0.847000 -0.266900 +vn -0.264200 0.955900 0.127900 +vn 0.000000 0.986300 0.164900 +vn -0.196600 0.877300 0.437700 +vn 0.000000 0.880500 0.474000 +vn 0.467800 0.883500 0.023800 +vn 0.608400 0.780800 -0.142200 +vn 0.690800 0.698600 -0.186400 +vn 0.657700 0.642500 -0.393200 +vn 0.766300 0.566500 -0.303000 +vn 0.352200 0.870200 0.344600 +vn 0.466300 0.867300 0.173900 +vn 0.459600 0.847000 -0.266900 +vn 0.553200 0.830200 -0.068800 +vn 0.755500 0.649300 -0.086700 +vn 0.612600 0.790000 -0.023500 +vn 0.831300 0.537300 -0.142000 +vn 0.360200 0.932500 0.025500 +vn 0.319500 0.947500 0.009400 +vn 0.264200 0.955900 0.127900 +vn 0.196600 0.877300 0.437700 +vn -0.793600 0.478400 -0.375900 +vn -0.861200 0.478200 -0.172100 +vn -0.854700 0.487100 -0.179600 +vn -0.786500 0.464000 -0.407500 +vn -0.813900 0.554800 -0.172600 +vn -0.755400 0.511400 -0.409600 +vn -0.702900 0.692600 -0.162100 +vn -0.649100 0.448800 -0.614200 +vn -0.662300 0.428400 -0.614600 +vn -0.505700 0.388000 -0.770500 +vn -0.495800 0.370800 -0.785300 +vn -0.658900 0.632000 -0.407800 +vn -0.580600 0.538600 -0.610500 +vn -0.501100 0.449800 -0.739300 +vn -0.669900 0.490100 -0.557600 +vn -0.481300 0.465300 -0.742900 +vn 0.662300 0.428400 -0.614600 +vn 0.649100 0.448800 -0.614200 +vn 0.755400 0.511400 -0.409600 +vn 0.580600 0.538600 -0.610500 +vn 0.495800 0.370800 -0.785300 +vn 0.505700 0.388000 -0.770500 +vn 0.501100 0.449800 -0.739300 +vn 0.786500 0.464000 -0.407500 +vn 0.813900 0.554800 -0.172600 +vn 0.854700 0.487100 -0.179600 +vn 0.658900 0.632000 -0.407800 +vn 0.702900 0.692600 -0.162100 +vn 0.861200 0.478200 -0.172100 +vn 0.793600 0.478400 -0.375900 +vn 0.669900 0.490100 -0.557600 +vn 0.481300 0.465300 -0.742900 +vn -0.480500 0.771000 -0.417900 +vn -0.501900 0.850500 -0.157100 +vn -0.240700 0.959000 -0.149800 +vn -0.254100 0.871300 -0.419700 +vn 0.043200 0.990000 -0.133900 +vn -0.002500 0.914400 -0.404700 +vn 0.303700 0.946100 -0.112200 +vn -0.046100 0.773100 -0.632600 +vn -0.253600 0.723900 -0.641500 +vn -0.061200 0.584100 -0.809400 +vn -0.236900 0.526800 -0.816300 +vn 0.238700 0.892600 -0.382300 +vn 0.154700 0.769400 -0.619700 +vn 0.073300 0.626500 -0.775900 +vn -0.438600 0.644600 -0.626200 +vn -0.398500 0.477400 -0.783100 +vn 0.253600 0.723900 -0.641500 +vn 0.046100 0.773100 -0.632600 +vn 0.002500 0.914400 -0.404700 +vn -0.154700 0.769400 -0.619700 +vn -0.238700 0.892600 -0.382300 +vn 0.236900 0.526800 -0.816300 +vn 0.061200 0.584100 -0.809400 +vn -0.073300 0.626500 -0.775900 +vn 0.254100 0.871300 -0.419700 +vn -0.043200 0.990000 -0.133900 +vn 0.240700 0.959000 -0.149800 +vn -0.303700 0.946100 -0.112200 +vn 0.501900 0.850500 -0.157100 +vn 0.480500 0.771000 -0.417900 +vn 0.438600 0.644600 -0.626200 +vn 0.398500 0.477400 -0.783100 +vn 0.404200 0.833700 -0.376200 +vn 0.480900 0.871100 -0.099400 +vn 0.558900 0.823000 -0.101600 +vn 0.470400 0.794400 -0.384200 +vn 0.572700 0.812500 -0.108900 +vn 0.477000 0.792100 -0.380800 +vn 0.540100 0.834100 -0.112200 +vn 0.303900 0.725300 -0.617700 +vn 0.311000 0.695200 -0.648000 +vn 0.053500 0.584400 -0.809700 +vn 0.446100 0.823700 -0.350000 +vn 0.300800 0.804100 -0.512800 +vn 0.072400 0.790900 -0.607600 +vn 0.275600 0.725600 -0.630500 +vn 0.094400 0.528900 -0.843400 +vn 0.096700 0.567800 -0.817400 +vn -0.311000 0.695200 -0.648000 +vn -0.303900 0.725300 -0.617700 +vn -0.477000 0.792100 -0.380800 +vn -0.300800 0.804100 -0.512800 +vn -0.446100 0.823700 -0.350000 +vn -0.094400 0.528900 -0.843400 +vn -0.053500 0.584400 -0.809700 +vn -0.072400 0.790900 -0.607600 +vn -0.470400 0.794400 -0.384200 +vn -0.572700 0.812500 -0.108900 +vn -0.558900 0.823000 -0.101600 +vn -0.540100 0.834100 -0.112200 +vn -0.480900 0.871100 -0.099400 +vn -0.404200 0.833700 -0.376200 +vn -0.275600 0.725600 -0.630500 +vn -0.096700 0.567800 -0.817400 +vn 0.413400 0.864400 -0.286000 +vn 0.494100 0.863400 -0.102100 +vn 0.461600 0.882900 -0.085300 +vn 0.401000 0.888500 -0.222800 +vn 0.436400 0.896800 -0.073000 +vn 0.392900 0.899300 -0.191700 +vn 0.413300 0.908300 -0.064100 +vn 0.364100 0.901900 -0.232000 +vn 0.344600 0.897700 -0.274700 +vn 0.351700 0.911500 -0.213000 +vn 0.305200 0.917600 -0.254700 +vn 0.376600 0.908400 -0.181300 +vn 0.361300 0.906100 -0.219900 +vn 0.364200 0.909400 -0.200800 +vn 0.345100 0.866300 -0.361200 +vn 0.228800 0.899000 -0.373300 +vn -0.344600 0.897700 -0.274700 +vn -0.364100 0.901900 -0.232000 +vn -0.392900 0.899300 -0.191700 +vn -0.361300 0.906100 -0.219900 +vn -0.376600 0.908400 -0.181300 +vn -0.305200 0.917600 -0.254700 +vn -0.351700 0.911500 -0.213000 +vn -0.364200 0.909400 -0.200800 +vn -0.401000 0.888500 -0.222800 +vn -0.436400 0.896800 -0.073000 +vn -0.461600 0.882900 -0.085300 +vn -0.413300 0.908300 -0.064100 +vn -0.494100 0.863400 -0.102100 +vn -0.413400 0.864400 -0.286000 +vn -0.345100 0.866300 -0.361200 +vn -0.228800 0.899000 -0.373300 +vn 0.398000 0.898900 -0.183100 +vn 0.436800 0.897600 -0.058500 +vn 0.560600 0.825600 -0.063700 +vn 0.509800 0.835600 -0.204400 +vn 0.759600 0.645700 -0.078000 +vn 0.693300 0.679700 -0.239500 +vn 0.924500 0.370100 -0.090700 +vn 0.662100 0.688000 -0.297000 +vn 0.491300 0.832900 -0.254600 +vn 0.659700 0.696200 -0.283200 +vn 0.497100 0.833000 -0.242800 +vn 0.860100 0.435800 -0.265100 +vn 0.824800 0.465300 -0.321200 +vn 0.819500 0.492000 -0.293900 +vn 0.385300 0.894800 -0.225400 +vn 0.394000 0.894800 -0.210000 +vn -0.491300 0.832900 -0.254600 +vn -0.662100 0.688000 -0.297000 +vn -0.693300 0.679700 -0.239500 +vn -0.824800 0.465300 -0.321200 +vn -0.860100 0.435800 -0.265100 +vn -0.497100 0.833000 -0.242800 +vn -0.659700 0.696200 -0.283200 +vn -0.819500 0.492000 -0.293900 +vn -0.509800 0.835600 -0.204400 +vn -0.759600 0.645700 -0.078000 +vn -0.560600 0.825600 -0.063700 +vn -0.924500 0.370100 -0.090700 +vn -0.436800 0.897600 -0.058500 +vn -0.398000 0.898900 -0.183100 +vn -0.385300 0.894800 -0.225400 +vn -0.394000 0.894800 -0.210000 +vn 0.945300 0.193400 -0.262700 +vn 0.989200 0.113500 -0.092700 +vn 0.993100 -0.079600 -0.085600 +vn 0.970500 -0.010100 -0.240600 +vn 0.965300 -0.249600 -0.076600 +vn 0.956000 -0.202700 -0.211800 +vn 0.904100 -0.421700 -0.068600 +vn 0.963400 -0.181300 -0.197100 +vn 0.967500 0.022900 -0.251900 +vn 0.982500 -0.163900 -0.088200 +vn 0.985300 0.046900 -0.164100 +vn 0.899800 -0.397300 -0.180100 +vn 0.915400 -0.374800 -0.146700 +vn 0.937400 -0.347000 -0.027600 +vn 0.924400 0.234100 -0.301000 +vn 0.931800 0.268300 -0.244500 +vn -0.967500 0.022900 -0.251900 +vn -0.963400 -0.181300 -0.197100 +vn -0.956000 -0.202700 -0.211800 +vn -0.915400 -0.374800 -0.146700 +vn -0.899800 -0.397300 -0.180100 +vn -0.985300 0.046900 -0.164100 +vn -0.982500 -0.163900 -0.088200 +vn -0.937400 -0.347000 -0.027600 +vn -0.970500 -0.010100 -0.240600 +vn -0.965300 -0.249600 -0.076600 +vn -0.993100 -0.079600 -0.085600 +vn -0.904100 -0.421700 -0.068600 +vn -0.989200 0.113500 -0.092700 +vn -0.945300 0.193400 -0.262700 +vn -0.924400 0.234100 -0.301000 +vn -0.931800 0.268300 -0.244500 +vn 0.719100 -0.682600 -0.129700 +vn 0.808400 -0.568800 -0.151400 +vn 0.816600 -0.574300 -0.057500 +vn 0.730900 -0.680700 -0.048700 +vn 0.655400 -0.754100 -0.042100 +vn 0.643300 -0.757300 -0.112100 +vn 0.593500 -0.804100 -0.033700 +vn 0.582600 -0.806500 -0.100800 +vn 0.665600 -0.744400 -0.053100 +vn 0.740800 -0.667300 -0.076900 +vn 0.699500 -0.711200 0.069300 +vn 0.600500 -0.798600 -0.039900 +vn 0.627600 -0.774300 0.080700 +vn 0.825700 -0.553700 -0.107800 +vn 0.773900 -0.631600 0.044800 +vn 0.853100 -0.521700 0.012100 +vn -0.719100 -0.682600 -0.129700 +vn -0.740800 -0.667300 -0.076900 +vn -0.665600 -0.744400 -0.053100 +vn -0.643300 -0.757300 -0.112100 +vn -0.600500 -0.798600 -0.039900 +vn -0.773900 -0.631600 0.044800 +vn -0.699500 -0.711200 0.069300 +vn -0.627600 -0.774300 0.080700 +vn -0.655400 -0.754100 -0.042100 +vn -0.730900 -0.680700 -0.048700 +vn -0.582600 -0.806500 -0.100800 +vn -0.593500 -0.804100 -0.033700 +vn -0.816600 -0.574300 -0.057500 +vn -0.808400 -0.568800 -0.151400 +vn -0.825700 -0.553700 -0.107800 +vn -0.853100 -0.521700 0.012100 +vn 0.462800 -0.880900 -0.098500 +vn 0.525500 -0.845200 -0.097100 +vn 0.534700 -0.844700 -0.023000 +vn 0.471500 -0.881800 -0.009500 +vn 0.408200 -0.912800 0.006500 +vn 0.400300 -0.910900 -0.099800 +vn 0.349600 -0.936500 0.026800 +vn 0.341700 -0.935000 -0.094900 +vn 0.410500 -0.910500 -0.049000 +vn 0.472400 -0.880200 -0.044600 +vn 0.447600 -0.890400 0.081600 +vn 0.355700 -0.933600 -0.042500 +vn 0.418400 -0.898800 0.130400 +vn 0.537900 -0.842100 -0.038900 +vn 0.495800 -0.865300 0.074200 +vn 0.559000 -0.825400 0.078900 +vn -0.462800 -0.880900 -0.098500 +vn -0.472400 -0.880200 -0.044600 +vn -0.410500 -0.910500 -0.049000 +vn -0.400300 -0.910900 -0.099800 +vn -0.355700 -0.933600 -0.042500 +vn -0.495800 -0.865300 0.074200 +vn -0.447600 -0.890400 0.081600 +vn -0.418400 -0.898800 0.130400 +vn -0.408200 -0.912800 0.006500 +vn -0.471500 -0.881800 -0.009500 +vn -0.341700 -0.935000 -0.094900 +vn -0.349600 -0.936500 0.026800 +vn -0.534700 -0.844700 -0.023000 +vn -0.525500 -0.845200 -0.097100 +vn -0.537900 -0.842100 -0.038900 +vn -0.559000 -0.825400 0.078900 +vn 0.220300 -0.384700 -0.896300 +vn 0.113400 -0.362800 -0.924900 +vn 0.110300 -0.504500 -0.856300 +vn 0.000000 -0.349200 -0.937000 +vn 0.000000 -0.488800 -0.872400 +vn 0.213100 -0.251400 -0.944100 +vn 0.117900 -0.240300 -0.963500 +vn 0.000000 -0.233300 -0.972400 +vn 0.229300 -0.526800 -0.818400 +vn 0.115100 -0.652900 -0.748600 +vn 0.247000 -0.664700 -0.705100 +vn 0.127500 -0.786200 -0.604600 +vn 0.000000 -0.640900 -0.767600 +vn 0.000000 -0.781600 -0.623800 +vn 0.397300 -0.649600 -0.648200 +vn 0.358700 -0.528500 -0.769400 +vn 0.563900 -0.583200 -0.584700 +vn 0.494600 -0.486300 -0.720400 +vn 0.271000 -0.781200 -0.562400 +vn 0.433500 -0.744300 -0.508100 +vn 0.616400 -0.649800 -0.444600 +vn 0.322600 -0.392300 -0.861400 +vn 0.287400 -0.253700 -0.923600 +vn 0.416400 -0.365700 -0.832400 +vn 0.338500 -0.234000 -0.911400 +vn -0.358700 -0.528500 -0.769400 +vn -0.397300 -0.649600 -0.648200 +vn -0.247000 -0.664700 -0.705100 +vn -0.433500 -0.744300 -0.508100 +vn -0.271000 -0.781200 -0.562400 +vn -0.494600 -0.486300 -0.720400 +vn -0.563900 -0.583200 -0.584700 +vn -0.616400 -0.649800 -0.444600 +vn -0.229300 -0.526800 -0.818400 +vn -0.115100 -0.652900 -0.748600 +vn -0.110300 -0.504500 -0.856300 +vn -0.127500 -0.786200 -0.604600 +vn -0.113400 -0.362800 -0.924900 +vn -0.220300 -0.384700 -0.896300 +vn -0.117900 -0.240300 -0.963500 +vn -0.213100 -0.251400 -0.944100 +vn -0.322600 -0.392300 -0.861400 +vn -0.416400 -0.365700 -0.832400 +vn -0.287400 -0.253700 -0.923600 +vn -0.338500 -0.234000 -0.911400 +vn 0.162000 -0.170000 -0.972000 +vn 0.099100 -0.191400 -0.976500 +vn 0.110900 -0.155800 -0.981500 +vn 0.000000 -0.200300 -0.979700 +vn 0.000000 -0.161100 -0.986900 +vn 0.137000 -0.245300 -0.959700 +vn 0.084700 -0.273300 -0.958200 +vn 0.000000 -0.285000 -0.958500 +vn 0.184400 -0.142500 -0.972400 +vn 0.117400 -0.167200 -0.978900 +vn 0.201400 -0.164600 -0.965600 +vn 0.000000 -0.167400 -0.985900 +vn 0.252800 -0.155700 -0.954900 +vn 0.220700 -0.123500 -0.967500 +vn 0.272600 -0.135300 -0.952500 +vn 0.222500 -0.099200 -0.969800 +vn 0.188100 -0.142000 -0.971800 +vn 0.154900 -0.208600 -0.965600 +vn 0.180900 -0.111500 -0.977100 +vn 0.142600 -0.169000 -0.975200 +vn -0.220700 -0.123500 -0.967500 +vn -0.252800 -0.155700 -0.954900 +vn -0.201400 -0.164600 -0.965600 +vn -0.222500 -0.099200 -0.969800 +vn -0.272600 -0.135300 -0.952500 +vn -0.184400 -0.142500 -0.972400 +vn -0.117400 -0.167200 -0.978900 +vn -0.110900 -0.155800 -0.981500 +vn -0.099100 -0.191400 -0.976500 +vn -0.162000 -0.170000 -0.972000 +vn -0.084700 -0.273300 -0.958200 +vn -0.137000 -0.245300 -0.959700 +vn -0.188100 -0.142000 -0.971800 +vn -0.180900 -0.111500 -0.977100 +vn -0.154900 -0.208600 -0.965600 +vn -0.142600 -0.169000 -0.975200 +vn 0.053700 -0.582700 -0.810900 +vn 0.035500 -0.610600 -0.791100 +vn 0.049100 -0.487900 -0.871500 +vn 0.000000 -0.621800 -0.783200 +vn 0.000000 -0.500500 -0.865700 +vn 0.049100 -0.734900 -0.676400 +vn 0.031800 -0.755700 -0.654100 +vn 0.000000 -0.764200 -0.644900 +vn 0.076400 -0.456500 -0.886400 +vn 0.067300 -0.378600 -0.923100 +vn 0.107300 -0.347100 -0.931600 +vn 0.000000 -0.391400 -0.920200 +vn 0.116900 -0.304800 -0.945200 +vn 0.078100 -0.413300 -0.907300 +vn 0.099900 -0.257900 -0.961000 +vn 0.057700 -0.364200 -0.929500 +vn 0.050800 -0.543600 -0.837800 +vn 0.048200 -0.706000 -0.706500 +vn 0.029500 -0.498800 -0.866200 +vn 0.031400 -0.673500 -0.738500 +vn -0.078100 -0.413300 -0.907300 +vn -0.116900 -0.304800 -0.945200 +vn -0.107300 -0.347100 -0.931600 +vn -0.057700 -0.364200 -0.929500 +vn -0.099900 -0.257900 -0.961000 +vn -0.076400 -0.456500 -0.886400 +vn -0.067300 -0.378600 -0.923100 +vn -0.049100 -0.487900 -0.871500 +vn -0.035500 -0.610600 -0.791100 +vn -0.053700 -0.582700 -0.810900 +vn -0.031800 -0.755700 -0.654100 +vn -0.049100 -0.734900 -0.676400 +vn -0.050800 -0.543600 -0.837800 +vn -0.029500 -0.498800 -0.866200 +vn -0.048200 -0.706000 -0.706500 +vn -0.031400 -0.673500 -0.738500 +vn 0.075600 -0.962600 -0.260300 +vn 0.092400 -0.953800 -0.285900 +vn 0.112800 -0.990900 -0.073500 +vn 0.087800 -0.994800 -0.051800 +vn 0.096300 -0.945500 -0.311000 +vn 0.126200 -0.987500 -0.093800 +vn 0.049900 -0.998100 -0.034200 +vn 0.044200 -0.969700 -0.240100 +vn 0.000000 -0.999600 -0.026700 +vn 0.000000 -0.972800 -0.231500 +vn 0.037000 -0.888300 -0.457700 +vn 0.060500 -0.875300 -0.479800 +vn 0.000000 -0.893800 -0.448400 +vn 0.067700 -0.858200 -0.508800 +vn 0.060700 -0.840300 -0.538700 +vn -0.075600 -0.962600 -0.260300 +vn -0.060500 -0.875300 -0.479800 +vn -0.037000 -0.888300 -0.457700 +vn -0.044200 -0.969700 -0.240100 +vn -0.049900 -0.998100 -0.034200 +vn -0.087800 -0.994800 -0.051800 +vn -0.112800 -0.990900 -0.073500 +vn -0.092400 -0.953800 -0.285900 +vn -0.126200 -0.987500 -0.093800 +vn -0.067700 -0.858200 -0.508800 +vn -0.096300 -0.945500 -0.311000 +vn -0.060700 -0.840300 -0.538700 +vn 0.191200 -0.915500 -0.353900 +vn 0.345900 -0.854500 -0.387500 +vn 0.369900 -0.918800 -0.137900 +vn 0.226700 -0.966300 -0.121500 +vn 0.591700 -0.685400 -0.424500 +vn 0.605400 -0.780800 -0.154000 +vn 0.153100 -0.982300 -0.108000 +vn 0.116400 -0.936200 -0.331400 +vn 0.071300 -0.821100 -0.566300 +vn 0.143500 -0.789300 -0.596900 +vn 0.033100 -0.637800 -0.769500 +vn 0.290700 -0.718500 -0.631800 +vn 0.497200 -0.558300 -0.664100 +vn 0.102900 -0.592800 -0.798800 +vn 0.242700 -0.528000 -0.813700 +vn 0.388900 -0.455800 -0.800600 +vn -0.191200 -0.915500 -0.353900 +vn -0.143500 -0.789300 -0.596900 +vn -0.071300 -0.821100 -0.566300 +vn -0.116400 -0.936200 -0.331400 +vn -0.102900 -0.592800 -0.798800 +vn -0.033100 -0.637800 -0.769500 +vn -0.153100 -0.982300 -0.108000 +vn -0.226700 -0.966300 -0.121500 +vn -0.369900 -0.918800 -0.137900 +vn -0.345900 -0.854500 -0.387500 +vn -0.605400 -0.780800 -0.154000 +vn -0.290700 -0.718500 -0.631800 +vn -0.242700 -0.528000 -0.813700 +vn -0.591700 -0.685400 -0.424500 +vn -0.497200 -0.558300 -0.664100 +vn -0.388900 -0.455800 -0.800600 +vn 0.908800 -0.147800 -0.390100 +vn 0.934000 0.017200 -0.356800 +vn 0.988600 -0.058300 -0.138800 +vn 0.957400 -0.247600 -0.148200 +vn 0.934000 0.093100 -0.344900 +vn 0.989300 0.041700 -0.139400 +vn 0.839000 -0.520600 -0.158100 +vn 0.807900 -0.407100 -0.425900 +vn 0.670200 -0.329800 -0.664800 +vn 0.766400 -0.109700 -0.632900 +vn 0.448600 -0.313300 -0.837000 +vn 0.805900 0.038200 -0.590700 +vn 0.819700 0.102800 -0.563500 +vn 0.518100 -0.128000 -0.845700 +vn 0.570600 -0.000100 -0.821200 +vn 0.611000 0.063200 -0.789100 +vn -0.908800 -0.147800 -0.390100 +vn -0.766400 -0.109700 -0.632900 +vn -0.670200 -0.329800 -0.664800 +vn -0.807900 -0.407100 -0.425900 +vn -0.518100 -0.128000 -0.845700 +vn -0.448600 -0.313300 -0.837000 +vn -0.839000 -0.520600 -0.158100 +vn -0.957400 -0.247600 -0.148200 +vn -0.988600 -0.058300 -0.138800 +vn -0.934000 0.017200 -0.356800 +vn -0.989300 0.041700 -0.139400 +vn -0.805900 0.038200 -0.590700 +vn -0.570600 -0.000100 -0.821200 +vn -0.934000 0.093100 -0.344900 +vn -0.819700 0.102800 -0.563500 +vn -0.611000 0.063200 -0.789100 +vn 0.930800 0.142800 -0.336400 +vn 0.931300 0.149000 -0.332300 +vn 0.980600 0.134300 -0.142700 +vn 0.982400 0.118900 -0.143700 +vn 0.932900 0.145800 -0.329400 +vn 0.979900 0.140300 -0.141500 +vn 0.985500 0.090400 -0.143400 +vn 0.931600 0.125500 -0.341100 +vn 0.828800 0.129600 -0.544400 +vn 0.837600 0.141800 -0.527500 +vn 0.644100 0.094700 -0.759000 +vn 0.846700 0.142000 -0.512700 +vn 0.856800 0.131000 -0.498600 +vn 0.673900 0.107800 -0.730800 +vn 0.704400 0.104500 -0.702000 +vn 0.738900 0.084300 -0.668400 +vn -0.930800 0.142800 -0.336400 +vn -0.837600 0.141800 -0.527500 +vn -0.828800 0.129600 -0.544400 +vn -0.931600 0.125500 -0.341100 +vn -0.673900 0.107800 -0.730800 +vn -0.644100 0.094700 -0.759000 +vn -0.985500 0.090400 -0.143400 +vn -0.982400 0.118900 -0.143700 +vn -0.980600 0.134300 -0.142700 +vn -0.931300 0.149000 -0.332300 +vn -0.979900 0.140300 -0.141500 +vn -0.846700 0.142000 -0.512700 +vn -0.704400 0.104500 -0.702000 +vn -0.932900 0.145800 -0.329400 +vn -0.856800 0.131000 -0.498600 +vn -0.738900 0.084300 -0.668400 +vn 0.470300 0.048000 -0.881200 +vn 0.420900 0.033100 -0.906500 +vn 0.272500 -0.026700 -0.961800 +vn 0.369600 -0.006400 -0.929100 +vn 0.223300 -0.070600 -0.972200 +vn 0.187600 -0.075900 -0.979300 +vn 0.236500 -0.065700 -0.969400 +vn 0.144000 -0.125300 -0.981600 +vn 0.326800 -0.014300 -0.944900 +vn 0.301000 -0.097600 -0.948600 +vn 0.397100 -0.034900 -0.917100 +vn 0.391800 -0.183800 -0.901500 +vn 0.526000 0.039400 -0.849500 +vn 0.494000 -0.096800 -0.864000 +vn 0.595200 0.003700 -0.803600 +vn -0.326800 -0.014300 -0.944900 +vn -0.397100 -0.034900 -0.917100 +vn -0.301000 -0.097600 -0.948600 +vn -0.236500 -0.065700 -0.969400 +vn -0.494000 -0.096800 -0.864000 +vn -0.391800 -0.183800 -0.901500 +vn -0.187600 -0.075900 -0.979300 +vn -0.272500 -0.026700 -0.961800 +vn -0.144000 -0.125300 -0.981600 +vn -0.223300 -0.070600 -0.972200 +vn -0.420900 0.033100 -0.906500 +vn -0.470300 0.048000 -0.881200 +vn -0.369600 -0.006400 -0.929100 +vn -0.526000 0.039400 -0.849500 +vn -0.595200 0.003700 -0.803600 +vn 0.113800 -0.246300 -0.962500 +vn 0.168600 -0.144200 -0.975100 +vn 0.310100 -0.074600 -0.947800 +vn 0.257100 -0.182500 -0.949000 +vn 0.229900 -0.347500 -0.909000 +vn 0.087500 -0.398600 -0.912900 +vn 0.025200 -0.449600 -0.892800 +vn 0.051600 -0.310800 -0.949100 +vn 0.096300 -0.206500 -0.973700 +vn -0.051600 -0.310800 -0.949100 +vn -0.025200 -0.449600 -0.892800 +vn -0.087500 -0.398600 -0.912900 +vn -0.113800 -0.246300 -0.962500 +vn -0.229900 -0.347500 -0.909000 +vn -0.257100 -0.182500 -0.949000 +vn -0.310100 -0.074600 -0.947800 +vn -0.168600 -0.144200 -0.975100 +vn -0.096300 -0.206500 -0.973700 +vn 0.801200 -0.279100 -0.529300 +vn 0.873800 -0.087300 -0.478300 +vn 0.786500 -0.094700 -0.610200 +vn 0.879100 0.051100 -0.473900 +vn 0.875200 -0.252500 -0.412500 +vn 0.919300 -0.060600 -0.388700 +vn 0.916400 0.076000 -0.392900 +vn 0.681700 -0.042700 -0.730300 +vn 0.613800 -0.174500 -0.769900 +vn 0.832000 0.047900 -0.552600 +vn 0.782500 0.060100 -0.619700 +vn 0.504000 -0.293000 -0.812500 +vn 0.615800 -0.386200 -0.686800 +vn 0.714400 -0.258800 -0.650100 +vn 0.708500 -0.452000 -0.541900 +vn 0.790200 -0.464900 -0.399200 +vn -0.615800 -0.386200 -0.686800 +vn -0.504000 -0.293000 -0.812500 +vn -0.613800 -0.174500 -0.769900 +vn -0.681700 -0.042700 -0.730300 +vn -0.786500 -0.094700 -0.610200 +vn -0.782500 0.060100 -0.619700 +vn -0.832000 0.047900 -0.552600 +vn -0.873800 -0.087300 -0.478300 +vn -0.801200 -0.279100 -0.529300 +vn -0.919300 -0.060600 -0.388700 +vn -0.875200 -0.252500 -0.412500 +vn -0.879100 0.051100 -0.473900 +vn -0.916400 0.076000 -0.392900 +vn -0.714400 -0.258800 -0.650100 +vn -0.708500 -0.452000 -0.541900 +vn -0.790200 -0.464900 -0.399200 +vn 0.941900 0.147200 -0.302000 +vn 0.947900 0.164000 -0.273200 +vn 0.978700 0.177500 -0.103100 +vn 0.979900 0.156200 -0.123700 +vn 0.951300 0.198400 -0.235900 +vn 0.973700 0.214900 -0.075600 +vn 0.979900 0.144700 -0.136900 +vn 0.936200 0.141900 -0.321300 +vn 0.870900 0.118800 -0.476900 +vn 0.889500 0.117100 -0.441600 +vn 0.909300 0.128700 -0.395700 +vn 0.925800 0.159600 -0.342700 +vn -0.941900 0.147200 -0.302000 +vn -0.889500 0.117100 -0.441600 +vn -0.870900 0.118800 -0.476900 +vn -0.936200 0.141900 -0.321300 +vn -0.979900 0.144700 -0.136900 +vn -0.979900 0.156200 -0.123700 +vn -0.978700 0.177500 -0.103100 +vn -0.947900 0.164000 -0.273200 +vn -0.973700 0.214900 -0.075600 +vn -0.909300 0.128700 -0.395700 +vn -0.951300 0.198400 -0.235900 +vn -0.925800 0.159600 -0.342700 +vn 0.971100 0.193800 0.139000 +vn 0.953100 0.286500 0.097300 +vn 0.952900 0.296700 0.061800 +vn 0.946100 0.322600 0.029300 +vn 0.973200 0.229400 -0.015400 +vn 0.976900 0.201200 0.071700 +vn 0.988600 -0.036100 0.146200 +vn 0.965800 -0.056200 0.253000 +vn 0.898800 -0.380300 0.218000 +vn 0.860500 -0.378800 0.340500 +vn 0.999800 -0.016900 -0.004100 +vn 0.912600 -0.398900 0.090000 +vn 0.930100 -0.036400 0.365500 +vn 0.960600 0.194600 0.198200 +vn 0.889700 0.000600 0.456500 +vn 0.809000 -0.366500 0.459600 +vn 0.741700 -0.318600 0.590200 +vn 0.952400 0.281200 0.117300 +vn 0.948600 0.206900 0.239500 +vn 0.949100 0.284200 0.135500 +vn -0.971100 0.193800 0.139000 +vn -0.960600 0.194600 0.198200 +vn -0.930100 -0.036400 0.365500 +vn -0.965800 -0.056200 0.253000 +vn -0.809000 -0.366500 0.459600 +vn -0.948600 0.206900 0.239500 +vn -0.889700 0.000600 0.456500 +vn -0.741700 -0.318600 0.590200 +vn -0.988600 -0.036100 0.146200 +vn -0.976900 0.201200 0.071700 +vn -0.999800 -0.016900 -0.004100 +vn -0.973200 0.229400 -0.015400 +vn -0.860500 -0.378800 0.340500 +vn -0.898800 -0.380300 0.218000 +vn -0.912600 -0.398900 0.090000 +vn -0.952900 0.296700 0.061800 +vn -0.946100 0.322600 0.029300 +vn -0.953100 0.286500 0.097300 +vn -0.952400 0.281200 0.117300 +vn -0.949100 0.284200 0.135500 +vn 0.953400 0.300800 -0.021700 +vn 0.964100 0.261400 -0.047100 +vn 0.950700 0.239600 -0.197100 +vn 0.952200 0.263000 -0.155500 +vn 0.938800 0.184300 -0.290900 +vn 0.959700 0.158700 -0.231700 +vn 0.945200 0.076800 -0.317100 +vn 0.982600 0.110500 -0.148900 +vn 0.959100 0.265200 -0.098400 +vn 0.973000 -0.002000 -0.230700 +vn 0.981600 -0.159800 -0.104600 +vn 0.945900 0.324500 0.003800 +vn -0.959100 0.265200 -0.098400 +vn -0.982600 0.110500 -0.148900 +vn -0.959700 0.158700 -0.231700 +vn -0.981600 -0.159800 -0.104600 +vn -0.973000 -0.002000 -0.230700 +vn -0.952200 0.263000 -0.155500 +vn -0.938800 0.184300 -0.290900 +vn -0.950700 0.239600 -0.197100 +vn -0.945200 0.076800 -0.317100 +vn -0.964100 0.261400 -0.047100 +vn -0.953400 0.300800 -0.021700 +vn -0.945900 0.324500 0.003800 +vn 0.777800 -0.617300 0.118100 +vn 0.806000 -0.569700 0.160500 +vn 0.976000 -0.153700 0.154100 +vn 0.517600 -0.853300 0.062900 +vn 0.527900 -0.837100 0.143500 +vn 0.959700 -0.210600 0.185800 +vn 0.972100 0.184100 0.145500 +vn 0.963900 0.114300 0.240300 +vn 0.883600 -0.046500 0.465800 +vn 0.872600 -0.328400 0.361600 +vn 0.694000 -0.311600 0.649000 +vn 0.703600 -0.681400 0.201300 +vn 0.501100 -0.862600 0.069500 +vn 0.675200 -0.507900 0.534800 +vn 0.583300 -0.748600 0.315200 +vn 0.480400 -0.865200 0.143800 +vn -0.872600 -0.328400 0.361600 +vn -0.883600 -0.046500 0.465800 +vn -0.963900 0.114300 0.240300 +vn -0.675200 -0.507900 0.534800 +vn -0.694000 -0.311600 0.649000 +vn -0.959700 -0.210600 0.185800 +vn -0.972100 0.184100 0.145500 +vn -0.976000 -0.153700 0.154100 +vn -0.806000 -0.569700 0.160500 +vn -0.777800 -0.617300 0.118100 +vn -0.527900 -0.837100 0.143500 +vn -0.703600 -0.681400 0.201300 +vn -0.583300 -0.748600 0.315200 +vn -0.517600 -0.853300 0.062900 +vn -0.501100 -0.862600 0.069500 +vn -0.480400 -0.865200 0.143800 +vn 0.316400 -0.944800 0.085000 +vn 0.367500 -0.922800 0.115800 +vn 0.369100 -0.929300 -0.008900 +vn 0.311400 -0.948100 -0.063600 +vn 0.382000 -0.922900 -0.046800 +vn 0.428600 -0.896600 0.111000 +vn 0.360400 -0.926400 0.108800 +vn 0.311800 -0.945900 -0.090000 +vn 0.306300 -0.949900 -0.062000 +vn 0.304700 -0.948700 -0.084400 +vn 0.360800 -0.922300 0.138300 +vn 0.313500 -0.948000 0.054400 +vn -0.304700 -0.948700 -0.084400 +vn -0.306300 -0.949900 -0.062000 +vn -0.311800 -0.945900 -0.090000 +vn -0.360800 -0.922300 0.138300 +vn -0.311400 -0.948100 -0.063600 +vn -0.382000 -0.922900 -0.046800 +vn -0.369100 -0.929300 -0.008900 +vn -0.360400 -0.926400 0.108800 +vn -0.428600 -0.896600 0.111000 +vn -0.367500 -0.922800 0.115800 +vn -0.316400 -0.944800 0.085000 +vn -0.313500 -0.948000 0.054400 +vn 0.166600 -0.494600 -0.853000 +vn 0.171600 -0.365600 -0.914800 +vn 0.289300 -0.367700 -0.883800 +vn 0.172100 -0.250100 -0.952800 +vn 0.291400 -0.263200 -0.919700 +vn 0.000000 -0.497700 -0.867300 +vn 0.000000 -0.365200 -0.930900 +vn 0.000000 -0.244800 -0.969600 +vn 0.281700 -0.489700 -0.825100 +vn 0.364900 -0.372200 -0.853400 +vn 0.356900 -0.488100 -0.796400 +vn 0.406800 -0.378500 -0.831400 +vn 0.368400 -0.280500 -0.886300 +vn 0.414600 -0.296000 -0.860500 +vn 0.354000 -0.606000 -0.712300 +vn 0.274300 -0.610300 -0.743100 +vn 0.358700 -0.700300 -0.617100 +vn 0.270300 -0.709200 -0.651100 +vn 0.400200 -0.491900 -0.773200 +vn 0.414300 -0.611200 -0.674300 +vn 0.440800 -0.690800 -0.573000 +vn 0.160100 -0.618500 -0.769200 +vn 0.000000 -0.623500 -0.781800 +vn 0.154600 -0.719700 -0.676800 +vn 0.000000 -0.725400 -0.688300 +vn -0.274300 -0.610300 -0.743100 +vn -0.354000 -0.606000 -0.712300 +vn -0.356900 -0.488100 -0.796400 +vn -0.414300 -0.611200 -0.674300 +vn -0.400200 -0.491900 -0.773200 +vn -0.270300 -0.709200 -0.651100 +vn -0.358700 -0.700300 -0.617100 +vn -0.440800 -0.690800 -0.573000 +vn -0.281700 -0.489700 -0.825100 +vn -0.364900 -0.372200 -0.853400 +vn -0.289300 -0.367700 -0.883800 +vn -0.368400 -0.280500 -0.886300 +vn -0.406800 -0.378500 -0.831400 +vn -0.414600 -0.296000 -0.860500 +vn -0.171600 -0.365600 -0.914800 +vn -0.166600 -0.494600 -0.853000 +vn -0.291400 -0.263200 -0.919700 +vn -0.172100 -0.250100 -0.952800 +vn -0.160100 -0.618500 -0.769200 +vn -0.154600 -0.719700 -0.676800 +vn 0.145600 -0.861900 -0.485700 +vn 0.150100 -0.798700 -0.582700 +vn 0.267900 -0.785600 -0.557700 +vn 0.000000 -0.868700 -0.495300 +vn 0.000000 -0.804900 -0.593400 +vn 0.264300 -0.846000 -0.463100 +vn 0.365900 -0.770800 -0.521500 +vn 0.457400 -0.750000 -0.477700 +vn 0.465600 -0.793900 -0.391000 +vn 0.369000 -0.823500 -0.430900 +vn 0.366400 -0.865700 -0.340900 +vn 0.259500 -0.891900 -0.370400 +vn 0.365200 -0.896000 -0.252600 +vn 0.255500 -0.924900 -0.281600 +vn 0.468000 -0.830000 -0.303500 +vn 0.468900 -0.856800 -0.214500 +vn 0.141000 -0.909800 -0.390400 +vn 0.000000 -0.917200 -0.398500 +vn 0.137200 -0.944100 -0.299700 +vn 0.000000 -0.951800 -0.306600 +vn -0.259500 -0.891900 -0.370400 +vn -0.366400 -0.865700 -0.340900 +vn -0.369000 -0.823500 -0.430900 +vn -0.468000 -0.830000 -0.303500 +vn -0.255500 -0.924900 -0.281600 +vn -0.365200 -0.896000 -0.252600 +vn -0.468900 -0.856800 -0.214500 +vn -0.264300 -0.846000 -0.463100 +vn -0.365900 -0.770800 -0.521500 +vn -0.267900 -0.785600 -0.557700 +vn -0.465600 -0.793900 -0.391000 +vn -0.457400 -0.750000 -0.477700 +vn -0.150100 -0.798700 -0.582700 +vn -0.145600 -0.861900 -0.485700 +vn -0.141000 -0.909800 -0.390400 +vn -0.137200 -0.944100 -0.299700 +vn 0.271600 -0.948300 -0.163900 +vn 0.141900 -0.972500 -0.184800 +vn 0.137200 -0.964000 -0.227600 +vn 0.000000 -0.981500 -0.191300 +vn 0.000000 -0.972000 -0.234800 +vn 0.258500 -0.943400 -0.207400 +vn 0.371900 -0.911200 -0.177100 +vn 0.391200 -0.911200 -0.129100 +vn 0.480500 -0.866100 -0.137300 +vn 0.506100 -0.858500 -0.082700 +vn 0.417100 -0.902800 -0.104800 +vn 0.290000 -0.946900 -0.139000 +vn 0.447700 -0.889200 -0.093500 +vn 0.548000 -0.835300 -0.045000 +vn 0.616200 -0.787500 -0.001600 +vn 0.149000 -0.975500 -0.161500 +vn 0.000000 -0.985700 -0.168600 +vn 0.305600 -0.942000 -0.138200 +vn 0.155200 -0.974800 -0.160400 +vn 0.000000 -0.985800 -0.167800 +vn -0.271600 -0.948300 -0.163900 +vn -0.290000 -0.946900 -0.139000 +vn -0.417100 -0.902800 -0.104800 +vn -0.391200 -0.911200 -0.129100 +vn -0.548000 -0.835300 -0.045000 +vn -0.305600 -0.942000 -0.138200 +vn -0.447700 -0.889200 -0.093500 +vn -0.616200 -0.787500 -0.001600 +vn -0.371900 -0.911200 -0.177100 +vn -0.258500 -0.943400 -0.207400 +vn -0.506100 -0.858500 -0.082700 +vn -0.480500 -0.866100 -0.137300 +vn -0.137200 -0.964000 -0.227600 +vn -0.141900 -0.972500 -0.184800 +vn -0.149000 -0.975500 -0.161500 +vn -0.155200 -0.974800 -0.160400 +vn 0.306400 -0.919800 -0.245000 +vn 0.153900 -0.950300 -0.270700 +vn 0.157000 -0.969300 -0.189400 +vn 0.310300 -0.935700 -0.167600 +vn 0.000000 -0.959100 -0.283000 +vn 0.000000 -0.980200 -0.198100 +vn 0.462900 -0.877300 -0.126200 +vn 0.464100 -0.861900 -0.204400 +vn 0.626800 -0.777800 -0.045200 +vn 0.645800 -0.752000 -0.131800 +vn 0.457800 -0.818500 -0.347000 +vn 0.294300 -0.871200 -0.393000 +vn 0.653000 -0.699400 -0.290400 +vn 0.143500 -0.892300 -0.428000 +vn 0.000000 -0.895500 -0.445100 +vn -0.306400 -0.919800 -0.245000 +vn -0.294300 -0.871200 -0.393000 +vn -0.457800 -0.818500 -0.347000 +vn -0.464100 -0.861900 -0.204400 +vn -0.653000 -0.699400 -0.290400 +vn -0.462900 -0.877300 -0.126200 +vn -0.310300 -0.935700 -0.167600 +vn -0.645800 -0.752000 -0.131800 +vn -0.626800 -0.777800 -0.045200 +vn -0.157000 -0.969300 -0.189400 +vn -0.153900 -0.950300 -0.270700 +vn -0.143500 -0.892300 -0.428000 +vn 0.965400 -0.192200 -0.176100 +vn 0.949900 -0.097600 -0.296700 +vn 0.909200 -0.318300 -0.268300 +vn 0.905300 -0.407100 -0.120600 +vn 0.818700 -0.517300 -0.249100 +vn 0.793300 -0.601400 -0.094700 +vn 0.758100 -0.651900 0.012500 +vn 0.858100 -0.513200 0.017700 +vn 0.732400 -0.676700 0.074800 +vn 0.794000 -0.597600 0.111100 +vn 0.934100 -0.356200 -0.022300 +vn 0.839400 -0.530800 0.116400 +vn -0.858100 -0.513200 0.017700 +vn -0.758100 -0.651900 0.012500 +vn -0.793300 -0.601400 -0.094700 +vn -0.794000 -0.597600 0.111100 +vn -0.732400 -0.676700 0.074800 +vn -0.905300 -0.407100 -0.120600 +vn -0.818700 -0.517300 -0.249100 +vn -0.909200 -0.318300 -0.268300 +vn -0.949900 -0.097600 -0.296700 +vn -0.965400 -0.192200 -0.176100 +vn -0.934100 -0.356200 -0.022300 +vn -0.839400 -0.530800 0.116400 +vn 0.832800 -0.523300 0.180700 +vn 0.905000 -0.399900 0.144900 +vn 0.876500 -0.468700 0.110000 +vn 0.803100 -0.578600 0.142200 +vn 0.963100 -0.242800 0.115800 +vn 0.951600 -0.297800 0.075600 +vn 0.727800 -0.665000 0.167100 +vn 0.754700 -0.622400 0.207300 +vn 0.646400 -0.742400 0.176100 +vn 0.658100 -0.721400 0.215400 +vn 0.784900 -0.583000 0.209700 +vn 0.863600 -0.470500 0.181100 +vn 0.826300 -0.539200 0.162800 +vn 0.665500 -0.712800 0.221300 +vn 0.683200 -0.709700 0.171500 +vn 0.927500 -0.337300 0.161100 +vn 0.970100 -0.193900 0.145600 +vn 0.896100 -0.422800 0.134900 +vn 0.947200 -0.293700 0.128700 +vn 0.978900 -0.146100 0.143100 +vn -0.832800 -0.523300 0.180700 +vn -0.863600 -0.470500 0.181100 +vn -0.784900 -0.583000 0.209700 +vn -0.754700 -0.622400 0.207300 +vn -0.665500 -0.712800 0.221300 +vn -0.896100 -0.422800 0.134900 +vn -0.826300 -0.539200 0.162800 +vn -0.683200 -0.709700 0.171500 +vn -0.727800 -0.665000 0.167100 +vn -0.803100 -0.578600 0.142200 +vn -0.658100 -0.721400 0.215400 +vn -0.646400 -0.742400 0.176100 +vn -0.876500 -0.468700 0.110000 +vn -0.905000 -0.399900 0.144900 +vn -0.951600 -0.297800 0.075600 +vn -0.927500 -0.337300 0.161100 +vn -0.947200 -0.293700 0.128700 +vn -0.963100 -0.242800 0.115800 +vn -0.970100 -0.193900 0.145600 +vn -0.978900 -0.146100 0.143100 +vn 0.166600 -0.012900 -0.985900 +vn 0.167600 0.136700 -0.976300 +vn 0.287600 0.112100 -0.951100 +vn 0.170000 0.313500 -0.934200 +vn 0.292200 0.288800 -0.911700 +vn 0.000000 -0.002900 -1.000000 +vn 0.000000 0.146900 -0.989100 +vn 0.000000 0.324000 -0.946000 +vn 0.285300 -0.037500 -0.957700 +vn 0.369500 0.079300 -0.925800 +vn 0.365100 -0.070000 -0.928300 +vn 0.422300 0.042400 -0.905500 +vn 0.375800 0.256900 -0.890300 +vn 0.428800 0.220400 -0.876100 +vn 0.365600 -0.189100 -0.911300 +vn 0.287500 -0.160800 -0.944200 +vn 0.415800 -0.104700 -0.903400 +vn 0.413900 -0.219800 -0.883400 +vn 0.168800 -0.139000 -0.975800 +vn 0.000000 -0.130200 -0.991500 +vn -0.287500 -0.160800 -0.944200 +vn -0.365600 -0.189100 -0.911300 +vn -0.365100 -0.070000 -0.928300 +vn -0.413900 -0.219800 -0.883400 +vn -0.415800 -0.104700 -0.903400 +vn -0.285300 -0.037500 -0.957700 +vn -0.369500 0.079300 -0.925800 +vn -0.287600 0.112100 -0.951100 +vn -0.375800 0.256900 -0.890300 +vn -0.422300 0.042400 -0.905500 +vn -0.428800 0.220400 -0.876100 +vn -0.167600 0.136700 -0.976300 +vn -0.166600 -0.012900 -0.985900 +vn -0.292200 0.288800 -0.911700 +vn -0.170000 0.313500 -0.934200 +vn -0.168800 -0.139000 -0.975800 +vn 0.064500 0.961700 0.266300 +vn 0.072000 0.905600 0.417900 +vn 0.153500 0.893600 0.421900 +vn 0.082500 0.819100 0.567700 +vn 0.170600 0.802700 0.571500 +vn 0.000000 0.964500 0.264000 +vn 0.000000 0.909700 0.415100 +vn 0.000000 0.825200 0.564800 +vn 0.140300 0.952500 0.270100 +vn 0.255600 0.870500 0.420600 +vn 0.237700 0.932800 0.270600 +vn 0.391800 0.826700 0.403700 +vn 0.275400 0.775800 0.567600 +vn 0.410000 0.732700 0.543100 +vn 0.223800 0.965600 0.132300 +vn 0.131600 0.982600 0.131000 +vn 0.215300 0.976400 0.013700 +vn 0.127300 0.991800 0.011700 +vn 0.371500 0.891000 0.260900 +vn 0.352700 0.926900 0.127800 +vn 0.338600 0.940900 0.012300 +vn 0.059900 0.990000 0.127900 +vn 0.000000 0.992000 0.125700 +vn 0.058100 0.998300 0.008900 +vn 0.000000 1.000000 0.007400 +vn -0.131600 0.982600 0.131000 +vn -0.223800 0.965600 0.132300 +vn -0.237700 0.932800 0.270600 +vn -0.352700 0.926900 0.127800 +vn -0.371500 0.891000 0.260900 +vn -0.127300 0.991800 0.011700 +vn -0.215300 0.976400 0.013700 +vn -0.338600 0.940900 0.012300 +vn -0.140300 0.952500 0.270100 +vn -0.255600 0.870500 0.420600 +vn -0.153500 0.893600 0.421900 +vn -0.275400 0.775800 0.567600 +vn -0.391800 0.826700 0.403700 +vn -0.410000 0.732700 0.543100 +vn -0.072000 0.905600 0.417900 +vn -0.064500 0.961700 0.266300 +vn -0.170600 0.802700 0.571500 +vn -0.082500 0.819100 0.567700 +vn -0.059900 0.990000 0.127900 +vn -0.058100 0.998300 0.008900 +vn 0.141900 0.960000 -0.241300 +vn 0.070300 0.969400 -0.235000 +vn 0.060500 0.992600 -0.105600 +vn 0.128800 0.986000 -0.106100 +vn 0.000000 0.972500 -0.233000 +vn 0.000000 0.994400 -0.105900 +vn 0.212300 0.971400 -0.106400 +vn 0.219100 0.943600 -0.248200 +vn 0.328700 0.938300 -0.107100 +vn 0.240900 0.883800 -0.401100 +vn 0.169200 0.906000 -0.388000 +vn 0.275500 0.790100 -0.547500 +vn 0.324600 0.911300 -0.253300 +vn 0.330800 0.849200 -0.411500 +vn 0.348100 0.752100 -0.559600 +vn 0.089100 0.921700 -0.377500 +vn 0.000000 0.927500 -0.373900 +vn 0.207000 0.819600 -0.534200 +vn 0.114900 0.843000 -0.525500 +vn 0.000000 0.852300 -0.523000 +vn -0.141900 0.960000 -0.241300 +vn -0.169200 0.906000 -0.388000 +vn -0.240900 0.883800 -0.401100 +vn -0.330800 0.849200 -0.411500 +vn -0.324600 0.911300 -0.253300 +vn -0.207000 0.819600 -0.534200 +vn -0.275500 0.790100 -0.547500 +vn -0.348100 0.752100 -0.559600 +vn -0.219100 0.943600 -0.248200 +vn -0.212300 0.971400 -0.106400 +vn -0.128800 0.986000 -0.106100 +vn -0.328700 0.938300 -0.107100 +vn -0.060500 0.992600 -0.105600 +vn -0.070300 0.969400 -0.235000 +vn -0.089100 0.921700 -0.377500 +vn -0.114900 0.843000 -0.525500 +vn 0.274500 0.593700 -0.756400 +vn 0.159800 0.622100 -0.766400 +vn 0.140700 0.739900 -0.657800 +vn 0.245400 0.711700 -0.658200 +vn 0.000000 0.634400 -0.773000 +vn 0.000000 0.751900 -0.659300 +vn 0.314600 0.677400 -0.664900 +vn 0.347300 0.559300 -0.752700 +vn 0.374300 0.637500 -0.673400 +vn 0.369600 0.426400 -0.825500 +vn 0.402000 0.519900 -0.753700 +vn 0.422600 0.387200 -0.819400 +vn 0.290200 0.460100 -0.839000 +vn 0.169000 0.484200 -0.858400 +vn 0.000000 0.494300 -0.869300 +vn -0.274500 0.593700 -0.756400 +vn -0.290200 0.460100 -0.839000 +vn -0.369600 0.426400 -0.825500 +vn -0.422600 0.387200 -0.819400 +vn -0.402000 0.519900 -0.753700 +vn -0.347300 0.559300 -0.752700 +vn -0.314600 0.677400 -0.664900 +vn -0.245400 0.711700 -0.658200 +vn -0.374300 0.637500 -0.673400 +vn -0.140700 0.739900 -0.657800 +vn -0.159800 0.622100 -0.766400 +vn -0.169000 0.484200 -0.858400 +vn 0.552200 0.833100 -0.030800 +vn 0.456400 0.889700 0.007200 +vn 0.420300 0.897500 -0.133900 +vn 0.521000 0.836700 -0.168800 +vn 0.432700 0.901200 0.026300 +vn 0.387600 0.913900 -0.120200 +vn 0.673700 0.709600 -0.206100 +vn 0.695500 0.714900 -0.072100 +vn 0.825600 0.526000 -0.203800 +vn 0.712500 0.694300 0.101400 +vn 0.573100 0.805000 0.153100 +vn 0.711000 0.638600 0.294400 +vn 0.831000 0.551000 -0.075900 +vn 0.837800 0.540400 0.077800 +vn 0.841600 0.484400 0.238700 +vn 0.486700 0.851400 0.195600 +vn 0.486200 0.847000 0.214800 +vn 0.570000 0.740800 0.355300 +vn 0.496800 0.772200 0.396000 +vn 0.530100 0.744900 0.405100 +vn -0.552200 0.833100 -0.030800 +vn -0.573100 0.805000 0.153100 +vn -0.712500 0.694300 0.101400 +vn -0.837800 0.540400 0.077800 +vn -0.831000 0.551000 -0.075900 +vn -0.570000 0.740800 0.355300 +vn -0.711000 0.638600 0.294400 +vn -0.841600 0.484400 0.238700 +vn -0.695500 0.714900 -0.072200 +vn -0.673700 0.709600 -0.206100 +vn -0.521000 0.836700 -0.168800 +vn -0.825600 0.526000 -0.203800 +vn -0.420300 0.897500 -0.133900 +vn -0.456400 0.889700 0.007200 +vn -0.387600 0.913900 -0.120200 +vn -0.486700 0.851400 0.195600 +vn -0.496800 0.772200 0.396000 +vn -0.432700 0.901200 0.026300 +vn -0.486200 0.847000 0.214800 +vn -0.530100 0.744900 0.405100 +vn 0.556800 0.638800 0.530900 +vn 0.516200 0.651800 0.555500 +vn 0.499300 0.690300 0.523600 +vn 0.554300 0.671300 0.491900 +vn 0.578600 0.606900 0.544900 +vn 0.555600 0.651800 0.516100 +vn 0.693800 0.578200 0.429200 +vn 0.686600 0.551400 0.473700 +vn 0.834600 0.424400 0.351100 +vn 0.828300 0.400700 0.391600 +vn 0.699100 0.552900 0.453400 +vn 0.582500 0.637300 0.504300 +vn 0.732500 0.572200 0.368800 +vn 0.833500 0.405800 0.375000 +vn 0.852300 0.428800 0.299500 +vn 0.549300 0.650500 0.524500 +vn 0.607500 0.607200 0.512100 +vn 0.629500 0.658500 0.412300 +vn 0.596100 0.678700 0.429000 +vn 0.639600 0.643200 0.420900 +vn -0.556800 0.638800 0.530900 +vn -0.582500 0.637300 0.504300 +vn -0.699100 0.552900 0.453400 +vn -0.686600 0.551400 0.473700 +vn -0.833500 0.405800 0.375000 +vn -0.629500 0.658500 0.412300 +vn -0.732500 0.572200 0.368800 +vn -0.852300 0.428800 0.299500 +vn -0.693800 0.578200 0.429200 +vn -0.554300 0.671300 0.491900 +vn -0.828300 0.400700 0.391600 +vn -0.834600 0.424400 0.351100 +vn -0.499300 0.690300 0.523600 +vn -0.516200 0.651800 0.555500 +vn -0.555600 0.651800 0.516100 +vn -0.549300 0.650500 0.524500 +vn -0.596100 0.678700 0.429000 +vn -0.578600 0.606900 0.544900 +vn -0.607500 0.607200 0.512100 +vn -0.639600 0.643200 0.420900 +vn 0.708400 0.686200 0.165100 +vn 0.662300 0.728300 0.176200 +vn 0.636600 0.710700 0.299400 +vn 0.676000 0.679400 0.285100 +vn 0.669900 0.721000 0.177300 +vn 0.661100 0.689500 0.295800 +vn 0.769700 0.587600 0.249500 +vn 0.796900 0.587800 0.138800 +vn 0.873900 0.446500 0.192000 +vn 0.890100 0.445900 0.094200 +vn 0.815900 0.576700 0.042100 +vn 0.729800 0.681200 0.057800 +vn 0.827500 0.560400 -0.035500 +vn 0.901700 0.432100 0.012400 +vn 0.911500 0.408400 -0.047500 +vn 0.677400 0.732700 0.064500 +vn 0.671700 0.737600 0.068200 +vn 0.743800 0.667500 -0.033900 +vn 0.687200 0.725800 -0.031000 +vn 0.670100 0.741800 -0.026300 +vn -0.708400 0.686200 0.165100 +vn -0.729800 0.681200 0.057800 +vn -0.815900 0.576700 0.042100 +vn -0.796900 0.587800 0.138800 +vn -0.901700 0.432100 0.012400 +vn -0.743800 0.667500 -0.033900 +vn -0.827500 0.560400 -0.035500 +vn -0.911500 0.408400 -0.047500 +vn -0.769700 0.587600 0.249500 +vn -0.676000 0.679400 0.285100 +vn -0.890100 0.445900 0.094200 +vn -0.873900 0.446500 0.192000 +vn -0.636600 0.710700 0.299400 +vn -0.662300 0.728300 0.176200 +vn -0.661100 0.689500 0.295800 +vn -0.677400 0.732700 0.064500 +vn -0.687200 0.725800 -0.031000 +vn -0.669900 0.721000 0.177300 +vn -0.671700 0.737600 0.068200 +vn -0.670100 0.741800 -0.026300 +vn 0.690400 0.673600 -0.263800 +vn 0.693100 0.708400 -0.133300 +vn 0.754100 0.644500 -0.126100 +vn 0.650200 0.710300 -0.269600 +vn 0.664900 0.735000 -0.132700 +vn 0.835300 0.537200 -0.116800 +vn 0.837200 0.502100 -0.216600 +vn 0.918500 0.379800 -0.109600 +vn 0.828300 0.447400 -0.337200 +vn 0.747300 0.543400 -0.382300 +vn 0.814500 0.362100 -0.453300 +vn 0.730200 0.441900 -0.521100 +vn 0.915400 0.352200 -0.194800 +vn 0.897600 0.321700 -0.301300 +vn 0.872400 0.293400 -0.390800 +vn 0.756100 0.607500 -0.243400 +vn 0.675800 0.609500 -0.414300 +vn 0.622200 0.655100 -0.428600 +vn 0.650500 0.507100 -0.565400 +vn 0.586500 0.559600 -0.585400 +vn -0.747300 0.543400 -0.382300 +vn -0.828300 0.447400 -0.337200 +vn -0.837200 0.502100 -0.216600 +vn -0.897600 0.321700 -0.301300 +vn -0.915400 0.352200 -0.194800 +vn -0.730200 0.441900 -0.521100 +vn -0.814500 0.362100 -0.453300 +vn -0.872400 0.293400 -0.390800 +vn -0.835300 0.537200 -0.116800 +vn -0.754100 0.644500 -0.126100 +vn -0.918500 0.379800 -0.109600 +vn -0.693100 0.708400 -0.133300 +vn -0.690400 0.673600 -0.263800 +vn -0.664900 0.735000 -0.132700 +vn -0.650200 0.710300 -0.269600 +vn -0.756100 0.607500 -0.243400 +vn -0.675800 0.609500 -0.414300 +vn -0.650500 0.507100 -0.565400 +vn -0.622200 0.655100 -0.428600 +vn -0.586500 0.559600 -0.585400 +vn 0.779900 0.171900 -0.601800 +vn 0.743700 0.087200 -0.662700 +vn 0.675900 0.087800 -0.731700 +vn 0.680300 -0.038800 -0.731900 +vn 0.874000 0.157900 -0.459500 +vn 0.840700 0.119100 -0.528200 +vn 0.727700 0.057600 -0.683500 +vn 0.697200 0.201300 -0.688000 +vn 0.622500 0.121300 -0.773200 +vn 0.624300 0.251900 -0.739400 +vn 0.580800 0.171800 -0.795700 +vn 0.643600 -0.042900 -0.764100 +vn 0.611000 -0.020700 -0.791300 +vn 0.582700 0.018000 -0.812400 +vn 0.630200 0.380300 -0.676900 +vn 0.711800 0.320600 -0.624800 +vn 0.568400 0.308500 -0.762700 +vn 0.565600 0.437000 -0.699300 +vn 0.799300 0.267500 -0.538000 +vn 0.879200 0.233700 -0.415100 +vn -0.711800 0.320600 -0.624800 +vn -0.630200 0.380300 -0.676900 +vn -0.624300 0.251900 -0.739400 +vn -0.565600 0.437000 -0.699300 +vn -0.568400 0.308500 -0.762700 +vn -0.697200 0.201300 -0.688000 +vn -0.622500 0.121300 -0.773200 +vn -0.675900 0.087800 -0.731700 +vn -0.611000 -0.020700 -0.791300 +vn -0.580800 0.171800 -0.795700 +vn -0.582700 0.018000 -0.812400 +vn -0.743700 0.087200 -0.662700 +vn -0.779900 0.171900 -0.601800 +vn -0.840700 0.119100 -0.528200 +vn -0.643600 -0.042900 -0.764100 +vn -0.680300 -0.038800 -0.731900 +vn -0.727700 0.057600 -0.683500 +vn -0.799300 0.267500 -0.538000 +vn -0.874000 0.157900 -0.459500 +vn -0.879200 0.233700 -0.415100 +vn 0.477300 0.426100 -0.768500 +vn 0.501800 0.289100 -0.815200 +vn 0.460600 0.343100 -0.818600 +vn 0.437500 0.477400 -0.762000 +vn 0.513400 0.122700 -0.849300 +vn 0.470200 0.175700 -0.864900 +vn 0.415000 0.596400 -0.687100 +vn 0.460000 0.548600 -0.698200 +vn 0.407400 0.710800 -0.573400 +vn 0.468300 0.663800 -0.583100 +vn 0.510300 0.494500 -0.703600 +vn 0.521300 0.368300 -0.769800 +vn 0.528900 0.612300 -0.587700 +vn 0.543000 0.229700 -0.807700 +vn 0.552700 0.067500 -0.830700 +vn -0.477300 0.426100 -0.768500 +vn -0.521300 0.368300 -0.769800 +vn -0.510300 0.494500 -0.703600 +vn -0.460000 0.548600 -0.698200 +vn -0.528900 0.612300 -0.587700 +vn -0.415000 0.596400 -0.687100 +vn -0.437500 0.477400 -0.762000 +vn -0.468300 0.663800 -0.583100 +vn -0.407400 0.710800 -0.573400 +vn -0.460600 0.343100 -0.818600 +vn -0.501800 0.289100 -0.815200 +vn -0.470200 0.175700 -0.864900 +vn -0.543000 0.229700 -0.807700 +vn -0.513400 0.122700 -0.849300 +vn -0.552700 0.067500 -0.830700 +vn 0.534600 0.803000 -0.263300 +vn 0.499700 0.753500 -0.427100 +vn 0.417700 0.804800 -0.421600 +vn 0.436400 0.861400 -0.259600 +vn 0.456700 0.882500 -0.112400 +vn 0.562800 0.818100 -0.118200 +vn 0.474000 0.880500 0.004200 +vn 0.581600 0.813400 -0.006400 +vn 0.634100 0.763100 -0.124500 +vn 0.607900 0.748300 -0.265600 +vn 0.648700 0.760900 -0.017100 +vn 0.569700 0.701100 -0.428900 +vn -0.534600 0.803000 -0.263300 +vn -0.607900 0.748300 -0.265600 +vn -0.634100 0.763100 -0.124500 +vn -0.562800 0.818100 -0.118200 +vn -0.648700 0.760900 -0.017100 +vn -0.456700 0.882500 -0.112400 +vn -0.436400 0.861400 -0.259600 +vn -0.581600 0.813400 -0.006400 +vn -0.474000 0.880500 0.004200 +vn -0.417700 0.804800 -0.421600 +vn -0.499700 0.753500 -0.427100 +vn -0.569700 0.701100 -0.428900 +vn 0.614500 0.760900 0.208100 +vn 0.597500 0.796200 0.094600 +vn 0.491600 0.863500 0.112800 +vn 0.512100 0.825900 0.236000 +vn 0.531200 0.763600 0.366900 +vn 0.628200 0.705400 0.328200 +vn 0.544300 0.677400 0.494900 +vn 0.633900 0.631200 0.446900 +vn 0.673900 0.674400 0.301700 +vn 0.668900 0.719500 0.186600 +vn 0.671400 0.611600 0.418500 +vn 0.659100 0.747900 0.078700 +vn -0.614500 0.760900 0.208100 +vn -0.668900 0.719500 0.186600 +vn -0.673900 0.674400 0.301700 +vn -0.628200 0.705400 0.328200 +vn -0.671400 0.611600 0.418500 +vn -0.531200 0.763600 0.366900 +vn -0.512100 0.825900 0.236000 +vn -0.633900 0.631200 0.446900 +vn -0.544300 0.677400 0.494900 +vn -0.491600 0.863500 0.112800 +vn -0.597500 0.796200 0.094600 +vn -0.659100 0.747900 0.078700 +vn 0.610700 0.556600 0.563200 +vn 0.627400 0.569800 0.530700 +vn 0.545900 0.601800 0.582900 +vn 0.536100 0.576000 0.617100 +vn 0.421100 0.645100 0.637500 +vn 0.513100 0.606800 0.607000 +vn 0.582600 0.598000 0.550400 +vn 0.462700 0.720800 0.516100 +vn 0.530200 0.715400 0.454900 +vn 0.422400 0.605400 0.674600 +vn 0.410200 0.620800 0.668100 +vn 0.369500 0.719900 0.587500 +vn 0.607700 0.607200 0.511800 +vn 0.558900 0.717800 0.415200 +vn 0.638100 0.557300 0.531200 +vn 0.658300 0.562400 0.500300 +vn -0.610700 0.556600 0.563200 +vn -0.638100 0.557300 0.531200 +vn -0.607700 0.607200 0.511800 +vn -0.582600 0.598000 0.550400 +vn -0.558900 0.717800 0.415200 +vn -0.513100 0.606800 0.607000 +vn -0.536100 0.576000 0.617100 +vn -0.410200 0.620800 0.668100 +vn -0.422400 0.605400 0.674600 +vn -0.530200 0.715400 0.454900 +vn -0.462700 0.720800 0.516100 +vn -0.369500 0.719900 0.587500 +vn -0.545900 0.601800 0.582900 +vn -0.627400 0.569800 0.530700 +vn -0.421100 0.645100 0.637500 +vn -0.658300 0.562400 0.500300 +vn 0.262100 0.964200 0.038400 +vn 0.192600 0.958200 -0.211400 +vn 0.302100 0.941000 -0.152500 +vn 0.148600 0.986300 0.071200 +vn 0.043800 0.949300 -0.311200 +vn 0.354000 0.934400 0.040300 +vn 0.365100 0.922400 -0.125900 +vn 0.414100 0.909600 0.033800 +vn 0.487800 0.841800 0.231000 +vn 0.444000 0.855700 0.265800 +vn 0.370600 0.873300 0.316100 +vn 0.281100 0.876600 0.390600 +vn -0.444000 0.855700 0.265800 +vn -0.487800 0.841800 0.231000 +vn -0.414100 0.909600 0.033800 +vn -0.354000 0.934400 0.040300 +vn -0.365100 0.922400 -0.125900 +vn -0.302100 0.941000 -0.152500 +vn -0.192600 0.958200 -0.211400 +vn -0.262100 0.964200 0.038400 +vn -0.043800 0.949300 -0.311200 +vn -0.148600 0.986300 0.071200 +vn -0.370600 0.873300 0.316100 +vn -0.281100 0.876600 0.390600 +vn 0.108800 0.678200 0.726800 +vn 0.120500 0.657700 0.743600 +vn 0.215000 0.642600 0.735400 +vn 0.128700 0.670600 0.730500 +vn 0.205800 0.679200 0.704500 +vn 0.000000 0.688600 0.725200 +vn 0.000000 0.673500 0.739100 +vn 0.000000 0.646800 0.762700 +vn 0.205100 0.656600 0.725800 +vn 0.304700 0.631700 0.712800 +vn 0.304900 0.633100 0.711400 +vn 0.273400 0.700900 0.658700 +vn 0.293400 0.683300 0.668600 +vn 0.188900 0.711200 0.677100 +vn 0.095100 0.731100 0.675500 +vn 0.000000 0.739400 0.673200 +vn -0.188900 0.711200 0.677100 +vn -0.293400 0.683300 0.668600 +vn -0.304900 0.633100 0.711400 +vn -0.205100 0.656600 0.725800 +vn -0.304700 0.631700 0.712800 +vn -0.215000 0.642600 0.735400 +vn -0.273400 0.700900 0.658700 +vn -0.120500 0.657700 0.743600 +vn -0.108800 0.678200 0.726800 +vn -0.205800 0.679200 0.704500 +vn -0.128700 0.670600 0.730500 +vn -0.095100 0.731100 0.675500 +vn -0.219800 0.375800 -0.900200 +vn -0.335800 0.398500 -0.853500 +vn -0.341500 0.310800 -0.887000 +vn -0.233500 0.693100 -0.681900 +vn -0.328700 0.765500 -0.553100 +vn -0.207300 0.300800 -0.930800 +vn -0.363400 0.347300 -0.864400 +vn -0.215100 0.359900 -0.907800 +vn -0.062000 0.434500 -0.898500 +vn -0.075800 0.378400 -0.922500 +vn -0.106600 0.442600 -0.890300 +vn -0.128500 0.693000 -0.709300 +vn 0.207300 0.300800 -0.930800 +vn 0.075800 0.378400 -0.922500 +vn 0.062000 0.434500 -0.898500 +vn 0.215100 0.359900 -0.907800 +vn 0.363400 0.347300 -0.864400 +vn 0.341500 0.310800 -0.887000 +vn 0.335800 0.398500 -0.853500 +vn 0.219800 0.375800 -0.900200 +vn 0.328700 0.765500 -0.553100 +vn 0.233500 0.693100 -0.681900 +vn 0.106600 0.442600 -0.890300 +vn 0.128500 0.693000 -0.709300 +vn 0.042600 0.919500 0.390800 +vn 0.031800 0.816900 0.575800 +vn -0.160500 0.974500 0.156700 +vn -0.134200 0.984300 -0.114500 +vn -0.004500 0.700500 0.713600 +vn -0.165900 0.904500 0.392900 +vn -0.080100 0.954600 -0.286800 +vn 0.057600 0.977800 0.201700 +vn 0.188900 0.840800 0.507300 +vn 0.155500 0.777500 0.609300 +vn 0.110300 0.711800 0.693700 +vn 0.051900 0.637200 0.768900 +vn -0.042600 0.919500 0.390800 +vn -0.155500 0.777500 0.609300 +vn -0.188900 0.840800 0.507300 +vn -0.057600 0.977800 0.201700 +vn 0.080100 0.954600 -0.286800 +vn 0.134200 0.984300 -0.114500 +vn 0.160500 0.974500 0.156700 +vn -0.031800 0.816900 0.575800 +vn 0.165900 0.904500 0.392900 +vn -0.110300 0.711800 0.693700 +vn 0.004500 0.700500 0.713600 +vn -0.051900 0.637200 0.768900 +vn -0.102800 0.558700 0.823000 +vn -0.046400 0.518800 0.853600 +vn -0.104500 0.666900 0.737700 +vn 0.000000 0.500700 0.865600 +vn 0.000000 0.639400 0.768900 +vn -0.199100 0.707400 0.678200 +vn -0.226300 0.768000 0.599100 +vn -0.060900 0.602900 0.795400 +vn -0.007000 0.544000 0.839000 +vn 0.000000 0.535700 0.844400 +vn 0.102800 0.558700 0.823000 +vn 0.046400 0.518800 0.853600 +vn 0.007000 0.544000 0.839000 +vn 0.060900 0.602900 0.795400 +vn 0.226300 0.768000 0.599100 +vn 0.199100 0.707400 0.678200 +vn 0.104500 0.666900 0.737700 +vn 0.934600 0.354600 -0.025700 +vn 0.939600 0.313000 -0.138400 +vn 0.994700 0.089300 -0.050400 +vn 0.989000 0.142400 0.038800 +vn 0.992200 -0.122300 0.022800 +vn 0.993900 -0.063400 0.090000 +vn 0.990600 -0.013200 0.135800 +vn 0.977800 0.173800 0.116600 +vn 0.986200 0.014100 0.165000 +vn 0.967900 0.168600 0.186100 +vn 0.927900 0.361300 0.091500 +vn 0.923900 0.322100 0.206600 +vn -0.977800 0.173800 0.116600 +vn -0.990600 -0.013200 0.135800 +vn -0.993900 -0.063400 0.090000 +vn -0.967900 0.168600 0.186100 +vn -0.986200 0.014100 0.165000 +vn -0.989000 0.142400 0.038800 +vn -0.992200 -0.122300 0.022800 +vn -0.994700 0.089300 -0.050400 +vn -0.939600 0.313000 -0.138400 +vn -0.934600 0.354600 -0.025700 +vn -0.927900 0.361300 0.091500 +vn -0.923900 0.322100 0.206600 +vn 0.961900 0.139400 0.234900 +vn 0.961900 0.149300 0.229100 +vn 0.984600 0.024900 0.172800 +vn 0.986600 0.029000 0.160700 +vn 0.987200 -0.115400 0.109600 +vn 0.991400 0.036900 0.125600 +vn 0.967900 0.146100 0.204400 +vn 0.994800 0.064800 0.078600 +vn 0.975600 0.171000 0.137500 +vn 0.991300 -0.107600 0.074900 +vn 0.992700 -0.110800 0.046400 +vn 0.992800 -0.115400 0.031400 +vn 0.923200 0.257200 0.285500 +vn 0.916900 0.252900 0.308800 +vn 0.936000 0.280300 0.212700 +vn 0.918500 0.274100 0.285000 +vn -0.961900 0.139400 0.234900 +vn -0.916900 0.252900 0.308800 +vn -0.923200 0.257200 0.285500 +vn -0.967900 0.146100 0.204400 +vn -0.936000 0.280300 0.212700 +vn -0.991400 0.036900 0.125600 +vn -0.986600 0.029000 0.160700 +vn -0.992700 -0.110800 0.046400 +vn -0.991300 -0.107600 0.074900 +vn -0.975600 0.171000 0.137500 +vn -0.994800 0.064800 0.078600 +vn -0.992800 -0.115400 0.031400 +vn -0.984600 0.024900 0.172800 +vn -0.961900 0.149300 0.229100 +vn -0.987200 -0.115400 0.109600 +vn -0.918500 0.274100 0.285000 +vn 0.976400 0.215500 -0.011000 +vn 0.978200 0.199200 0.057700 +vn 0.993000 0.116200 0.018900 +vn 0.988200 0.149600 -0.031600 +vn 0.999900 0.012300 0.001000 +vn 0.981000 0.177600 -0.077400 +vn 0.973600 0.218400 -0.065400 +vn 0.971900 0.208600 -0.109200 +vn 0.972500 0.209300 -0.101700 +vn 0.997100 0.072500 -0.021300 +vn 0.987000 0.160100 -0.012300 +vn 0.878800 0.474000 0.053600 +vn 0.956000 0.291800 -0.030800 +vn 0.952200 0.303400 0.034200 +vn 0.959700 0.270900 -0.074900 +vn 0.946500 0.300700 0.117100 +vn -0.976400 0.215500 -0.011000 +vn -0.952200 0.303400 0.034200 +vn -0.956000 0.291800 -0.030800 +vn -0.973600 0.218400 -0.065400 +vn -0.959700 0.270900 -0.074900 +vn -0.981000 0.177600 -0.077400 +vn -0.988200 0.149600 -0.031600 +vn -0.987000 0.160100 -0.012300 +vn -0.997100 0.072500 -0.021300 +vn -0.972500 0.209300 -0.101700 +vn -0.971900 0.208600 -0.109200 +vn -0.878800 0.474000 0.053600 +vn -0.993000 0.116200 0.018900 +vn -0.978200 0.199200 0.057700 +vn -0.999900 0.012300 0.001000 +vn -0.946500 0.300700 0.117100 +vn 0.939300 0.215300 -0.267200 +vn 0.906000 0.175200 -0.385300 +vn 0.933000 0.161400 -0.321500 +vn 0.764300 0.528400 -0.369500 +vn 0.764400 0.330200 -0.553800 +vn 0.960300 0.177200 -0.215500 +vn 0.929700 0.220400 -0.295000 +vn 0.954400 0.227900 -0.192700 +vn 0.961900 0.246600 -0.118300 +vn 0.970700 0.193900 -0.141900 +vn 0.958600 0.229200 -0.169000 +vn 0.790500 0.583800 -0.185000 +vn -0.970700 0.193900 -0.141900 +vn -0.961900 0.246600 -0.118300 +vn -0.954400 0.227900 -0.192700 +vn -0.960300 0.177200 -0.215500 +vn -0.929700 0.220400 -0.295000 +vn -0.933000 0.161400 -0.321500 +vn -0.906000 0.175200 -0.385300 +vn -0.939300 0.215300 -0.267200 +vn -0.764400 0.330200 -0.553800 +vn -0.764300 0.528400 -0.369500 +vn -0.958600 0.229200 -0.169000 +vn -0.790500 0.583800 -0.185000 +vn 0.658900 -0.750300 0.052800 +vn 0.628700 -0.777500 0.015000 +vn 0.593300 -0.804100 -0.035600 +vn 0.687000 -0.719200 0.103600 +vn 0.675700 -0.734300 0.065100 +vn 0.629100 -0.777300 -0.000800 +vn 0.568600 -0.817000 -0.095800 +vn 0.610600 -0.789000 -0.068700 +vn 0.558500 -0.810400 -0.176900 +vn 0.602900 -0.795500 -0.060200 +vn 0.613300 -0.789600 0.018000 +vn 0.541300 -0.838200 -0.065700 +vn 0.545500 -0.837800 0.022800 +vn 0.606300 -0.780000 -0.154700 +vn 0.604400 -0.781600 -0.153700 +vn 0.544900 -0.820900 -0.170700 +vn 0.630200 -0.772900 0.073700 +vn 0.653900 -0.744100 0.136700 +vn 0.558600 -0.824500 0.089700 +vn 0.571800 -0.805400 0.155600 +vn -0.613300 -0.789600 0.018000 +vn -0.602900 -0.795500 -0.060200 +vn -0.610600 -0.789000 -0.068700 +vn -0.604400 -0.781600 -0.153700 +vn -0.606300 -0.780000 -0.154700 +vn -0.545500 -0.837800 0.022800 +vn -0.541300 -0.838200 -0.065700 +vn -0.544900 -0.820900 -0.170700 +vn -0.629100 -0.777300 -0.000800 +vn -0.568600 -0.817000 -0.095800 +vn -0.593300 -0.804100 -0.035600 +vn -0.558500 -0.810400 -0.176900 +vn -0.628700 -0.777500 0.015000 +vn -0.658900 -0.750300 0.052800 +vn -0.675700 -0.734300 0.065100 +vn -0.687000 -0.719200 0.103600 +vn -0.630200 -0.772900 0.073700 +vn -0.558600 -0.824500 0.089700 +vn -0.653900 -0.744100 0.136700 +vn -0.571800 -0.805400 0.155600 +vn 0.437500 -0.895800 0.078200 +vn 0.482600 -0.866000 0.131200 +vn 0.459600 -0.881100 0.111100 +vn 0.529100 -0.829600 0.178000 +vn 0.505800 -0.856600 0.101500 +vn 0.587400 -0.798500 0.131900 +vn 0.483000 -0.870300 0.096000 +vn 0.469600 -0.882400 0.026900 +vn 0.483200 -0.861400 0.156600 +vn 0.494300 -0.856900 0.146100 +vn 0.458100 -0.886200 -0.068800 +vn 0.394500 -0.917500 -0.050500 +vn 0.451200 -0.873300 -0.184000 +vn 0.362900 -0.915500 -0.173500 +vn 0.428900 -0.902200 0.045400 +vn 0.362300 -0.932000 0.008200 +vn 0.413700 -0.904600 0.102300 +vn 0.266900 -0.954500 -0.132500 +vn 0.185000 -0.982500 0.018300 +vn -0.394500 -0.917500 -0.050500 +vn -0.458100 -0.886200 -0.068800 +vn -0.469600 -0.882400 0.026900 +vn -0.362900 -0.915500 -0.173500 +vn -0.451200 -0.873300 -0.184000 +vn -0.483000 -0.870300 0.096000 +vn -0.459600 -0.881100 0.111100 +vn -0.494300 -0.856900 0.146100 +vn -0.483200 -0.861400 0.156600 +vn -0.482600 -0.866000 0.131200 +vn -0.437500 -0.895800 0.078200 +vn -0.587400 -0.798500 0.131900 +vn -0.505800 -0.856600 0.101500 +vn -0.529100 -0.829600 0.178000 +vn -0.428900 -0.902200 0.045400 +vn -0.362300 -0.932000 0.008200 +vn -0.266900 -0.954500 -0.132500 +vn -0.413700 -0.904600 0.102300 +vn -0.185000 -0.982500 0.018300 +vn 0.511800 -0.838000 0.189200 +vn 0.567200 -0.798200 0.202800 +vn 0.569800 -0.804200 0.168900 +vn 0.511500 -0.844000 0.161300 +vn 0.482400 -0.860400 0.164200 +vn 0.493500 -0.849300 0.187400 +vn 0.489500 -0.850600 0.192000 +vn 0.516300 -0.833800 0.195500 +vn 0.496700 -0.848800 0.181100 +vn 0.500700 -0.845400 0.186000 +vn 0.537500 -0.822200 0.187400 +vn 0.556700 -0.805800 0.201600 +vn -0.511800 -0.838000 0.189200 +vn -0.500700 -0.845400 0.186000 +vn -0.496700 -0.848800 0.181100 +vn -0.493500 -0.849300 0.187400 +vn -0.537500 -0.822200 0.187400 +vn -0.482400 -0.860400 0.164200 +vn -0.511500 -0.844000 0.161300 +vn -0.516300 -0.833800 0.195500 +vn -0.489500 -0.850600 0.192000 +vn -0.569800 -0.804200 0.168900 +vn -0.567200 -0.798200 0.202800 +vn -0.556700 -0.805800 0.201600 +vn 0.629900 -0.728300 0.269800 +vn 0.566000 -0.794600 0.219600 +vn 0.524200 -0.815200 0.246300 +vn 0.582200 -0.747000 0.320900 +vn 0.469100 -0.826100 0.312200 +vn 0.669600 -0.618400 0.411300 +vn 0.722000 -0.607700 0.330700 +vn 0.527400 -0.737800 0.421200 +vn 0.603200 -0.590800 0.535800 +vn 0.760800 -0.593200 0.263200 +vn 0.669900 -0.703500 0.236900 +vn 0.770700 -0.602700 0.206900 +vn 0.600700 -0.771500 0.209300 +vn 0.700600 -0.680000 0.216300 +vn 0.644000 -0.741300 0.189100 +vn -0.629900 -0.728300 0.269800 +vn -0.669900 -0.703500 0.236900 +vn -0.760800 -0.593200 0.263200 +vn -0.722000 -0.607700 0.330700 +vn -0.700600 -0.680000 0.216300 +vn -0.770700 -0.602700 0.206900 +vn -0.669600 -0.618400 0.411300 +vn -0.582200 -0.747000 0.320900 +vn -0.603200 -0.590800 0.535800 +vn -0.527400 -0.737800 0.421200 +vn -0.524200 -0.815200 0.246300 +vn -0.566000 -0.794600 0.219600 +vn -0.469100 -0.826100 0.312200 +vn -0.600700 -0.771500 0.209300 +vn -0.644000 -0.741300 0.189100 +vn 0.720300 -0.665100 0.196900 +vn 0.763500 -0.619600 0.181700 +vn 0.748300 -0.644500 0.157000 +vn 0.725100 -0.666100 0.174600 +vn 0.717200 -0.686700 0.118300 +vn 0.710000 -0.689100 0.144800 +vn 0.697000 -0.697100 0.167800 +vn -0.725100 -0.666100 0.174600 +vn -0.720300 -0.665100 0.196900 +vn -0.697000 -0.697100 0.167800 +vn -0.710000 -0.689100 0.144800 +vn -0.717200 -0.686700 0.118300 +vn -0.748300 -0.644500 0.157000 +vn -0.763500 -0.619600 0.181700 +vn 0.479800 -0.705400 0.521700 +vn 0.428000 -0.823000 0.373400 +vn 0.426300 -0.822500 0.376500 +vn 0.464500 -0.706600 0.533800 +vn 0.479500 -0.802500 0.355100 +vn 0.522200 -0.643000 0.560200 +vn 0.548200 -0.552600 0.627800 +vn -0.464500 -0.706600 0.533800 +vn -0.479800 -0.705400 0.521700 +vn -0.548200 -0.552600 0.627800 +vn -0.522200 -0.643000 0.560200 +vn -0.479500 -0.802500 0.355100 +vn -0.426300 -0.822500 0.376500 +vn -0.428000 -0.823000 0.373400 +vn 0.600400 -0.178800 -0.779400 +vn 0.618400 -0.169400 -0.767400 +vn 0.551300 -0.305000 -0.776500 +vn 0.622300 -0.136300 -0.770800 +vn 0.551200 -0.278100 -0.786600 +vn 0.544600 -0.317000 -0.776500 +vn 0.463700 -0.459800 -0.757300 +vn 0.362400 -0.619300 -0.696400 +vn 0.421900 -0.621200 -0.660400 +vn 0.447700 -0.437500 -0.779800 +vn 0.313600 -0.604100 -0.732600 +vn 0.487500 -0.464100 -0.739600 +vn 0.505000 -0.460400 -0.730100 +vn 0.546900 -0.308700 -0.778200 +vn 0.545300 -0.419500 -0.725600 +vn 0.489500 -0.599700 -0.633000 +vn 0.588000 -0.547000 -0.595800 +vn 0.582200 -0.167000 -0.795700 +vn 0.545400 -0.281000 -0.789600 +vn 0.565100 -0.137300 -0.813500 +vn -0.546900 -0.308700 -0.778200 +vn -0.505000 -0.460400 -0.730100 +vn -0.487500 -0.464100 -0.739600 +vn -0.489500 -0.599700 -0.633000 +vn -0.545400 -0.281000 -0.789600 +vn -0.545300 -0.419500 -0.725600 +vn -0.588000 -0.547000 -0.595800 +vn -0.544600 -0.317000 -0.776500 +vn -0.463700 -0.459800 -0.757300 +vn -0.551300 -0.305000 -0.776500 +vn -0.447700 -0.437500 -0.779800 +vn -0.421900 -0.621200 -0.660400 +vn -0.362400 -0.619300 -0.696400 +vn -0.313600 -0.604100 -0.732600 +vn -0.618400 -0.169400 -0.767400 +vn -0.600400 -0.178800 -0.779400 +vn -0.551200 -0.278100 -0.786600 +vn -0.622300 -0.136300 -0.770800 +vn -0.582200 -0.167000 -0.795700 +vn -0.565100 -0.137300 -0.813500 +vn 0.457300 -0.140500 -0.878100 +vn 0.464100 0.000900 -0.885800 +vn 0.506700 -0.046200 -0.860900 +vn 0.503000 -0.185300 -0.844100 +vn 0.542700 -0.095200 -0.834500 +vn 0.536500 -0.237800 -0.809700 +vn 0.547900 -0.347600 -0.760900 +vn 0.512000 -0.290500 -0.808300 +vn 0.587200 -0.425200 -0.688800 +vn 0.530500 -0.352800 -0.770800 +vn 0.453200 -0.247800 -0.856200 +vn 0.456000 -0.311900 -0.833500 +vn -0.512000 -0.290500 -0.808300 +vn -0.547900 -0.347600 -0.760900 +vn -0.536500 -0.237800 -0.809700 +vn -0.530500 -0.352800 -0.770800 +vn -0.587200 -0.425200 -0.688800 +vn -0.503000 -0.185300 -0.844100 +vn -0.542700 -0.095200 -0.834500 +vn -0.506700 -0.046200 -0.860900 +vn -0.464100 0.000900 -0.885800 +vn -0.457300 -0.140500 -0.878100 +vn -0.453200 -0.247800 -0.856200 +vn -0.456000 -0.311900 -0.833500 +vn 0.215500 -0.870100 -0.443300 +vn 0.217200 -0.931600 -0.291400 +vn 0.339300 -0.889600 -0.305600 +vn 0.087300 -0.891100 -0.445300 +vn 0.068400 -0.965300 -0.252100 +vn 0.331900 -0.837700 -0.433600 +vn 0.443000 -0.844500 -0.301000 +vn 0.548400 -0.789900 -0.274300 +vn 0.553100 -0.746200 -0.370400 +vn 0.439000 -0.798200 -0.412500 +vn 0.454400 -0.723600 -0.519500 +vn 0.359100 -0.751500 -0.553400 +vn 0.565100 -0.682300 -0.463700 +vn 0.265000 -0.767500 -0.583600 +vn 0.174600 -0.772800 -0.610200 +vn -0.359100 -0.751500 -0.553400 +vn -0.454400 -0.723600 -0.519500 +vn -0.439000 -0.798200 -0.412500 +vn -0.565100 -0.682300 -0.463700 +vn -0.331900 -0.837700 -0.433600 +vn -0.443000 -0.844500 -0.301000 +vn -0.339300 -0.889600 -0.305600 +vn -0.553100 -0.746200 -0.370400 +vn -0.548400 -0.789900 -0.274300 +vn -0.217200 -0.931600 -0.291400 +vn -0.215500 -0.870100 -0.443300 +vn -0.068400 -0.965300 -0.252100 +vn -0.087300 -0.891100 -0.445300 +vn -0.265000 -0.767500 -0.583600 +vn -0.174600 -0.772800 -0.610200 +vn 0.608900 -0.754800 -0.243800 +vn 0.557800 -0.786200 -0.266000 +vn 0.554600 -0.755200 -0.349300 +vn 0.612600 -0.721300 -0.323100 +vn 0.542200 -0.719800 -0.433500 +vn 0.612700 -0.682100 -0.399200 +vn 0.516100 -0.676700 -0.525000 +vn 0.628600 -0.663200 -0.406100 +vn 0.619100 -0.712800 -0.329700 +vn 0.599800 -0.636900 -0.484400 +vn 0.634100 -0.594300 -0.494800 +vn 0.609600 -0.753200 -0.247000 +vn -0.619100 -0.712800 -0.329700 +vn -0.628600 -0.663200 -0.406100 +vn -0.612700 -0.682100 -0.399200 +vn -0.634100 -0.594300 -0.494800 +vn -0.599800 -0.636900 -0.484400 +vn -0.612600 -0.721300 -0.323100 +vn -0.542200 -0.719800 -0.433500 +vn -0.554600 -0.755200 -0.349300 +vn -0.516100 -0.676700 -0.525000 +vn -0.557800 -0.786200 -0.266000 +vn -0.608900 -0.754800 -0.243800 +vn -0.609600 -0.753200 -0.247000 +vn 0.523600 -0.487100 -0.698900 +vn 0.461600 -0.492400 -0.737800 +vn 0.454000 -0.386100 -0.803000 +vn 0.535300 -0.421800 -0.731800 +vn 0.605600 -0.496400 -0.621900 +vn 0.565800 -0.565300 -0.600200 +vn 0.480800 -0.611800 -0.628100 +vn -0.523600 -0.487100 -0.698900 +vn -0.461600 -0.492400 -0.737800 +vn -0.480800 -0.611800 -0.628100 +vn -0.565800 -0.565300 -0.600200 +vn -0.605600 -0.496400 -0.621900 +vn -0.535300 -0.421800 -0.731800 +vn -0.454000 -0.386100 -0.803000 +vn 0.288500 0.507300 0.812000 +vn -0.027200 0.855600 0.516900 +vn -0.108600 0.844900 0.523700 +vn -0.256100 0.951700 0.169500 +vn -0.327200 0.924700 0.194600 +vn 0.338800 0.464500 0.818200 +vn 0.049300 0.850100 0.524300 +vn -0.186500 0.970000 0.155700 +vn 0.221600 0.533400 0.816200 +vn -0.196500 0.818000 0.540500 +vn 0.141400 0.535500 0.832600 +vn -0.294300 0.774400 0.560000 +vn -0.404400 0.886700 0.223900 +vn -0.492000 0.834400 0.248500 +vn 0.435500 0.132700 0.890300 +vn 0.482800 0.113300 0.868300 +vn 0.580200 -0.157400 0.799000 +vn 0.598800 -0.178900 0.780700 +vn 0.049900 0.508600 0.859600 +vn 0.370000 0.116700 0.921600 +vn 0.535900 -0.165400 0.827900 +vn 0.508500 0.072700 0.857900 +vn 0.511900 0.027100 0.858600 +vn 0.592700 -0.215000 0.776100 +vn 0.566100 -0.250700 0.785200 +vn -0.482800 0.113300 0.868300 +vn -0.435500 0.132700 0.890300 +vn -0.141400 0.535500 0.832600 +vn -0.370000 0.116700 0.921600 +vn -0.049900 0.508600 0.859600 +vn -0.598800 -0.178900 0.780700 +vn -0.580200 -0.157400 0.799000 +vn -0.535900 -0.165400 0.827900 +vn -0.221600 0.533400 0.816200 +vn 0.196500 0.818000 0.540500 +vn 0.108600 0.844900 0.523700 +vn 0.404400 0.886700 0.223900 +vn 0.294300 0.774400 0.560000 +vn 0.492000 0.834400 0.248500 +vn 0.027200 0.855600 0.516900 +vn -0.288500 0.507300 0.812000 +vn -0.049300 0.850100 0.524300 +vn -0.338800 0.464500 0.818200 +vn 0.327200 0.924700 0.194600 +vn 0.256100 0.951700 0.169500 +vn 0.186500 0.970000 0.155700 +vn -0.508500 0.072700 0.857900 +vn -0.592700 -0.215000 0.776100 +vn -0.511900 0.027100 0.858600 +vn -0.566100 -0.250700 0.785200 +vn 0.411900 0.350800 0.841000 +vn 0.245300 0.774400 0.583200 +vn 0.135600 0.825700 0.547500 +vn 0.377500 0.412600 0.829000 +vn 0.047900 0.980300 0.191600 +vn -0.094100 0.982300 0.161700 +vn 0.498900 -0.009300 0.866600 +vn 0.473600 -0.037400 0.879900 +vn 0.526000 -0.273600 0.805300 +vn 0.472200 -0.284700 0.834200 +vn 0.429700 -0.055500 0.901200 +vn 0.433700 0.283600 0.855300 +vn 0.355600 -0.052700 0.933100 +vn 0.394600 -0.283700 0.874000 +vn 0.269700 -0.261900 0.926600 +vn 0.367900 0.685800 0.627900 +vn 0.243600 0.937400 0.248600 +vn 0.432000 0.220700 0.874400 +vn 0.479500 0.557500 0.677600 +vn 0.468600 0.819100 0.330700 +vn -0.411900 0.350800 0.841000 +vn -0.433700 0.283600 0.855300 +vn -0.429700 -0.055500 0.901200 +vn -0.473600 -0.037400 0.879900 +vn -0.394600 -0.283700 0.874000 +vn -0.432000 0.220700 0.874400 +vn -0.355600 -0.052700 0.933100 +vn -0.269700 -0.261900 0.926600 +vn -0.498900 -0.009300 0.866600 +vn -0.377500 0.412600 0.829000 +vn -0.472200 -0.284700 0.834200 +vn -0.526000 -0.273600 0.805300 +vn -0.135600 0.825700 0.547500 +vn -0.245300 0.774400 0.583200 +vn 0.094100 0.982300 0.161700 +vn -0.367900 0.685800 0.627900 +vn -0.479500 0.557500 0.677600 +vn -0.047900 0.980300 0.191600 +vn -0.243600 0.937400 0.248600 +vn -0.468600 0.819100 0.330700 +vn 0.365600 0.110700 0.924100 +vn 0.556300 0.276900 0.783500 +vn 0.543800 0.406100 0.734400 +vn 0.403600 0.164000 0.900100 +vn 0.729400 0.436000 0.527000 +vn 0.645200 0.629800 0.432400 +vn 0.261300 -0.043800 0.964200 +vn 0.181900 -0.039300 0.982500 +vn 0.120500 -0.223100 0.967300 +vn 0.123100 -0.029100 0.991900 +vn 0.331200 0.053700 0.942000 +vn 0.092100 -0.028600 0.995300 +vn 0.312500 -0.000300 0.949900 +vn -0.011300 -0.168000 0.985700 +vn -0.109100 -0.107500 0.988200 +vn -0.167100 -0.053000 0.984500 +vn 0.546300 0.156700 0.822800 +vn 0.755000 0.258500 0.602600 +vn 0.531000 0.035000 0.846600 +vn 0.752500 0.079500 0.653700 +vn -0.331200 0.053700 0.942000 +vn -0.123100 -0.029100 0.991900 +vn -0.181900 -0.039300 0.982500 +vn 0.109100 -0.107500 0.988200 +vn 0.011300 -0.168000 0.985700 +vn -0.312500 -0.000300 0.949900 +vn -0.092100 -0.028600 0.995300 +vn 0.167100 -0.053000 0.984500 +vn -0.365600 0.110700 0.924100 +vn -0.261300 -0.043800 0.964200 +vn -0.120500 -0.223100 0.967300 +vn -0.403600 0.164000 0.900100 +vn -0.543800 0.406100 0.734400 +vn -0.556300 0.276900 0.783500 +vn -0.645200 0.629800 0.432400 +vn -0.546300 0.156700 0.822800 +vn -0.531000 0.035000 0.846600 +vn -0.729400 0.436000 0.527000 +vn -0.755000 0.258500 0.602600 +vn -0.752500 0.079500 0.653700 +vn 0.535100 -0.129000 0.834900 +vn 0.526600 -0.061100 0.847900 +vn 0.310300 -0.042700 0.949600 +vn 0.726500 -0.192600 0.659600 +vn 0.736700 -0.072200 0.672300 +vn 0.322300 -0.058600 0.944800 +vn 0.081200 -0.019700 0.996500 +vn 0.085200 0.017000 0.996200 +vn -0.199100 0.011300 0.979900 +vn 0.114900 0.072600 0.990700 +vn 0.354500 -0.063400 0.932900 +vn 0.179200 0.134100 0.974600 +vn 0.407600 -0.073600 0.910200 +vn -0.209300 0.105300 0.972100 +vn -0.181700 0.221900 0.958000 +vn -0.100800 0.346000 0.932800 +vn 0.558200 -0.191400 0.807300 +vn 0.723300 -0.305300 0.619300 +vn 0.588100 -0.269100 0.762700 +vn 0.711800 -0.433600 0.552500 +vn -0.354500 -0.063400 0.932900 +vn -0.114900 0.072600 0.990700 +vn -0.085200 0.017000 0.996200 +vn 0.181700 0.221900 0.958000 +vn 0.209300 0.105300 0.972100 +vn -0.407600 -0.073600 0.910200 +vn -0.179200 0.134100 0.974600 +vn 0.100800 0.346000 0.932800 +vn -0.322300 -0.058600 0.944800 +vn -0.081200 -0.019700 0.996500 +vn -0.310300 -0.042700 0.949600 +vn 0.199100 0.011300 0.979900 +vn -0.526600 -0.061100 0.847900 +vn -0.535100 -0.129000 0.834900 +vn -0.736700 -0.072200 0.672300 +vn -0.726500 -0.192600 0.659600 +vn -0.558200 -0.191400 0.807300 +vn -0.588100 -0.269100 0.762700 +vn -0.723300 -0.305300 0.619300 +vn -0.711800 -0.433600 0.552500 +vn 0.612900 -0.473900 0.632300 +vn 0.610400 -0.367000 0.701800 +vn 0.471400 -0.107500 0.875300 +vn 0.619500 -0.687200 0.379300 +vn 0.677800 -0.568600 0.466100 +vn 0.530700 -0.173100 0.829700 +vn 0.271500 0.170200 0.947300 +vn 0.378600 0.156200 0.912300 +vn 0.024700 0.435500 0.899800 +vn 0.478000 0.102500 0.872400 +vn 0.577300 -0.256400 0.775200 +vn 0.555300 0.021000 0.831400 +vn 0.166700 0.463800 0.870100 +vn 0.304300 0.435200 0.847300 +vn 0.438700 0.360900 0.822900 +vn 0.593500 -0.571500 0.566600 +vn 0.545600 -0.778800 0.309200 +vn 0.598800 -0.342400 0.724000 +vn 0.561000 -0.648400 0.514600 +vn 0.468600 -0.843300 0.262900 +vn -0.577300 -0.256400 0.775200 +vn -0.478000 0.102500 0.872400 +vn -0.378600 0.156200 0.912300 +vn -0.304300 0.435200 0.847300 +vn -0.598800 -0.342400 0.724000 +vn -0.555300 0.021000 0.831400 +vn -0.438700 0.360900 0.822900 +vn -0.530700 -0.173100 0.829700 +vn -0.271500 0.170200 0.947300 +vn -0.471400 -0.107500 0.875300 +vn -0.166700 0.463800 0.870100 +vn -0.024700 0.435500 0.899800 +vn -0.610400 -0.367000 0.701800 +vn -0.612900 -0.473900 0.632300 +vn -0.677800 -0.568600 0.466100 +vn -0.619500 -0.687200 0.379300 +vn -0.593500 -0.571500 0.566600 +vn -0.561000 -0.648400 0.514600 +vn -0.545600 -0.778800 0.309200 +vn -0.468600 -0.843300 0.262900 +vn 0.443400 -0.756000 0.481400 +vn 0.512900 -0.706600 0.487500 +vn 0.591400 -0.413200 0.692400 +vn 0.302500 -0.924300 0.232600 +vn 0.390600 -0.888100 0.242000 +vn 0.594900 -0.064000 0.801200 +vn 0.581200 -0.127900 0.803600 +vn 0.528800 0.265600 0.806100 +vn 0.551200 0.191900 0.812000 +vn 0.548800 -0.467300 0.693100 +vn 0.513700 -0.137800 0.846800 +vn 0.462100 -0.488800 0.739900 +vn 0.360900 -0.037500 0.931900 +vn 0.504200 0.183700 0.843800 +vn 0.362700 0.294800 0.884000 +vn 0.342800 -0.792500 0.504400 +vn 0.198000 -0.953400 0.227400 +vn 0.305100 -0.434500 0.847400 +vn 0.196500 -0.796500 0.571800 +vn 0.069600 -0.972600 0.221800 +vn -0.548800 -0.467300 0.693100 +vn -0.462100 -0.488800 0.739900 +vn -0.513700 -0.137800 0.846800 +vn -0.581200 -0.127900 0.803600 +vn -0.504200 0.183700 0.843800 +vn -0.305100 -0.434500 0.847400 +vn -0.360900 -0.037500 0.931900 +vn -0.362700 0.294800 0.884000 +vn -0.594900 -0.064000 0.801200 +vn -0.591400 -0.413200 0.692400 +vn -0.551200 0.191900 0.812000 +vn -0.528800 0.265600 0.806100 +vn -0.512900 -0.706600 0.487500 +vn -0.443400 -0.756000 0.481400 +vn -0.390600 -0.888100 0.242000 +vn -0.302500 -0.924300 0.232600 +vn -0.342800 -0.792500 0.504400 +vn -0.196500 -0.796500 0.571800 +vn -0.198000 -0.953400 0.227400 +vn -0.069600 -0.972600 0.221800 +vn 0.363400 0.680500 0.636200 +vn 0.472000 0.468400 0.746800 +vn 0.411700 0.544800 0.730600 +vn 0.275100 0.744100 0.608800 +vn 0.275600 0.634000 0.722500 +vn 0.142500 0.869500 0.472900 +vn 0.246000 0.825800 0.507400 +vn 0.016800 0.943100 0.332000 +vn 0.127800 0.919600 0.371600 +vn 0.113500 0.808000 0.578200 +vn -0.026100 0.903300 0.428100 +vn -0.148100 0.949200 0.277400 +vn 0.264900 0.812000 0.520000 +vn 0.363500 0.666100 0.651200 +vn 0.188700 0.853200 0.486300 +vn 0.165000 0.907000 0.387400 +vn 0.116400 0.924400 0.363200 +vn 0.449400 0.455800 0.768300 +vn 0.263400 0.734600 0.625200 +vn 0.326800 0.551200 0.767700 +vn -0.363400 0.680500 0.636200 +vn -0.363500 0.666100 0.651200 +vn -0.264900 0.812000 0.520000 +vn -0.246000 0.825800 0.507400 +vn -0.165000 0.907000 0.387400 +vn -0.263400 0.734600 0.625200 +vn -0.188700 0.853200 0.486300 +vn -0.116400 0.924400 0.363200 +vn -0.142500 0.869500 0.472900 +vn -0.275100 0.744100 0.608800 +vn 0.026100 0.903300 0.428100 +vn -0.113500 0.808000 0.578200 +vn -0.127800 0.919600 0.371600 +vn -0.016800 0.943100 0.332000 +vn 0.148100 0.949200 0.277400 +vn -0.411700 0.544800 0.730600 +vn -0.472000 0.468400 0.746800 +vn -0.275600 0.634000 0.722500 +vn -0.449400 0.455800 0.768300 +vn -0.326800 0.551200 0.767700 +vn -0.250600 0.806000 0.536100 +vn -0.065400 0.686500 0.724100 +vn -0.229700 0.634700 0.737800 +vn -0.428000 0.735900 0.524600 +vn -0.384200 0.518700 0.763700 +vn -0.559700 0.770200 0.305700 +vn -0.386200 0.854700 0.346900 +vn -0.644000 0.760400 0.083200 +vn -0.487100 0.859500 0.154900 +vn -0.595400 0.610400 0.522400 +vn -0.723700 0.636200 0.267500 +vn -0.784700 0.619600 0.014800 +vn -0.210200 0.898000 0.386600 +vn -0.071800 0.829600 0.553600 +vn -0.322300 0.920900 0.219200 +vn 0.104200 0.685800 0.720300 +vn 0.250600 0.806000 0.536100 +vn 0.071800 0.829600 0.553600 +vn 0.210200 0.898000 0.386600 +vn 0.386200 0.854700 0.346900 +vn 0.322300 0.920900 0.219200 +vn 0.559700 0.770200 0.305700 +vn 0.428000 0.735900 0.524600 +vn 0.723700 0.636200 0.267500 +vn 0.595400 0.610400 0.522400 +vn 0.487100 0.859500 0.154900 +vn 0.644000 0.760400 0.083200 +vn 0.784700 0.619600 0.014800 +vn 0.229700 0.634700 0.737800 +vn 0.065400 0.686500 0.724100 +vn 0.384200 0.518700 0.763700 +vn -0.104200 0.685800 0.720300 +vn -0.534400 0.198800 0.821500 +vn -0.519400 0.052300 0.852900 +vn -0.787800 0.091700 0.609100 +vn -0.464000 -0.069900 0.883100 +vn -0.730900 -0.076200 0.678200 +vn -0.785900 0.266900 0.557700 +vn -0.944200 0.118000 0.307600 +vn -0.919400 0.298100 0.256400 +vn -0.991700 0.127900 0.007700 +vn -0.912700 -0.074800 0.401700 +vn -0.992600 -0.069900 0.098800 +vn -0.845900 0.473600 0.245000 +vn -0.722600 0.443400 0.530400 +vn -0.953900 0.298400 -0.031300 +vn -0.886300 0.462200 -0.027600 +vn -0.493900 0.359200 0.791800 +vn 0.722600 0.443400 0.530400 +vn 0.845900 0.473600 0.245000 +vn 0.919400 0.298100 0.256400 +vn 0.886300 0.462200 -0.027600 +vn 0.953900 0.298400 -0.031300 +vn 0.785900 0.266900 0.557700 +vn 0.944200 0.118000 0.307600 +vn 0.787800 0.091700 0.609100 +vn 0.912700 -0.074800 0.401700 +vn 0.991700 0.127900 0.007700 +vn 0.992600 -0.069900 0.098800 +vn 0.519400 0.052300 0.852900 +vn 0.534400 0.198800 0.821500 +vn 0.730900 -0.076200 0.678200 +vn 0.464000 -0.069900 0.883100 +vn 0.493900 0.359200 0.791800 +vn -0.219400 -0.284400 0.933300 +vn -0.026200 -0.369400 0.928900 +vn -0.165700 -0.489200 0.856300 +vn 0.183600 -0.422700 0.887500 +vn 0.100000 -0.544100 0.833000 +vn -0.417900 -0.381200 0.824600 +vn -0.299500 -0.592800 0.747500 +vn -0.595400 -0.459900 0.658700 +vn -0.435600 -0.688700 0.579600 +vn 0.016800 -0.648700 0.760900 +vn -0.076000 -0.753400 0.653100 +vn -0.804500 -0.273900 0.526900 +vn -0.612000 -0.235500 0.755000 +vn -0.743900 -0.518800 0.421000 +vn -0.924900 -0.293500 0.241800 +vn -0.368800 -0.179200 0.912000 +vn 0.612000 -0.235500 0.755000 +vn 0.804500 -0.273900 0.526900 +vn 0.595400 -0.459900 0.658700 +vn 0.924900 -0.293500 0.241800 +vn 0.743900 -0.518800 0.421000 +vn 0.417900 -0.381200 0.824600 +vn 0.299500 -0.592800 0.747500 +vn 0.165700 -0.489200 0.856300 +vn -0.016800 -0.648700 0.760900 +vn 0.435600 -0.688700 0.579600 +vn 0.076000 -0.753400 0.653100 +vn 0.026200 -0.369400 0.928900 +vn 0.219400 -0.284400 0.933300 +vn -0.100000 -0.544100 0.833000 +vn -0.183600 -0.422700 0.887500 +vn 0.368800 -0.179200 0.912000 +vn 0.448300 -0.443100 0.776300 +vn 0.517900 -0.433500 0.737400 +vn 0.498300 -0.534500 0.682600 +vn 0.572700 -0.414500 0.707200 +vn 0.562300 -0.513200 0.648400 +vn 0.418300 -0.548000 0.724300 +vn 0.473700 -0.611600 0.633600 +vn 0.384600 -0.631200 0.673600 +vn 0.443500 -0.684200 0.578900 +vn 0.545000 -0.586400 0.599300 +vn 0.521400 -0.653600 0.548600 +vn 0.249900 -0.648000 0.719400 +vn 0.299600 -0.555400 0.775700 +vn 0.344600 -0.712100 0.611700 +vn 0.192200 -0.740800 0.643600 +vn 0.347200 -0.443600 0.826200 +vn -0.299600 -0.555400 0.775700 +vn -0.249900 -0.648000 0.719400 +vn -0.384600 -0.631200 0.673600 +vn -0.192200 -0.740800 0.643600 +vn -0.344600 -0.712100 0.611700 +vn -0.418300 -0.548000 0.724300 +vn -0.473700 -0.611600 0.633600 +vn -0.498300 -0.534500 0.682600 +vn -0.545000 -0.586400 0.599300 +vn -0.443500 -0.684200 0.578900 +vn -0.521400 -0.653600 0.548600 +vn -0.517900 -0.433500 0.737400 +vn -0.448300 -0.443100 0.776300 +vn -0.562300 -0.513200 0.648400 +vn -0.572700 -0.414500 0.707200 +vn -0.347200 -0.443600 0.826200 +vn 0.654000 -0.447500 0.609900 +vn 0.615900 -0.481800 0.623200 +vn 0.615800 -0.384000 0.687900 +vn 0.641200 -0.350800 0.682500 +vn 0.642600 -0.326200 0.693300 +vn 0.670300 -0.419700 0.611900 +vn 0.614800 -0.322300 0.719800 +vn 0.659000 -0.408800 0.631300 +vn 0.682600 -0.484500 0.547000 +vn 0.654300 -0.515600 0.553100 +vn 0.682700 -0.544800 0.486900 +vn 0.686000 -0.468400 0.556700 +vn 0.698700 -0.526700 0.484100 +vn 0.606600 -0.552400 0.571800 +vn 0.645000 -0.577300 0.500700 +vn 0.589600 -0.616100 0.522200 +vn -0.654000 -0.447500 0.609900 +vn -0.654300 -0.515600 0.553100 +vn -0.682600 -0.484500 0.547000 +vn -0.670300 -0.419700 0.611900 +vn -0.686000 -0.468400 0.556700 +vn -0.645000 -0.577300 0.500700 +vn -0.682700 -0.544800 0.486900 +vn -0.698700 -0.526700 0.484100 +vn -0.642600 -0.326200 0.693300 +vn -0.641200 -0.350800 0.682500 +vn -0.659000 -0.408800 0.631300 +vn -0.614800 -0.322300 0.719800 +vn -0.615800 -0.384000 0.687900 +vn -0.615900 -0.481800 0.623200 +vn -0.606600 -0.552400 0.571800 +vn -0.589600 -0.616100 0.522200 +vn 0.792100 -0.609100 0.040400 +vn 0.707300 -0.699100 0.104400 +vn 0.628200 -0.771000 0.104600 +vn 0.854400 -0.519400 0.012700 +vn 0.786400 -0.611100 0.089800 +vn 0.530700 -0.825800 0.190500 +vn 0.686500 -0.689400 0.231300 +vn 0.307400 -0.877400 0.368300 +vn 0.502000 -0.603400 0.619500 +vn 0.802000 -0.521800 0.290800 +vn 0.840500 -0.535100 0.084700 +vn 0.899100 -0.287600 0.329800 +vn 0.921700 -0.380000 0.077600 +vn 0.644100 -0.246100 0.724200 +vn 0.701300 0.161700 0.694200 +vn 0.746900 -0.657200 0.100900 +vn 0.868900 -0.493900 -0.031900 +vn 0.897300 -0.433300 -0.084100 +vn 0.917600 -0.393200 -0.058400 +vn 0.912100 -0.389000 -0.129300 +vn -0.840500 -0.535100 0.084700 +vn -0.802000 -0.521800 0.290800 +vn -0.686500 -0.689400 0.231300 +vn -0.644100 -0.246100 0.724200 +vn -0.502000 -0.603400 0.619500 +vn -0.921700 -0.380000 0.077600 +vn -0.899100 -0.287600 0.329800 +vn -0.701300 0.161700 0.694200 +vn -0.530700 -0.825800 0.190500 +vn -0.628200 -0.771000 0.104600 +vn -0.307400 -0.877400 0.368300 +vn -0.707300 -0.699100 0.104400 +vn -0.792100 -0.609100 0.040400 +vn -0.786400 -0.611100 0.089800 +vn -0.854400 -0.519400 0.012700 +vn -0.746900 -0.657200 0.100900 +vn -0.868900 -0.493900 -0.031900 +vn -0.917600 -0.393200 -0.058400 +vn -0.897300 -0.433300 -0.084100 +vn -0.912100 -0.389000 -0.129300 +vn 0.172700 -0.313900 0.933600 +vn 0.035700 -0.791200 0.610600 +vn -0.039900 -0.737800 0.673800 +vn -0.041500 -0.989200 0.140300 +vn -0.101400 -0.978000 0.182000 +vn 0.051500 -0.236000 0.970400 +vn 0.034100 -0.769100 0.638200 +vn 0.128500 -0.326200 0.936500 +vn -0.044900 -0.977600 0.205600 +vn 0.185100 0.119200 0.975400 +vn 0.121100 0.219600 0.968000 +vn 0.202400 0.449700 0.869900 +vn 0.161500 0.533000 0.830500 +vn 0.262700 0.154100 0.952500 +vn 0.290600 0.498400 0.816800 +vn -0.121100 0.219600 0.968000 +vn -0.185100 0.119200 0.975400 +vn -0.128500 -0.326200 0.936500 +vn -0.161500 0.533000 0.830500 +vn -0.202400 0.449700 0.869900 +vn -0.051500 -0.236000 0.970400 +vn -0.034100 -0.769100 0.638200 +vn 0.039900 -0.737800 0.673800 +vn 0.044900 -0.977600 0.205600 +vn -0.035700 -0.791200 0.610600 +vn -0.172700 -0.313900 0.933600 +vn 0.101400 -0.978000 0.182000 +vn 0.041500 -0.989200 0.140300 +vn -0.262700 0.154100 0.952500 +vn -0.290600 0.498400 0.816800 +vn 0.940400 -0.338500 -0.032700 +vn 0.971600 -0.236500 -0.000500 +vn 0.964200 -0.257800 0.061100 +vn 0.924500 -0.377200 0.055000 +vn 0.873800 -0.479900 0.078700 +vn 0.906000 -0.422900 -0.017900 +vn 0.919200 -0.378300 -0.108500 +vn 0.941800 -0.319400 -0.105000 +vn 0.920000 -0.363200 -0.147000 +vn 0.971100 -0.232900 -0.052200 +vn 0.935800 -0.330200 -0.123000 +vn 0.960900 -0.271400 -0.055200 +vn -0.940400 -0.338500 -0.032700 +vn -0.941800 -0.319400 -0.105000 +vn -0.919200 -0.378300 -0.108500 +vn -0.906000 -0.422900 -0.017900 +vn -0.935800 -0.330200 -0.123000 +vn -0.920000 -0.363200 -0.147000 +vn -0.873800 -0.479900 0.078700 +vn -0.924500 -0.377200 0.055000 +vn -0.964200 -0.257800 0.061100 +vn -0.971600 -0.236500 -0.000500 +vn -0.971100 -0.232900 -0.052200 +vn -0.960900 -0.271400 -0.055200 +vn 0.098200 0.417000 0.903600 +vn -0.236500 0.770700 0.591600 +vn 0.316700 0.704500 0.635100 +vn 0.420500 0.272500 0.865400 +vn 0.837200 0.347100 0.422500 +vn -0.437800 0.870900 0.223200 +vn 0.134800 0.975300 0.174600 +vn 0.513700 -0.069800 0.855100 +vn 0.365800 0.015400 0.930500 +vn 0.548400 -0.260400 0.794600 +vn 0.494700 -0.225800 0.839200 +vn 0.791100 0.082500 0.606100 +vn 0.730500 -0.155000 0.665100 +vn 0.659000 -0.289700 0.694000 +vn 0.327100 0.077300 0.941800 +vn -0.001900 0.476500 0.879200 +vn 0.494200 -0.194000 0.847400 +vn -0.349800 0.751800 0.559000 +vn -0.536900 0.808400 0.241100 +vn -0.098200 0.417000 0.903600 +vn 0.001900 0.476500 0.879200 +vn -0.327100 0.077300 0.941800 +vn -0.365800 0.015400 0.930500 +vn -0.494200 -0.194000 0.847400 +vn -0.513700 -0.069800 0.855100 +vn -0.420500 0.272500 0.865400 +vn -0.730500 -0.155000 0.665100 +vn -0.791100 0.082500 0.606100 +vn -0.494700 -0.225800 0.839200 +vn -0.548400 -0.260400 0.794600 +vn -0.659000 -0.289700 0.694000 +vn -0.316700 0.704500 0.635100 +vn 0.236500 0.770700 0.591600 +vn -0.134800 0.975300 0.174600 +vn -0.837200 0.347100 0.422500 +vn 0.349800 0.751800 0.559000 +vn 0.437800 0.870900 0.223200 +vn 0.536900 0.808400 0.241100 +vn 0.141700 0.865700 0.480000 +vn 0.168000 0.743900 0.646800 +vn 0.189600 0.679300 0.708900 +vn 0.152000 0.823300 0.546800 +vn 0.102900 0.906900 0.408500 +vn 0.107100 0.930900 0.349200 +vn 0.054100 0.952000 0.301100 +vn 0.073900 0.962700 0.260100 +vn 0.206000 0.914200 0.349000 +vn 0.232400 0.856700 0.460500 +vn 0.425500 0.788600 0.444000 +vn 0.188500 0.947900 0.256700 +vn 0.450900 0.816600 0.360100 +vn 0.253200 0.739000 0.624200 +vn 0.441700 0.722800 0.531500 +vn 0.501900 0.582400 0.639500 +vn -0.141700 0.865700 0.480000 +vn -0.232400 0.856700 0.460500 +vn -0.206000 0.914200 0.349000 +vn -0.107100 0.930900 0.349200 +vn -0.188500 0.947900 0.256700 +vn -0.441700 0.722800 0.531400 +vn -0.425500 0.788600 0.444000 +vn -0.450900 0.816600 0.360100 +vn -0.102900 0.906900 0.408500 +vn -0.152000 0.823300 0.546800 +vn -0.073900 0.962700 0.260100 +vn -0.054000 0.952000 0.301100 +vn -0.189600 0.679300 0.708900 +vn -0.168000 0.743900 0.646800 +vn -0.253200 0.739000 0.624200 +vn -0.501900 0.582400 0.639500 +vn 0.721200 0.136200 0.679200 +vn 0.696200 0.022900 0.717400 +vn 0.705600 0.100100 0.701400 +vn 0.666900 0.101600 0.738200 +vn 0.686200 0.172800 0.706500 +vn 0.786100 -0.009600 0.618000 +vn 0.748600 -0.111100 0.653600 +vn 0.728800 -0.003900 0.684700 +vn 0.710400 0.190700 0.677400 +vn 0.776000 0.074900 0.626200 +vn 0.755300 0.141800 0.639800 +vn 0.897300 -0.067500 0.436200 +vn 0.790000 0.135700 0.597900 +vn 0.938200 -0.112900 0.327100 +vn 0.689500 0.358800 0.629100 +vn 0.636300 0.453000 0.624300 +vn 0.865000 0.020400 0.501300 +vn 0.823300 0.129600 0.552700 +vn 0.656400 0.467200 0.592300 +vn 0.738700 0.401600 0.541300 +vn -0.636300 0.453000 0.624300 +vn -0.689500 0.358800 0.629100 +vn -0.755300 0.141800 0.639800 +vn -0.823300 0.129600 0.552700 +vn -0.865000 0.020400 0.501300 +vn -0.710400 0.190700 0.677400 +vn -0.776000 0.074900 0.626200 +vn -0.705600 0.100100 0.701400 +vn -0.790000 0.135700 0.597900 +vn -0.897300 -0.067500 0.436200 +vn -0.938200 -0.112900 0.327100 +vn -0.696200 0.022900 0.717400 +vn -0.721200 0.136200 0.679200 +vn -0.748600 -0.111100 0.653600 +vn -0.786100 -0.009600 0.618000 +vn -0.686200 0.172800 0.706500 +vn -0.666900 0.101600 0.738200 +vn -0.728800 -0.003900 0.684700 +vn -0.656400 0.467200 0.592300 +vn -0.738700 0.401600 0.541300 +vn 0.612100 0.416100 0.672400 +vn 0.579700 0.530200 0.618700 +vn 0.759200 0.391000 0.520300 +vn 0.933300 0.003800 0.359100 +vn 0.921900 0.006300 0.387300 +vn 0.565500 0.649700 0.508000 +vn 0.776400 0.453700 0.437400 +vn 0.952100 -0.032900 0.304100 +vn 0.758800 0.321000 0.566700 +vn 0.774800 0.239100 0.585200 +vn 0.654000 0.303200 0.693000 +vn 0.925800 -0.034200 0.376400 +vn 0.628700 0.244400 0.738200 +vn 0.580500 0.377900 0.721200 +vn 0.703100 0.170800 0.690200 +vn 0.536800 0.527100 0.658700 +vn 0.518300 0.663400 0.539600 +vn 0.671900 0.325300 0.665300 +vn 0.621300 0.499100 0.604000 +vn 0.555300 0.631000 0.541700 +vn -0.612100 0.416100 0.672400 +vn -0.580500 0.377900 0.721200 +vn -0.628700 0.244400 0.738200 +vn -0.654000 0.303200 0.693000 +vn -0.671900 0.325300 0.665300 +vn -0.703100 0.170800 0.690200 +vn -0.774800 0.239100 0.585200 +vn -0.758800 0.321000 0.566700 +vn -0.925800 -0.034200 0.376400 +vn -0.921900 0.006300 0.387300 +vn -0.759200 0.391000 0.520300 +vn -0.579700 0.530200 0.618700 +vn -0.776400 0.453700 0.437400 +vn -0.933300 0.003800 0.359100 +vn -0.952100 -0.032900 0.304100 +vn -0.536800 0.527100 0.658700 +vn -0.621300 0.499100 0.604000 +vn -0.565500 0.649700 0.508000 +vn -0.518300 0.663400 0.539600 +vn -0.555300 0.631000 0.541700 +vn 0.822600 -0.492000 0.284900 +vn 0.764900 -0.524800 0.373500 +vn 0.804900 -0.485800 0.340800 +vn 0.718000 -0.525400 0.456500 +vn 0.762600 -0.477900 0.436000 +vn 0.793400 -0.553100 0.253900 +vn 0.743200 -0.583000 0.328200 +vn 0.713800 -0.573500 0.402000 +vn 0.859700 -0.436900 0.264400 +vn 0.854400 -0.448900 0.261700 +vn 0.896500 -0.419000 0.143900 +vn 0.912500 -0.398400 0.091900 +vn 0.827400 -0.433200 0.357400 +vn 0.886900 -0.408600 0.215300 +vn 0.892100 -0.410000 0.189600 +vn 0.938000 -0.283500 0.199300 +vn 0.918900 -0.307500 0.247200 +vn 0.940200 -0.320300 0.116100 +vn 0.899000 -0.361800 0.246800 +vn 0.876000 -0.431000 0.216300 +vn -0.918900 -0.307500 0.247200 +vn -0.938000 -0.283500 0.199300 +vn -0.892100 -0.410000 0.189600 +vn -0.940200 -0.320300 0.116100 +vn -0.912500 -0.398400 0.091900 +vn -0.859700 -0.436900 0.264400 +vn -0.854400 -0.448900 0.261700 +vn -0.827400 -0.433200 0.357400 +vn -0.762600 -0.477900 0.436000 +vn -0.896500 -0.419000 0.143900 +vn -0.886900 -0.408600 0.215300 +vn -0.804900 -0.485800 0.340800 +vn -0.764900 -0.524800 0.373500 +vn -0.822600 -0.492000 0.284900 +vn -0.743200 -0.583000 0.328200 +vn -0.793400 -0.553100 0.253900 +vn -0.718000 -0.525400 0.456500 +vn -0.713800 -0.573500 0.402000 +vn -0.899000 -0.361800 0.246800 +vn -0.876000 -0.431000 0.216300 +vn 0.635800 -0.420300 0.647300 +vn 0.677100 -0.373700 0.633800 +vn 0.742200 -0.392900 0.542800 +vn 0.769200 -0.322900 0.551300 +vn 0.839100 -0.354100 0.412800 +vn 0.607100 -0.410400 0.680400 +vn 0.609600 -0.358100 0.707100 +vn 0.677400 -0.433500 0.594300 +vn 0.793100 -0.412500 0.448000 +vn 0.723000 -0.448400 0.525500 +vn 0.874000 -0.381800 0.300500 +vn 0.688000 -0.501200 0.524800 +vn 0.660300 -0.478600 0.578700 +vn 0.693000 -0.560000 0.454100 +vn 0.689800 -0.535300 0.487400 +vn 0.641900 -0.466300 0.608700 +vn 0.636800 -0.456600 0.621200 +vn 0.685300 -0.522700 0.507100 +vn 0.687600 -0.512700 0.514000 +vn -0.660300 -0.478600 0.578700 +vn -0.688000 -0.501200 0.524800 +vn -0.723000 -0.448400 0.525500 +vn -0.689800 -0.535300 0.487400 +vn -0.693000 -0.560000 0.454100 +vn -0.677400 -0.433500 0.594300 +vn -0.793100 -0.412500 0.448000 +vn -0.742200 -0.392900 0.542800 +vn -0.874000 -0.381800 0.300500 +vn -0.677100 -0.373700 0.633800 +vn -0.635800 -0.420300 0.647300 +vn -0.609600 -0.358100 0.707100 +vn -0.607100 -0.410400 0.680400 +vn -0.839100 -0.354100 0.412800 +vn -0.769200 -0.322900 0.551300 +vn -0.641900 -0.466300 0.608700 +vn -0.685300 -0.522700 0.507100 +vn -0.636800 -0.456600 0.621200 +vn -0.687600 -0.512700 0.514000 +vn 0.606300 -0.406800 0.683200 +vn 0.631500 -0.407800 0.659400 +vn 0.574800 -0.330700 0.748500 +vn 0.554600 -0.339900 0.759500 +vn 0.564600 -0.349400 0.747700 +vn 0.596100 -0.407100 0.692000 +vn 0.640800 -0.456300 0.617300 +vn 0.657100 -0.456200 0.600000 +vn 0.691900 -0.511100 0.510000 +vn 0.673100 -0.461700 0.577700 +vn 0.698600 -0.512100 0.499700 +vn 0.700700 -0.518300 0.490300 +vn -0.606300 -0.406800 0.683200 +vn -0.657100 -0.456200 0.600000 +vn -0.640800 -0.456300 0.617300 +vn -0.698600 -0.512100 0.499700 +vn -0.691900 -0.511100 0.510000 +vn -0.596100 -0.407100 0.692000 +vn -0.564600 -0.349400 0.747700 +vn -0.554600 -0.339900 0.759500 +vn -0.574800 -0.330700 0.748500 +vn -0.631500 -0.407800 0.659400 +vn -0.673100 -0.461700 0.577700 +vn -0.700700 -0.518300 0.490300 +vn 0.978800 -0.145500 0.144000 +vn 0.999300 -0.010100 0.036400 +vn 0.991800 -0.126700 0.018400 +vn 0.962400 -0.262700 0.068400 +vn 0.966700 -0.255700 0.000900 +vn 0.928300 -0.338200 0.154600 +vn 0.920000 -0.290800 0.262700 +vn 0.943700 -0.329800 0.024400 +vn 0.921400 -0.378500 0.087600 +vn 0.870000 -0.231800 0.435000 +vn 0.932900 -0.058700 0.355200 +vn 0.982700 0.053300 0.177300 +vn -0.978800 -0.145500 0.144000 +vn -0.932900 -0.058700 0.355200 +vn -0.870000 -0.231800 0.435000 +vn -0.920000 -0.290800 0.262700 +vn -0.928300 -0.338200 0.154600 +vn -0.962400 -0.262700 0.068400 +vn -0.921400 -0.378500 0.087600 +vn -0.943700 -0.329800 0.024400 +vn -0.991800 -0.126700 0.018400 +vn -0.966700 -0.255700 0.000900 +vn -0.999300 -0.010100 0.036400 +vn -0.982700 0.053300 0.177300 +vn 0.929400 -0.368400 -0.022200 +vn 0.942900 -0.328400 -0.054700 +vn 0.928000 -0.356800 -0.107100 +vn 0.922400 -0.380200 -0.068300 +vn 0.919600 -0.370600 -0.130300 +vn 0.923200 -0.376000 -0.079400 +vn 0.922400 -0.367800 -0.117600 +vn 0.930100 -0.367400 -0.002700 +vn 0.919800 -0.392400 -0.005100 +vn 0.935000 -0.351800 -0.044500 +vn 0.953800 -0.288600 0.083300 +vn 0.916200 -0.399200 0.033800 +vn -0.919800 -0.392400 -0.005100 +vn -0.930100 -0.367400 -0.002700 +vn -0.923200 -0.376000 -0.079400 +vn -0.953800 -0.288600 0.083300 +vn -0.935000 -0.351800 -0.044500 +vn -0.922400 -0.380200 -0.068300 +vn -0.919600 -0.370600 -0.130300 +vn -0.928000 -0.356800 -0.107100 +vn -0.922400 -0.367800 -0.117600 +vn -0.942900 -0.328400 -0.054700 +vn -0.929400 -0.368400 -0.022200 +vn -0.916200 -0.399200 0.033800 +vn 0.970400 -0.223500 0.091000 +vn 0.953000 -0.302300 0.020400 +vn 0.941500 -0.332800 -0.052900 +vn 0.959700 -0.266900 0.087400 +vn 0.944100 -0.095900 0.315500 +vn 0.951200 -0.118100 0.284900 +vn 0.947100 -0.242400 0.210200 +vn -0.970400 -0.223500 0.091000 +vn -0.953000 -0.302300 0.020400 +vn -0.947100 -0.242400 0.210200 +vn -0.951200 -0.118100 0.284900 +vn -0.944100 -0.095900 0.315500 +vn -0.959700 -0.266900 0.087400 +vn -0.941500 -0.332800 -0.052900 +vn 0.707000 -0.549800 0.444700 +vn 0.699500 -0.547500 0.459200 +vn 0.707200 -0.559900 0.431700 +vn 0.714700 -0.555700 0.424500 +vn 0.690800 -0.544100 0.476000 +vn 0.696800 -0.565600 0.441000 +vn 0.718900 -0.554700 0.418900 +vn 0.712100 -0.552400 0.433100 +vn 0.720500 -0.556700 0.413600 +vn 0.714400 -0.556300 0.424400 +vn 0.671300 -0.484600 0.560700 +vn 0.673800 -0.473200 0.567500 +vn 0.579800 -0.339500 0.740600 +vn 0.665500 -0.494400 0.559100 +vn 0.559600 -0.354500 0.749000 +vn 0.674200 -0.458300 0.579200 +vn 0.674100 -0.436900 0.595600 +vn 0.595300 -0.319200 0.737400 +vn 0.606600 -0.289800 0.740300 +vn 0.614400 -0.245600 0.749700 +vn -0.707000 -0.549800 0.444700 +vn -0.673800 -0.473200 0.567500 +vn -0.671300 -0.484600 0.560700 +vn -0.712100 -0.552400 0.433100 +vn -0.665500 -0.494400 0.559100 +vn -0.595300 -0.319200 0.737400 +vn -0.579800 -0.339500 0.740600 +vn -0.559600 -0.354500 0.749000 +vn -0.718900 -0.554700 0.418900 +vn -0.714700 -0.555700 0.424500 +vn -0.714400 -0.556300 0.424400 +vn -0.720500 -0.556700 0.413600 +vn -0.707200 -0.559900 0.431700 +vn -0.699500 -0.547500 0.459200 +vn -0.696800 -0.565600 0.441000 +vn -0.674200 -0.458300 0.579200 +vn -0.606600 -0.289800 0.740300 +vn -0.690800 -0.544100 0.476000 +vn -0.674100 -0.436900 0.595600 +vn -0.614400 -0.245600 0.749700 +vn 0.710900 -0.564300 0.419700 +vn 0.718100 -0.564700 0.406600 +vn 0.716400 -0.575800 0.394000 +vn 0.704100 -0.575500 0.415900 +vn 0.710300 -0.596300 0.373900 +vn 0.712600 -0.613400 0.340600 +vn 0.684500 -0.613200 0.394100 +vn 0.693000 -0.593500 0.409300 +vn 0.620900 -0.532800 0.574900 +vn 0.639400 -0.518000 0.568200 +vn 0.472500 -0.386200 0.792100 +vn 0.504400 -0.378400 0.776100 +vn 0.603700 -0.550200 0.576800 +vn 0.444300 -0.391800 0.805600 +vn 0.654000 -0.506000 0.562300 +vn 0.531600 -0.370600 0.761600 +vn -0.639400 -0.518000 0.568200 +vn -0.620900 -0.532800 0.574900 +vn -0.693000 -0.593500 0.409300 +vn -0.603700 -0.550200 0.576800 +vn -0.504400 -0.378400 0.776100 +vn -0.472500 -0.386200 0.792100 +vn -0.444300 -0.391800 0.805600 +vn -0.704100 -0.575500 0.415900 +vn -0.710300 -0.596300 0.373900 +vn -0.716400 -0.575800 0.394000 +vn -0.684500 -0.613200 0.394100 +vn -0.712600 -0.613400 0.340600 +vn -0.718100 -0.564700 0.406600 +vn -0.710900 -0.564300 0.419700 +vn -0.654000 -0.506000 0.562300 +vn -0.531600 -0.370600 0.761600 +vn 0.729200 -0.592400 0.342400 +vn 0.691500 -0.622700 0.366000 +vn 0.726800 -0.622400 0.290500 +vn 0.768200 -0.592900 0.241500 +vn 0.850700 -0.475800 0.223300 +vn 0.804800 -0.475000 0.355800 +vn 0.952400 -0.067000 0.297400 +vn 0.895500 -0.090500 0.435700 +vn 0.699800 -0.407500 0.586700 +vn 0.640800 -0.522000 0.562900 +vn 0.524100 -0.245100 0.815600 +vn 0.742000 -0.104100 0.662200 +vn 0.579000 -0.102100 0.808900 +vn 0.605500 -0.557400 0.567900 +vn 0.465100 -0.350000 0.813100 +vn 0.434900 -0.391100 0.811100 +vn -0.729200 -0.592400 0.342400 +vn -0.640800 -0.522000 0.562900 +vn -0.699800 -0.407500 0.586700 +vn -0.804800 -0.475000 0.355800 +vn -0.742000 -0.104100 0.662200 +vn -0.465100 -0.350000 0.813100 +vn -0.524100 -0.245100 0.815600 +vn -0.579000 -0.102100 0.808900 +vn -0.850700 -0.475800 0.223300 +vn -0.768200 -0.592900 0.241500 +vn -0.895500 -0.090500 0.435700 +vn -0.952400 -0.067000 0.297400 +vn -0.726800 -0.622400 0.290500 +vn -0.691500 -0.622700 0.366000 +vn -0.605500 -0.557400 0.567900 +vn -0.434900 -0.391100 0.811100 +vn 0.752100 0.457800 0.474000 +vn 0.786600 0.485800 0.381000 +vn 0.554100 0.727400 0.404700 +vn 0.539500 0.719300 0.437500 +vn 0.503600 0.750200 0.428400 +vn 0.495400 0.759200 0.422000 +vn 0.589300 0.661300 0.464000 +vn 0.465700 0.668100 0.580300 +vn 0.499200 0.596200 0.628700 +vn 0.355300 0.431000 0.829500 +vn 0.383200 0.344600 0.857000 +vn 0.600400 0.662100 0.448500 +vn 0.556300 0.588600 0.586500 +vn 0.409500 0.393900 0.822800 +vn 0.641900 0.347100 0.683700 +vn 0.488600 0.135700 0.861900 +vn -0.499200 0.596200 0.628700 +vn -0.465700 0.668100 0.580300 +vn -0.495400 0.759200 0.422000 +vn -0.556300 0.588600 0.586500 +vn -0.600400 0.662100 0.448500 +vn -0.383200 0.344600 0.857000 +vn -0.355300 0.431000 0.829500 +vn -0.409500 0.393900 0.822800 +vn -0.539500 0.719300 0.437500 +vn -0.503600 0.750200 0.428400 +vn -0.554100 0.727400 0.404700 +vn -0.589300 0.661300 0.464000 +vn -0.786600 0.485800 0.381000 +vn -0.752100 0.457800 0.474000 +vn -0.641900 0.347100 0.683700 +vn -0.488600 0.135700 0.861900 +vn 0.729000 0.484200 0.483700 +vn 0.713000 0.488300 0.503200 +vn 0.779400 0.310300 0.544400 +vn 0.799700 0.307400 0.515600 +vn 0.810000 0.134100 0.570900 +vn 0.833100 0.126300 0.538400 +vn 0.816400 -0.079200 0.572000 +vn 0.742700 0.124700 0.657800 +vn 0.713000 0.286100 0.640100 +vn 0.502200 0.128000 0.855200 +vn 0.839700 -0.104700 0.532900 +vn 0.753100 -0.076500 0.653400 +vn 0.471300 0.047900 0.880600 +vn 0.658900 0.434700 0.613800 +vn 0.497800 0.220800 0.838600 +vn 0.470700 0.304300 0.828200 +vn -0.713000 0.286100 0.640100 +vn -0.742700 0.124700 0.657800 +vn -0.833100 0.126300 0.538400 +vn -0.753100 -0.076500 0.653400 +vn -0.497800 0.220800 0.838600 +vn -0.502200 0.128000 0.855200 +vn -0.471300 0.047900 0.880600 +vn -0.799700 0.307400 0.515600 +vn -0.810000 0.134100 0.570900 +vn -0.779400 0.310200 0.544400 +vn -0.839700 -0.104700 0.532900 +vn -0.816400 -0.079200 0.572000 +vn -0.713000 0.488300 0.503200 +vn -0.729000 0.484200 0.483700 +vn -0.658900 0.434700 0.613800 +vn -0.470700 0.304300 0.828200 +vn 0.835800 -0.193700 0.513600 +vn 0.820700 -0.269900 0.503600 +vn 0.809800 -0.223800 0.542300 +vn 0.842400 -0.155000 0.516000 +vn 0.823000 0.303500 0.480100 +vn 0.790800 0.303000 0.531700 +vn 0.483000 0.816000 0.317700 +vn 0.434700 0.823300 0.364900 +vn 0.569100 0.436100 0.697000 +vn 0.685600 -0.002000 0.727900 +vn 0.132400 0.633000 0.762700 +vn 0.228400 0.844500 0.484400 +vn -0.018400 0.838100 0.545200 +vn 0.730100 -0.194200 0.655200 +vn 0.286000 0.303400 0.908900 +vn 0.395700 0.077400 0.915100 +vn -0.835800 -0.193700 0.513600 +vn -0.685600 -0.002000 0.727900 +vn -0.569100 0.436100 0.697000 +vn -0.790800 0.303000 0.531700 +vn -0.228400 0.844500 0.484400 +vn -0.286000 0.303400 0.908900 +vn -0.132400 0.633000 0.762700 +vn 0.018400 0.838100 0.545200 +vn -0.823000 0.303500 0.480100 +vn -0.842400 -0.155000 0.516000 +vn -0.434700 0.823300 0.364900 +vn -0.483000 0.816000 0.317700 +vn -0.809800 -0.223800 0.542300 +vn -0.820700 -0.269900 0.503600 +vn -0.730100 -0.194200 0.655200 +vn -0.395700 0.077400 0.915100 +vn -0.002400 0.971200 0.238300 +vn 0.091600 0.968000 0.233600 +vn 0.159200 0.966600 0.200700 +vn 0.048700 0.976900 0.207800 +vn 0.016100 0.969500 0.244700 +vn -0.010300 0.960700 0.277300 +vn 0.064900 0.954700 0.290300 +vn -0.029700 0.914900 0.402600 +vn -0.071600 0.933400 0.351400 +vn -0.039500 0.811600 0.582800 +vn -0.134900 0.848900 0.511000 +vn 0.062800 0.942600 0.327900 +vn 0.105000 0.873400 0.475500 +vn 0.171600 0.714900 0.677800 +vn -0.025800 0.938500 0.344400 +vn -0.129300 0.861000 0.491900 +vn 0.071600 0.933400 0.351400 +vn 0.029700 0.914900 0.402600 +vn 0.010300 0.960700 0.277300 +vn -0.105000 0.873400 0.475500 +vn -0.062800 0.942600 0.327900 +vn 0.134900 0.848900 0.511000 +vn 0.039500 0.811600 0.582800 +vn -0.171600 0.714900 0.677800 +vn 0.002400 0.971200 0.238300 +vn -0.016100 0.969500 0.244700 +vn -0.048700 0.976900 0.207800 +vn -0.064900 0.954700 0.290300 +vn -0.159200 0.966600 0.200700 +vn -0.091600 0.968000 0.233600 +vn 0.025800 0.938500 0.344400 +vn 0.129300 0.861000 0.491900 +vn 0.636100 -0.569900 0.520200 +vn 0.581800 -0.606800 0.541600 +vn 0.574700 -0.650800 0.496100 +vn 0.633600 -0.611000 0.474600 +vn 0.511200 -0.654700 0.556800 +vn 0.502200 -0.692900 0.517300 +vn 0.675000 -0.580600 0.455200 +vn 0.672700 -0.548200 0.496900 +vn 0.665000 -0.417100 0.619500 +vn 0.640000 -0.413300 0.647600 +vn 0.610200 -0.191300 0.768700 +vn 0.600100 -0.430400 0.674300 +vn 0.544700 -0.472400 0.692900 +vn 0.592300 -0.142300 0.793000 +vn 0.565100 -0.110000 0.817700 +vn 0.531400 -0.108700 0.840100 +vn -0.636100 -0.569900 0.520200 +vn -0.640000 -0.413300 0.647600 +vn -0.665000 -0.417100 0.619500 +vn -0.672700 -0.548200 0.496900 +vn -0.592300 -0.142300 0.793000 +vn -0.610200 -0.191300 0.768700 +vn -0.675000 -0.580600 0.455200 +vn -0.633600 -0.611000 0.474600 +vn -0.574700 -0.650800 0.496100 +vn -0.581800 -0.606800 0.541600 +vn -0.502200 -0.692900 0.517300 +vn -0.600100 -0.430400 0.674300 +vn -0.565100 -0.110000 0.817700 +vn -0.511200 -0.654700 0.556800 +vn -0.544700 -0.472400 0.692900 +vn -0.531400 -0.108700 0.840100 +vn 0.307900 -0.759600 0.572800 +vn 0.140300 -0.786000 0.602000 +vn 0.139800 -0.806300 0.574700 +vn 0.308500 -0.770500 0.557800 +vn -0.121500 -0.768900 0.627600 +vn -0.149800 -0.816500 0.557600 +vn 0.417900 -0.732400 0.537600 +vn 0.423800 -0.709400 0.563000 +vn 0.469800 -0.543700 0.695400 +vn 0.361300 -0.603500 0.710800 +vn 0.493400 -0.156900 0.855500 +vn 0.222600 -0.570300 0.790600 +vn 0.076400 -0.472400 0.878000 +vn 0.422600 -0.185500 0.887100 +vn 0.348600 -0.103500 0.931500 +vn 0.323100 -0.008200 0.946300 +vn -0.307900 -0.759600 0.572800 +vn -0.361300 -0.603500 0.710800 +vn -0.469800 -0.543700 0.695400 +vn -0.423800 -0.709400 0.563000 +vn -0.422600 -0.185500 0.887100 +vn -0.493400 -0.156900 0.855500 +vn -0.417900 -0.732400 0.537600 +vn -0.308500 -0.770500 0.557800 +vn -0.139800 -0.806300 0.574700 +vn -0.140300 -0.786000 0.602000 +vn 0.149800 -0.816500 0.557600 +vn -0.222600 -0.570300 0.790600 +vn -0.348700 -0.103500 0.931500 +vn 0.121500 -0.768900 0.627600 +vn -0.076400 -0.472400 0.878000 +vn -0.323100 -0.008200 0.946300 +vn -0.697400 -0.506700 0.506800 +vn -0.873000 -0.296200 0.387500 +vn -0.948800 -0.302300 0.091700 +vn -0.796200 -0.539800 0.273100 +vn -0.956700 -0.076400 0.280900 +vn -0.996300 -0.073100 -0.045100 +vn -0.511400 -0.731600 0.450700 +vn -0.433200 -0.675200 0.596900 +vn -0.084800 -0.363300 0.927800 +vn 0.309500 0.056000 0.949200 +vn 0.317600 0.075900 0.945200 +vn -0.227700 -0.250700 0.940900 +vn -0.339100 -0.131300 0.931500 +vn -0.396400 -0.018000 0.917900 +vn 0.332700 0.064000 0.940800 +vn 0.349800 0.019800 0.936600 +vn 0.697400 -0.506700 0.506800 +vn 0.227700 -0.250700 0.940900 +vn 0.084800 -0.363300 0.927800 +vn 0.433200 -0.675200 0.596900 +vn -0.317600 0.075900 0.945200 +vn -0.309500 0.056000 0.949200 +vn 0.511400 -0.731600 0.450700 +vn 0.796200 -0.539800 0.273100 +vn 0.948800 -0.302300 0.091700 +vn 0.873000 -0.296200 0.387500 +vn 0.996300 -0.073100 -0.045100 +vn 0.339100 -0.131300 0.931500 +vn -0.332700 0.064000 0.940800 +vn 0.956700 -0.076400 0.280900 +vn 0.396400 -0.018000 0.917900 +vn -0.349800 0.019800 0.936600 +vn -0.817300 0.495500 0.293900 +vn -0.882300 0.452900 -0.128400 +vn -0.944300 0.293800 -0.148000 +vn -0.671700 0.650500 0.354400 +vn -0.786600 0.613600 -0.069100 +vn -0.984500 0.124500 -0.123500 +vn -0.963600 0.129700 0.233600 +vn -0.915800 0.319900 0.242900 +vn -0.384700 0.088500 0.918800 +vn -0.324700 0.188100 0.926900 +vn 0.358300 -0.025400 0.933200 +vn -0.232100 0.281200 0.931100 +vn -0.129500 0.363500 0.922500 +vn 0.353700 -0.047400 0.934100 +vn 0.344500 -0.045400 0.937700 +vn 0.338500 -0.019700 0.940700 +vn 0.915800 0.319900 0.242900 +vn 0.324700 0.188100 0.926900 +vn 0.384700 0.088500 0.918800 +vn 0.963600 0.129700 0.233600 +vn -0.353700 -0.047400 0.934100 +vn -0.358300 -0.025400 0.933200 +vn 0.984500 0.124500 -0.123500 +vn 0.944300 0.293800 -0.148000 +vn 0.882300 0.452900 -0.128400 +vn 0.817300 0.495500 0.293900 +vn 0.786600 0.613600 -0.069100 +vn 0.671700 0.650500 0.354400 +vn 0.232100 0.281200 0.931100 +vn -0.344500 -0.045400 0.937700 +vn 0.129500 0.363500 0.922500 +vn -0.338500 -0.019700 0.940700 +vn -0.179600 0.889800 0.419400 +vn -0.342500 0.923300 0.173700 +vn -0.501400 0.859000 0.102900 +vn -0.030900 0.910000 0.413400 +vn -0.177600 0.956800 0.230000 +vn -0.333600 0.846100 0.415600 +vn -0.652900 0.757200 0.017500 +vn -0.499100 0.771100 0.395200 +vn -0.025100 0.446600 0.894400 +vn 0.061700 0.525600 0.848500 +vn 0.343400 0.029500 0.938700 +vn 0.150900 0.602300 0.783900 +vn 0.244200 0.660300 0.710100 +vn 0.361700 0.095800 0.927300 +vn 0.403200 0.181000 0.897000 +vn 0.453000 0.273100 0.848600 +vn -0.061700 0.525600 0.848500 +vn 0.025100 0.446600 0.894400 +vn 0.499100 0.771100 0.395200 +vn -0.361700 0.095800 0.927300 +vn -0.343400 0.029500 0.938700 +vn 0.333600 0.846100 0.415600 +vn 0.652900 0.757200 0.017500 +vn 0.501400 0.859000 0.102900 +vn 0.342500 0.923300 0.173700 +vn 0.179600 0.889800 0.419400 +vn 0.177600 0.956800 0.230000 +vn 0.030900 0.910000 0.413400 +vn -0.150900 0.602300 0.783900 +vn -0.403200 0.181000 0.897000 +vn -0.244200 0.660300 0.710100 +vn -0.453000 0.273100 0.848600 +vn 0.137400 0.919400 0.368400 +vn 0.103900 0.944700 0.311100 +vn 0.069100 0.950700 0.302300 +vn 0.141900 0.910100 0.389400 +vn -0.029400 0.961300 0.273900 +vn 0.084500 0.911600 0.402400 +vn 0.311400 0.701300 0.641300 +vn 0.315700 0.742600 0.590600 +vn 0.493000 0.361900 0.791100 +vn 0.475700 0.446500 0.757800 +vn 0.252300 0.800800 0.543100 +vn 0.383000 0.561000 0.733800 +vn -0.315700 0.742600 0.590600 +vn -0.311400 0.701300 0.641300 +vn -0.084500 0.911600 0.402400 +vn -0.475700 0.446500 0.757800 +vn -0.493000 0.361900 0.791100 +vn -0.141900 0.910100 0.389400 +vn 0.029400 0.961300 0.273900 +vn -0.069100 0.950700 0.302300 +vn -0.103900 0.944700 0.311100 +vn -0.137400 0.919400 0.368400 +vn -0.252300 0.800800 0.543100 +vn -0.383000 0.561000 0.733800 +vn 0.198900 0.089400 0.975900 +vn 0.282300 0.165900 0.944900 +vn 0.288200 0.148600 0.946000 +vn 0.180000 0.082700 0.980200 +vn 0.249900 0.192000 0.949000 +vn 0.263900 0.139200 0.954400 +vn 0.157200 0.157900 0.974900 +vn 0.188500 0.179400 0.965500 +vn 0.076800 0.255400 0.963800 +vn 0.194200 0.111800 0.974500 +vn 0.142900 0.166000 0.975700 +vn 0.175800 0.093100 0.980000 +vn 0.205500 0.156100 0.966100 +vn 0.089400 0.270900 0.958400 +vn 0.205500 0.230100 0.951200 +vn 0.182200 0.051500 0.981900 +vn 0.169300 0.029000 0.985100 +vn 0.214900 0.084300 0.973000 +vn 0.211800 0.040800 0.976400 +vn 0.200400 0.011000 0.979600 +vn -0.194200 0.111800 0.974500 +vn -0.175800 0.093100 0.980000 +vn -0.142900 0.166000 0.975700 +vn -0.157200 0.157900 0.974900 +vn -0.089400 0.270900 0.958400 +vn -0.214900 0.084300 0.973000 +vn -0.205500 0.156100 0.966100 +vn -0.205500 0.230100 0.951200 +vn -0.263900 0.139200 0.954400 +vn -0.288200 0.148600 0.946000 +vn -0.076800 0.255400 0.963800 +vn -0.188500 0.179400 0.965500 +vn -0.282300 0.165900 0.944900 +vn -0.198900 0.089400 0.975900 +vn -0.249900 0.192000 0.949000 +vn -0.180000 0.082700 0.980200 +vn -0.182200 0.051500 0.981900 +vn -0.211800 0.040800 0.976400 +vn -0.169300 0.029000 0.985100 +vn -0.200400 0.011000 0.979600 +vn 0.307400 0.024800 0.951200 +vn 0.260000 0.036400 0.964900 +vn 0.276300 0.068200 0.958600 +vn 0.290100 -0.001300 0.957000 +vn 0.248400 0.006100 0.968600 +vn 0.292200 0.119200 0.948900 +vn 0.360000 0.068700 0.930400 +vn 0.334200 0.154000 0.929800 +vn 0.418700 0.076700 0.904900 +vn 0.328300 0.047400 0.943400 +vn 0.402400 0.018700 0.915200 +vn 0.358700 0.017800 0.933300 +vn 0.415600 -0.025800 0.909100 +vn 0.460800 0.010600 0.887400 +vn 0.460000 -0.045600 0.886700 +vn 0.337400 0.005800 0.941300 +vn 0.323800 -0.008000 0.946100 +vn 0.375300 -0.009600 0.926800 +vn 0.354400 -0.004600 0.935100 +vn 0.347600 -0.012100 0.937500 +vn -0.328300 0.047400 0.943400 +vn -0.358700 0.017800 0.933300 +vn -0.402400 0.018700 0.915200 +vn -0.360000 0.068700 0.930400 +vn -0.460800 0.010600 0.887400 +vn -0.375300 -0.009600 0.926800 +vn -0.415600 -0.025800 0.909100 +vn -0.460000 -0.045600 0.886700 +vn -0.292200 0.119200 0.948900 +vn -0.276300 0.068200 0.958600 +vn -0.418700 0.076700 0.904900 +vn -0.334200 0.154000 0.929800 +vn -0.260000 0.036400 0.964900 +vn -0.307400 0.024800 0.951200 +vn -0.248400 0.006100 0.968600 +vn -0.290100 -0.001300 0.957000 +vn -0.337400 0.005800 0.941300 +vn -0.354400 -0.004600 0.935100 +vn -0.323800 -0.008000 0.946100 +vn -0.347600 -0.012100 0.937500 +vn 0.362900 -0.036300 0.931100 +vn 0.349300 -0.008800 0.936900 +vn 0.357000 -0.009100 0.934000 +vn 0.374500 -0.027400 0.926800 +vn 0.359900 -0.006800 0.933000 +vn 0.359900 -0.012100 0.932900 +vn 0.407300 -0.056700 0.911500 +vn 0.392300 -0.076300 0.916700 +vn 0.445700 -0.085300 0.891100 +vn 0.369400 -0.096600 0.924200 +vn 0.339100 -0.044800 0.939700 +vn 0.343400 -0.120000 0.931500 +vn 0.308200 -0.052900 0.949800 +vn 0.426800 -0.116900 0.896700 +vn 0.406000 -0.144500 0.902300 +vn 0.389000 -0.174400 0.904500 +vn 0.328900 -0.006400 0.944300 +vn 0.347000 0.005900 0.937800 +vn 0.297900 0.002900 0.954600 +vn 0.321200 0.031000 0.946500 +vn -0.339100 -0.044800 0.939700 +vn -0.369400 -0.096600 0.924200 +vn -0.392300 -0.076300 0.916700 +vn -0.406000 -0.144500 0.902300 +vn -0.426800 -0.116900 0.896700 +vn -0.308200 -0.052900 0.949800 +vn -0.343400 -0.120000 0.931500 +vn -0.389000 -0.174400 0.904500 +vn -0.407300 -0.056700 0.911500 +vn -0.374500 -0.027400 0.926800 +vn -0.445700 -0.085300 0.891100 +vn -0.362900 -0.036300 0.931100 +vn -0.357000 -0.009100 0.934000 +vn -0.359900 -0.012100 0.932900 +vn -0.359900 -0.006800 0.933000 +vn -0.349300 -0.008800 0.936900 +vn -0.328900 -0.006400 0.944300 +vn -0.297900 0.002900 0.954600 +vn -0.347000 0.005900 0.937800 +vn -0.321200 0.031000 0.946500 +vn 0.296000 0.101000 0.949800 +vn 0.278500 0.036300 0.959700 +vn 0.294200 -0.040000 0.954900 +vn 0.300700 0.163000 0.939700 +vn 0.298100 0.083700 0.950800 +vn 0.319800 0.011100 0.947400 +vn 0.336500 -0.125700 0.933200 +vn 0.366700 -0.087700 0.926200 +vn 0.392200 -0.188100 0.900400 +vn 0.419800 -0.017800 0.907400 +vn 0.376000 0.082700 0.922900 +vn 0.477900 0.052900 0.876800 +vn 0.442400 0.150500 0.884100 +vn 0.424500 -0.157400 0.891600 +vn 0.467600 -0.089800 0.879400 +vn 0.501000 -0.021900 0.865200 +vn 0.344000 0.174400 0.922600 +vn 0.328700 0.243400 0.912500 +vn 0.406800 0.236400 0.882400 +vn 0.373600 0.306600 0.875400 +vn -0.376000 0.082700 0.922900 +vn -0.419800 -0.017800 0.907400 +vn -0.366700 -0.087700 0.926200 +vn -0.467600 -0.089800 0.879400 +vn -0.424500 -0.157400 0.891600 +vn -0.442400 0.150500 0.884100 +vn -0.477900 0.052900 0.876800 +vn -0.501000 -0.021900 0.865200 +vn -0.319800 0.011100 0.947400 +vn -0.336500 -0.125700 0.933200 +vn -0.294200 -0.040000 0.954900 +vn -0.392200 -0.188100 0.900400 +vn -0.278500 0.036300 0.959700 +vn -0.296000 0.101000 0.949800 +vn -0.298100 0.083700 0.950800 +vn -0.300700 0.163000 0.939700 +vn -0.344000 0.174400 0.922600 +vn -0.406800 0.236400 0.882400 +vn -0.328700 0.243400 0.912500 +vn -0.373600 0.306600 0.875400 +vn 0.402300 0.178300 0.897900 +vn 0.386000 0.268300 0.882600 +vn 0.467500 0.164400 0.868600 +vn 0.491600 0.087600 0.866400 +vn 0.379200 0.335100 0.862500 +vn 0.446100 0.215500 0.868600 +vn 0.511400 0.003100 0.859300 +vn 0.422000 0.084100 0.902700 +vn 0.520100 -0.073700 0.850900 +vn 0.438100 0.006300 0.898900 +vn 0.349600 0.101400 0.931400 +vn 0.337200 0.192000 0.921600 +vn 0.371700 0.033400 0.927700 +vn 0.339400 0.280700 0.897700 +vn 0.357100 0.348500 0.866600 +vn -0.402300 0.178300 0.897900 +vn -0.337200 0.192000 0.921600 +vn -0.349600 0.101400 0.931400 +vn -0.422000 0.084100 0.902700 +vn -0.371700 0.033400 0.927700 +vn -0.511400 0.003100 0.859300 +vn -0.491600 0.087600 0.866400 +vn -0.438100 0.006300 0.898900 +vn -0.520100 -0.073700 0.850900 +vn -0.467500 0.164400 0.868600 +vn -0.386000 0.268300 0.882600 +vn -0.446100 0.215500 0.868600 +vn -0.339400 0.280700 0.897700 +vn -0.379200 0.335100 0.862500 +vn -0.357100 0.348500 0.866600 +vn 0.499300 -0.165500 0.850500 +vn 0.476900 -0.188400 0.858500 +vn 0.414200 -0.095600 0.905100 +vn 0.447400 -0.203400 0.870800 +vn 0.386200 -0.106400 0.916300 +vn 0.432900 -0.076800 0.898100 +vn 0.378100 -0.040300 0.924900 +vn 0.388500 -0.028400 0.921000 +vn 0.356600 -0.045500 0.933100 +vn 0.386600 -0.006300 0.922200 +vn 0.441100 -0.045500 0.896300 +vn 0.513800 -0.129900 0.848000 +vn -0.441100 -0.045500 0.896300 +vn -0.386600 -0.006300 0.922200 +vn -0.388500 -0.028400 0.921000 +vn -0.432900 -0.076800 0.898100 +vn -0.378100 -0.040300 0.924900 +vn -0.414200 -0.095600 0.905100 +vn -0.356600 -0.045500 0.933100 +vn -0.476900 -0.188400 0.858500 +vn -0.499300 -0.165500 0.850500 +vn -0.386200 -0.106400 0.916300 +vn -0.447400 -0.203400 0.870800 +vn -0.513800 -0.129900 0.848000 +vn 0.301300 -0.111900 0.946900 +vn 0.350200 -0.111600 0.930000 +vn 0.407500 -0.213500 0.887800 +vn 0.364900 -0.220000 0.904600 +vn 0.327400 -0.214100 0.920300 +vn 0.283200 -0.212500 0.935200 +vn 0.210000 -0.100300 0.972500 +vn 0.259000 -0.100900 0.960600 +vn 0.240000 -0.034700 0.970100 +vn 0.286300 -0.041700 0.957200 +vn 0.189800 -0.027100 0.981400 +vn 0.325700 -0.045700 0.944300 +vn -0.301300 -0.111900 0.946900 +vn -0.286300 -0.041700 0.957200 +vn -0.240000 -0.034700 0.970100 +vn -0.259000 -0.100900 0.960600 +vn -0.189800 -0.027100 0.981400 +vn -0.327400 -0.214100 0.920300 +vn -0.364900 -0.220000 0.904600 +vn -0.210000 -0.100300 0.972500 +vn -0.283200 -0.212500 0.935200 +vn -0.407500 -0.213500 0.887800 +vn -0.350200 -0.111600 0.930000 +vn -0.325700 -0.045700 0.944300 +vn 0.189900 -0.021600 0.981500 +vn 0.249600 0.100600 0.963100 +vn 0.222800 0.183800 0.957400 +vn 0.164500 0.055800 0.984800 +vn 0.157800 -0.004900 0.987500 +vn 0.181400 -0.079300 0.980200 +vn 0.264000 -0.202100 0.943100 +vn 0.282600 -0.153700 0.946800 +vn 0.347700 -0.050900 0.936200 +vn -0.189900 -0.021600 0.981500 +vn -0.282600 -0.153700 0.946800 +vn -0.264000 -0.202100 0.943100 +vn -0.181400 -0.079300 0.980200 +vn -0.157800 -0.004900 0.987500 +vn -0.164500 0.055800 0.984800 +vn -0.222800 0.183800 0.957400 +vn -0.249600 0.100600 0.963100 +vn -0.347700 -0.050900 0.936200 +vn -0.076900 0.526800 0.846500 +vn -0.161400 0.732000 0.661900 +vn -0.161800 0.728800 0.665400 +vn -0.127900 0.578600 0.805500 +vn -0.034200 0.669600 0.741900 +vn -0.015700 0.534700 0.844800 +vn 0.218100 0.524900 0.822700 +vn 0.028600 0.400300 0.915900 +vn -0.031500 0.405800 0.913400 +vn 0.220100 0.401100 0.889200 +vn 0.210500 0.310400 0.927000 +vn 0.066700 0.308300 0.948900 +vn 0.031500 0.405800 0.913400 +vn -0.028600 0.400300 0.915900 +vn 0.015700 0.534700 0.844800 +vn -0.210500 0.310400 0.927000 +vn -0.220100 0.401100 0.889200 +vn 0.127900 0.578600 0.805500 +vn 0.034200 0.669600 0.741900 +vn 0.161800 0.728800 0.665400 +vn -0.218100 0.524900 0.822700 +vn 0.161400 0.732000 0.661900 +vn 0.076900 0.526800 0.846500 +vn -0.066700 0.308300 0.948900 +vn 0.518100 0.125200 0.846100 +vn 0.530100 0.217400 0.819600 +vn 0.549000 0.133600 0.825100 +vn 0.542700 0.047500 0.838500 +vn 0.511900 0.052700 0.857400 +vn 0.512900 0.013400 0.858300 +vn 0.478400 0.087600 0.873700 +vn 0.514800 -0.024800 0.856900 +vn 0.497500 -0.053400 0.865800 +vn 0.384700 0.187000 0.903900 +vn 0.425400 0.237700 0.873200 +vn 0.438500 0.339900 0.831900 +vn -0.518100 0.125200 0.846100 +vn -0.425400 0.237700 0.873200 +vn -0.384700 0.187000 0.903900 +vn -0.478400 0.087600 0.873700 +vn -0.512900 0.013400 0.858300 +vn -0.542700 0.047500 0.838500 +vn -0.497500 -0.053400 0.865800 +vn -0.514800 -0.024800 0.856900 +vn -0.549000 0.133600 0.825100 +vn -0.530100 0.217400 0.819600 +vn -0.511900 0.052700 0.857400 +vn -0.438500 0.339900 0.831900 +vn 0.441600 -0.073900 0.894200 +vn 0.427400 -0.115700 0.896600 +vn 0.438800 -0.157100 0.884700 +vn 0.431400 -0.137400 0.891600 +vn 0.440200 -0.176300 0.880400 +vn 0.453400 -0.124700 0.882500 +vn 0.431400 -0.166800 0.886600 +vn 0.448600 -0.136800 0.883200 +vn 0.425300 -0.192100 0.884400 +vn 0.474300 -0.098900 0.874800 +vn 0.481900 -0.079500 0.872600 +vn 0.472700 -0.015600 0.881100 +vn -0.481900 -0.079500 0.872600 +vn -0.474300 -0.098900 0.874800 +vn -0.448600 -0.136800 0.883200 +vn -0.453400 -0.124700 0.882500 +vn -0.431400 -0.166800 0.886600 +vn -0.438800 -0.157100 0.884700 +vn -0.425300 -0.192100 0.884400 +vn -0.427400 -0.115700 0.896600 +vn -0.441600 -0.073900 0.894200 +vn -0.440200 -0.176300 0.880400 +vn -0.431400 -0.137400 0.891600 +vn -0.472700 -0.015600 0.881100 +vn 0.477100 -0.115300 0.871200 +vn 0.500200 -0.062600 0.863600 +vn 0.515300 -0.088100 0.852400 +vn 0.507800 0.023800 0.861100 +vn 0.527500 0.010600 0.849500 +vn 0.488000 -0.149100 0.860000 +vn 0.501200 -0.105300 0.858900 +vn 0.469700 -0.170800 0.866100 +vn 0.518800 -0.006800 0.854800 +vn 0.438700 -0.200300 0.876000 +vn 0.459100 -0.176700 0.870600 +vn 0.451000 -0.138200 0.881700 +vn -0.459100 -0.176700 0.870600 +vn -0.438700 -0.200300 0.876000 +vn -0.469700 -0.170800 0.866100 +vn -0.488000 -0.149100 0.860000 +vn -0.501200 -0.105300 0.858900 +vn -0.515300 -0.088100 0.852400 +vn -0.518800 -0.006800 0.854800 +vn -0.500200 -0.062600 0.863600 +vn -0.477100 -0.115300 0.871200 +vn -0.527500 0.010600 0.849500 +vn -0.507800 0.023800 0.861100 +vn -0.451000 -0.138200 0.881700 +vn 0.497200 0.203300 0.843500 +vn 0.469900 0.188600 0.862300 +vn 0.441100 0.240000 0.864700 +vn 0.464800 0.273400 0.842200 +vn 0.412900 0.267100 0.870700 +vn 0.452700 0.269100 0.850100 +vn 0.487600 0.191800 0.851700 +vn 0.426000 0.330900 0.842000 +vn 0.412000 0.335600 0.847100 +vn 0.514100 0.098000 0.852100 +vn 0.519500 0.116700 0.846400 +vn 0.494400 0.117600 0.861200 +vn -0.497200 0.203300 0.843500 +vn -0.519500 0.116700 0.846400 +vn -0.514100 0.098000 0.852100 +vn -0.487600 0.191800 0.851700 +vn -0.452700 0.269100 0.850100 +vn -0.464800 0.273400 0.842200 +vn -0.412000 0.335600 0.847100 +vn -0.426000 0.330900 0.842000 +vn -0.441100 0.240000 0.864700 +vn -0.469900 0.188600 0.862300 +vn -0.412900 0.267100 0.870700 +vn -0.494400 0.117600 0.861200 +vn 0.388300 0.362800 0.847100 +vn 0.411500 0.220200 0.884400 +vn 0.437700 0.220700 0.871600 +vn 0.385200 0.361100 0.849200 +vn 0.380500 0.377000 0.844400 +vn 0.393700 0.363900 0.844100 +vn 0.396600 0.256700 0.881300 +vn -0.411500 0.220200 0.884400 +vn -0.396600 0.256700 0.881300 +vn -0.393700 0.363900 0.844100 +vn -0.388300 0.362800 0.847100 +vn -0.380500 0.377000 0.844400 +vn -0.385200 0.361100 0.849200 +vn -0.437700 0.220700 0.871600 +vn -0.078000 -0.908000 -0.411600 +vn -0.003500 -0.903100 -0.429300 +vn 0.226600 -0.972600 -0.052400 +vn 0.134900 -0.989300 -0.055300 +vn 0.104600 -0.889800 -0.444200 +vn 0.325500 -0.944500 -0.044500 +vn 0.043300 -0.997000 -0.063700 +vn -0.126700 -0.908000 -0.399200 +vn -0.050000 -0.995300 -0.082700 +vn -0.150700 -0.906300 -0.394700 +vn -0.265500 -0.667200 -0.696000 +vn -0.271000 -0.647000 -0.712600 +vn -0.332500 -0.373100 -0.866100 +vn -0.204100 -0.701400 -0.682900 +vn -0.196000 -0.431500 -0.880500 +vn -0.226400 -0.635900 -0.737800 +vn -0.127000 -0.627500 -0.768200 +vn -0.377900 -0.342600 -0.860100 +vn -0.357600 -0.328800 -0.874100 +vn -0.275700 -0.322900 -0.905400 +vn 0.078000 -0.908000 -0.411600 +vn 0.271000 -0.647000 -0.712600 +vn 0.265500 -0.667200 -0.696000 +vn 0.126700 -0.908000 -0.399200 +vn 0.204100 -0.701400 -0.682900 +vn 0.377900 -0.342600 -0.860100 +vn 0.332500 -0.373100 -0.866100 +vn 0.196000 -0.431500 -0.880500 +vn -0.043300 -0.997000 -0.063700 +vn -0.134900 -0.989300 -0.055300 +vn 0.150700 -0.906300 -0.394700 +vn 0.050000 -0.995300 -0.082700 +vn -0.226600 -0.972600 -0.052400 +vn 0.003500 -0.903100 -0.429300 +vn -0.325500 -0.944500 -0.044500 +vn 0.226400 -0.635900 -0.737800 +vn 0.357600 -0.328800 -0.874100 +vn -0.104600 -0.889800 -0.444200 +vn 0.127000 -0.627500 -0.768200 +vn 0.275700 -0.322900 -0.905400 +vn 0.397500 -0.827300 -0.396900 +vn 0.571100 -0.761000 -0.307700 +vn 0.677300 -0.720300 0.149700 +vn 0.559000 -0.827500 0.052300 +vn 0.749800 -0.642800 -0.156800 +vn 0.776100 -0.570500 0.268600 +vn 0.438800 -0.898500 -0.011200 +vn 0.243800 -0.866000 -0.436600 +vn 0.006100 -0.617100 -0.786800 +vn 0.150700 -0.604300 -0.782300 +vn -0.165800 -0.318100 -0.933400 +vn 0.329800 -0.587300 -0.739100 +vn 0.558900 -0.543400 -0.626400 +vn -0.056500 -0.315600 -0.947200 +vn 0.074200 -0.319300 -0.944700 +vn 0.266100 -0.323300 -0.908100 +vn -0.397500 -0.827300 -0.396900 +vn -0.150700 -0.604300 -0.782300 +vn -0.006100 -0.617100 -0.786800 +vn -0.243800 -0.866000 -0.436600 +vn 0.056500 -0.315600 -0.947200 +vn 0.165800 -0.318100 -0.933400 +vn -0.438800 -0.898500 -0.011200 +vn -0.559000 -0.827500 0.052300 +vn -0.677300 -0.720300 0.149700 +vn -0.571100 -0.761000 -0.307700 +vn -0.776100 -0.570500 0.268600 +vn -0.329800 -0.587300 -0.739100 +vn -0.074200 -0.319300 -0.944700 +vn -0.749800 -0.642800 -0.156800 +vn -0.558900 -0.543400 -0.626400 +vn -0.266100 -0.323300 -0.908100 +vn 0.945300 -0.281800 0.163800 +vn 0.979300 -0.074600 0.188000 +vn 0.890900 -0.078100 0.447400 +vn 0.865300 -0.244800 0.437400 +vn 0.984300 0.124700 0.124900 +vn 0.907000 0.112200 0.405900 +vn 0.835500 -0.402100 0.374500 +vn 0.881400 -0.471400 0.027900 +vn 0.782400 -0.447000 -0.433500 +vn 0.933300 -0.282600 -0.221200 +vn 0.517700 -0.298600 -0.801800 +vn 0.990300 -0.062800 -0.123600 +vn 0.978900 0.120800 -0.164700 +vn 0.759800 -0.192500 -0.621000 +vn 0.893100 -0.008100 -0.449800 +vn 0.920100 0.117100 -0.373700 +vn -0.945300 -0.281800 0.163800 +vn -0.933300 -0.282600 -0.221200 +vn -0.782500 -0.447000 -0.433500 +vn -0.881400 -0.471400 0.027900 +vn -0.759800 -0.192500 -0.621000 +vn -0.517700 -0.298600 -0.801800 +vn -0.835500 -0.402100 0.374500 +vn -0.865300 -0.244800 0.437400 +vn -0.890900 -0.078100 0.447400 +vn -0.907000 0.112200 0.405900 +vn -0.984300 0.124700 0.124900 +vn -0.979300 -0.074600 0.188000 +vn -0.990300 -0.062800 -0.123600 +vn -0.893100 -0.008100 -0.449800 +vn -0.978900 0.120800 -0.164700 +vn -0.920100 0.117100 -0.373700 +vn 0.590700 0.736900 -0.328500 +vn 0.660300 0.748900 0.056300 +vn 0.818000 0.537900 0.203400 +vn 0.264600 0.831900 -0.487700 +vn 0.389400 0.917200 -0.084100 +vn 0.818200 0.561300 -0.124400 +vn 0.889600 0.322900 0.322900 +vn 0.939000 0.342200 0.034100 +vn 0.913800 0.309800 -0.262700 +vn 0.736000 0.495200 -0.461600 +vn 0.829400 0.217200 -0.514600 +vn 0.587600 0.334400 -0.736800 +vn 0.446500 0.590200 -0.672400 +vn 0.117600 0.596100 -0.794200 +vn 0.265000 0.359400 -0.894800 +vn -0.008800 0.316600 -0.948500 +vn -0.736000 0.495200 -0.461600 +vn -0.913800 0.309800 -0.262700 +vn -0.939000 0.342200 0.034100 +vn -0.587600 0.334400 -0.736800 +vn -0.829400 0.217200 -0.514600 +vn -0.818200 0.561300 -0.124400 +vn -0.889600 0.322900 0.322900 +vn -0.818000 0.537900 0.203400 +vn -0.660300 0.748900 0.056300 +vn -0.590700 0.736900 -0.328500 +vn -0.389400 0.917200 -0.084100 +vn -0.264600 0.831900 -0.487700 +vn -0.446500 0.590200 -0.672400 +vn -0.265000 0.359400 -0.894800 +vn -0.117600 0.596100 -0.794200 +vn 0.008800 0.316600 -0.948500 +vn -0.354500 0.778400 -0.518100 +vn -0.251000 0.949700 -0.187000 +vn -0.116500 0.974500 -0.191700 +vn -0.427800 0.763200 -0.484100 +vn -0.332300 0.928300 -0.166600 +vn -0.236900 0.803900 -0.545500 +vn 0.096100 0.981500 -0.165200 +vn -0.041900 0.834800 -0.549000 +vn -0.144100 0.558000 -0.817200 +vn -0.295200 0.529400 -0.795300 +vn -0.190100 0.276200 -0.942100 +vn -0.294700 0.260000 -0.919500 +vn -0.392600 0.514100 -0.762600 +vn -0.457700 0.508200 -0.729500 +vn -0.372700 0.259000 -0.891000 +vn -0.436400 0.262300 -0.860700 +vn 0.295200 0.529400 -0.795300 +vn 0.144100 0.558000 -0.817200 +vn 0.041900 0.834800 -0.549000 +vn 0.294700 0.260000 -0.919500 +vn 0.190100 0.276200 -0.942100 +vn 0.236900 0.803900 -0.545500 +vn -0.096100 0.981500 -0.165200 +vn 0.116500 0.974500 -0.191700 +vn 0.251000 0.949700 -0.187000 +vn 0.354500 0.778400 -0.518100 +vn 0.332300 0.928300 -0.166600 +vn 0.427800 0.763200 -0.484100 +vn 0.392600 0.514100 -0.762600 +vn 0.372700 0.259000 -0.891000 +vn 0.457700 0.508200 -0.729500 +vn 0.436400 0.262300 -0.860700 +vn -0.563000 0.744900 -0.357900 +vn -0.509200 0.859400 -0.047000 +vn -0.447600 0.889300 -0.093600 +vn -0.607700 0.738000 -0.293200 +vn -0.583000 0.812500 -0.001400 +vn -0.525800 0.748500 -0.404100 +vn -0.390200 0.910900 -0.134500 +vn -0.478400 0.756100 -0.446500 +vn -0.505300 0.509000 -0.696800 +vn -0.536800 0.519500 -0.664800 +vn -0.484400 0.265000 -0.833700 +vn -0.543900 0.546000 -0.637200 +vn -0.537300 0.583800 -0.608700 +vn -0.496700 0.275100 -0.823200 +vn -0.465700 0.296900 -0.833600 +vn -0.364200 0.332400 -0.870000 +vn 0.536800 0.519500 -0.664800 +vn 0.505300 0.509000 -0.696800 +vn 0.478400 0.756100 -0.446500 +vn 0.496700 0.275100 -0.823200 +vn 0.484400 0.265000 -0.833700 +vn 0.525800 0.748500 -0.404100 +vn 0.390200 0.910900 -0.134500 +vn 0.447600 0.889300 -0.093600 +vn 0.509200 0.859400 -0.047000 +vn 0.563000 0.744900 -0.357900 +vn 0.583000 0.812500 -0.001400 +vn 0.607700 0.738000 -0.293200 +vn 0.543900 0.546000 -0.637200 +vn 0.465700 0.296900 -0.833600 +vn 0.537300 0.583800 -0.608700 +vn 0.364200 0.332400 -0.870000 +vn -0.454100 0.094300 -0.885900 +vn -0.452500 0.091100 -0.887100 +vn -0.430200 -0.017300 -0.902600 +vn -0.401100 0.089700 -0.911600 +vn -0.371900 -0.017000 -0.928100 +vn -0.430800 -0.019900 -0.902200 +vn -0.408700 -0.129600 -0.903400 +vn -0.414900 -0.137700 -0.899400 +vn -0.341100 -0.127100 -0.931400 +vn -0.347700 -0.159500 -0.923900 +vn -0.357600 -0.030500 -0.933300 +vn -0.162100 -0.209700 -0.964200 +vn -0.154400 -0.064600 -0.985900 +vn -0.390300 0.097600 -0.915500 +vn -0.205200 0.089300 -0.974600 +vn 0.357600 -0.030500 -0.933300 +vn 0.347700 -0.159500 -0.923900 +vn 0.414900 -0.137700 -0.899400 +vn 0.154400 -0.064600 -0.985900 +vn 0.162100 -0.209700 -0.964200 +vn 0.430800 -0.019900 -0.902200 +vn 0.408700 -0.129600 -0.903400 +vn 0.430200 -0.017300 -0.902600 +vn 0.341100 -0.127100 -0.931400 +vn 0.452500 0.091100 -0.887100 +vn 0.454100 0.094300 -0.885900 +vn 0.371900 -0.017000 -0.928100 +vn 0.401100 0.089700 -0.911600 +vn 0.390300 0.097600 -0.915500 +vn 0.205200 0.089300 -0.974600 +vn -0.222200 -0.022600 -0.974700 +vn -0.292800 -0.018600 -0.956000 +vn -0.331000 0.087300 -0.939500 +vn -0.263700 0.084200 -0.960900 +vn -0.187500 0.086800 -0.978400 +vn -0.150200 -0.027200 -0.988300 +vn -0.075200 0.108600 -0.991200 +vn -0.058900 -0.028700 -0.997800 +vn -0.077200 -0.135800 -0.987700 +vn -0.166400 -0.128900 -0.977600 +vn 0.044800 -0.146600 -0.988200 +vn -0.249700 -0.126600 -0.960000 +vn 0.222200 -0.022600 -0.974700 +vn 0.166400 -0.128900 -0.977600 +vn 0.077200 -0.135800 -0.987700 +vn 0.150200 -0.027200 -0.988300 +vn -0.044800 -0.146600 -0.988200 +vn 0.187500 0.086800 -0.978400 +vn 0.263700 0.084200 -0.960900 +vn 0.058900 -0.028700 -0.997800 +vn 0.075200 0.108600 -0.991200 +vn 0.331000 0.087300 -0.939500 +vn 0.292800 -0.018600 -0.956000 +vn 0.249700 -0.126600 -0.960000 +vn 0.109000 -0.011000 -0.994000 +vn 0.119000 0.153000 -0.981000 +vn 0.442900 0.155600 -0.883000 +vn 0.333300 0.025400 -0.942500 +vn 0.718700 0.080400 -0.690600 +vn 0.524400 -0.065400 -0.849000 +vn 0.242600 -0.145500 -0.959100 +vn -0.109000 -0.011000 -0.994000 +vn -0.242600 -0.145500 -0.959100 +vn -0.524400 -0.065400 -0.849000 +vn -0.333300 0.025400 -0.942500 +vn -0.718700 0.080400 -0.690600 +vn -0.442900 0.155600 -0.883000 +vn -0.119000 0.153000 -0.981000 +vn -0.193700 0.714800 -0.671900 +vn 0.349700 0.578800 -0.736600 +vn 0.121700 0.900800 -0.416900 +vn 0.243400 0.306600 -0.920200 +vn 0.575700 0.168800 -0.800000 +vn -0.479000 0.832500 -0.278500 +vn 0.068300 0.987300 -0.143200 +vn -0.520600 0.853500 -0.023900 +vn -0.615600 0.788000 0.010200 +vn -0.613000 0.748300 -0.253500 +vn -0.466700 0.639900 -0.610500 +vn -0.131300 0.355100 -0.925500 +vn 0.613000 0.748300 -0.253500 +vn 0.615600 0.788000 0.010200 +vn 0.520600 0.853500 -0.023900 +vn 0.479000 0.832500 -0.278500 +vn -0.068300 0.987300 -0.143200 +vn -0.121700 0.900800 -0.416900 +vn -0.349700 0.578800 -0.736600 +vn 0.193700 0.714800 -0.671900 +vn -0.575700 0.168800 -0.800000 +vn -0.243400 0.306600 -0.920200 +vn 0.466700 0.639900 -0.610500 +vn 0.131300 0.355100 -0.925500 +vn 0.380500 -0.187200 -0.905600 +vn 0.510000 -0.240000 -0.826000 +vn 0.562000 -0.086100 -0.822600 +vn 0.393000 -0.022400 -0.919200 +vn 0.102700 0.046800 -0.993600 +vn 0.141200 -0.124000 -0.982200 +vn 0.087500 -0.281600 -0.955500 +vn -0.021000 -0.492200 -0.870200 +vn 0.143100 -0.542800 -0.827600 +vn 0.292600 -0.350900 -0.889500 +vn 0.406000 -0.401400 -0.820900 +vn 0.250900 -0.579300 -0.775500 +vn -0.380500 -0.187200 -0.905600 +vn -0.292600 -0.350900 -0.889500 +vn -0.087500 -0.281600 -0.955500 +vn -0.141200 -0.124000 -0.982200 +vn -0.143100 -0.542800 -0.827600 +vn 0.021000 -0.492200 -0.870200 +vn -0.102700 0.046800 -0.993600 +vn -0.393000 -0.022400 -0.919200 +vn -0.562000 -0.086100 -0.822600 +vn -0.510000 -0.240000 -0.826000 +vn -0.406000 -0.401400 -0.820900 +vn -0.250900 -0.579300 -0.775500 +vn -0.112200 -0.893000 -0.435900 +vn -0.133300 -0.974300 -0.181500 +vn -0.079000 -0.973400 -0.215200 +vn -0.033300 -0.896000 -0.442800 +vn 0.081400 -0.767200 -0.636200 +vn -0.015200 -0.750000 -0.661300 +vn -0.116100 -0.727900 -0.675700 +vn -0.144100 -0.898500 -0.414600 +vn -0.107700 -0.985700 -0.129600 +vn 0.112200 -0.893000 -0.435900 +vn 0.144100 -0.898500 -0.414600 +vn 0.116100 -0.727900 -0.675700 +vn 0.015200 -0.750000 -0.661300 +vn -0.081400 -0.767200 -0.636200 +vn 0.033300 -0.896000 -0.442800 +vn 0.079000 -0.973400 -0.215200 +vn 0.133300 -0.974300 -0.181500 +vn 0.107700 -0.985700 -0.129600 +s 1 +f 1/1/1 5/2/2 6/3/3 +f 2/4/4 6/3/3 4440/5/5 +f 5/2/2 4438/6/6 4439/7/7 +f 6/3/3 4439/7/7 7498/8/8 +f 2/4/4 7/9/9 3/10/10 +f 7/9/9 4443/11/11 4444/12/12 +f 4441/13/13 4442/14/14 7/9/9 +f 4442/14/14 7452/15/15 4443/11/11 +f 1/1/1 3/10/10 8/16/16 +f 4/17/17 8/16/16 4446/18/18 +f 3/10/10 4444/12/12 4445/19/19 +f 8/16/16 4445/19/19 7454/20/20 +f 4/17/17 9/21/21 5/2/2 +f 9/21/21 4437/22/22 4438/6/6 +f 4447/23/23 4448/24/24 9/21/21 +f 4448/24/24 7496/25/25 4437/22/22 +f 10/26/26 14/27/27 15/28/28 +f 11/29/29 15/28/28 4452/30/30 +f 14/27/27 4450/31/31 4451/32/32 +f 15/28/28 4451/32/32 7455/33/33 +f 11/29/29 16/34/34 12/35/35 +f 16/34/34 4455/36/36 4456/37/37 +f 4453/38/38 4454/39/39 16/34/34 +f 4454/39/39 7453/40/40 4455/36/36 +f 10/26/26 12/35/35 17/41/41 +f 13/42/42 17/41/41 4458/43/43 +f 12/35/35 4456/37/37 4457/44/44 +f 17/41/41 4457/44/44 7499/45/45 +f 13/42/42 18/46/46 14/27/27 +f 18/46/46 4449/47/47 4450/31/31 +f 4459/48/48 4460/49/49 18/46/46 +f 4460/49/49 7497/50/50 4449/47/47 +f 19/51/51 23/52/52 24/53/53 +f 20/54/54 24/53/53 4448/24/24 +f 23/52/52 4462/55/55 4463/56/56 +f 24/53/53 4463/56/56 7496/25/25 +f 20/54/54 25/57/57 21/58/58 +f 25/57/57 4464/59/59 4465/60/60 +f 4447/23/23 4446/18/18 25/57/57 +f 4446/18/18 7454/20/20 4464/59/59 +f 19/51/51 21/58/58 26/61/61 +f 22/62/62 26/61/61 4467/63/63 +f 21/58/58 4465/60/60 4466/64/64 +f 26/61/61 4466/64/64 7456/65/65 +f 22/62/62 27/66/66 23/52/52 +f 27/66/66 4461/67/67 4462/55/55 +f 4468/68/68 4469/69/69 27/66/66 +f 4469/69/69 7494/70/70 4461/67/67 +f 28/71/71 32/72/72 33/73/73 +f 29/74/74 33/73/73 4473/75/75 +f 32/72/72 4471/76/76 4472/77/77 +f 33/73/73 4472/77/77 7457/78/78 +f 29/74/74 34/79/79 30/80/80 +f 34/79/79 4451/32/32 4450/31/31 +f 4474/81/81 4475/82/82 34/79/79 +f 4475/82/82 7455/33/33 4451/32/32 +f 28/71/71 30/80/80 35/83/83 +f 31/84/84 35/83/83 4476/85/85 +f 30/80/80 4450/31/31 4449/47/47 +f 35/83/83 4449/47/47 7497/50/50 +f 31/84/84 36/86/86 32/72/72 +f 36/86/86 4470/87/87 4471/76/76 +f 4477/88/88 4478/89/89 36/86/86 +f 4478/89/89 7495/90/90 4470/87/87 +f 37/91/91 41/92/92 42/93/93 +f 38/94/94 42/93/93 4479/95/95 +f 41/92/92 4465/96/60 4464/97/59 +f 42/93/93 4464/97/59 7454/98/20 +f 38/94/94 43/99/96 39/100/97 +f 43/99/96 4482/101/98 4483/102/99 +f 4480/103/100 4481/104/101 43/99/96 +f 4481/104/101 7460/105/102 4482/101/98 +f 37/91/91 39/100/97 44/106/103 +f 40/107/104 44/106/103 4485/108/105 +f 39/100/97 4483/102/99 4484/109/106 +f 44/106/103 4484/109/106 7458/110/107 +f 40/107/104 45/111/108 41/92/92 +f 45/111/108 4466/112/64 4465/96/60 +f 4486/113/109 4487/114/110 45/111/108 +f 4487/114/110 7456/115/65 4466/112/64 +f 46/116/111 50/117/112 51/118/113 +f 47/119/114 51/118/113 4491/120/115 +f 50/117/112 4489/121/116 4490/122/117 +f 51/118/113 4490/122/117 7459/123/118 +f 47/119/114 52/124/119 48/125/120 +f 52/124/119 4494/126/121 4495/127/122 +f 4492/128/123 4493/129/124 52/124/119 +f 4493/129/124 7461/130/125 4494/126/121 +f 46/116/111 48/125/120 53/131/126 +f 49/132/127 53/131/126 4475/82/82 +f 48/125/120 4495/127/122 4496/133/128 +f 53/131/126 4496/133/128 7455/33/33 +f 49/132/127 54/134/129 50/117/112 +f 54/134/129 4488/135/130 4489/121/116 +f 4474/81/81 4473/75/75 54/134/129 +f 4473/75/75 7457/78/78 4488/135/130 +f 55/136/131 59/137/132 60/138/133 +f 56/139/134 60/138/133 4497/140/135 +f 59/137/132 4444/12/12 4443/11/11 +f 60/138/133 4443/11/11 7452/15/15 +f 56/139/134 61/141/136 57/142/137 +f 61/141/136 4500/143/138 4501/144/139 +f 4498/145/140 4499/146/141 61/141/136 +f 4499/146/141 7462/147/142 4500/143/138 +f 55/136/131 57/142/137 62/148/143 +f 58/149/144 62/148/143 4481/150/101 +f 57/142/137 4501/144/139 4502/151/145 +f 62/148/143 4502/151/145 7460/152/102 +f 58/149/144 63/153/146 59/137/132 +f 63/153/146 4445/19/19 4444/12/12 +f 4480/154/100 4479/155/95 63/153/146 +f 4479/155/95 7454/20/20 4445/19/19 +f 64/156/147 68/157/148 69/158/149 +f 65/159/150 69/158/149 4503/160/151 +f 68/157/148 4495/127/122 4494/126/121 +f 69/158/149 4494/126/121 7461/130/125 +f 65/159/150 70/161/152 66/162/153 +f 70/161/152 4506/163/154 4507/164/155 +f 4504/165/156 4505/166/157 70/161/152 +f 4505/166/157 7463/167/158 4506/163/154 +f 64/156/147 66/162/153 71/168/159 +f 67/169/160 71/168/159 4454/39/39 +f 66/162/153 4507/164/155 4508/170/161 +f 71/168/159 4508/170/161 7453/40/40 +f 67/169/160 72/171/162 68/157/148 +f 72/171/162 4496/133/128 4495/127/122 +f 4453/38/38 4452/30/30 72/171/162 +f 4452/30/30 7455/33/33 4496/133/128 +f 73/172/163 77/173/164 78/174/165 +f 74/175/166 78/174/165 4509/176/167 +f 77/173/164 4501/177/139 4500/178/138 +f 78/174/165 4500/178/138 7462/179/142 +f 74/175/166 79/180/168 75/181/169 +f 79/180/168 4512/182/170 4513/183/171 +f 4510/184/172 4511/185/173 79/180/168 +f 4511/185/173 7464/186/174 4512/182/170 +f 73/172/163 75/181/169 80/187/175 +f 76/188/176 80/187/175 4515/189/177 +f 75/181/169 4513/183/171 4514/190/178 +f 80/187/175 4514/190/178 7466/191/179 +f 76/188/176 81/192/180 77/173/164 +f 81/192/180 4502/193/145 4501/177/139 +f 4516/194/181 4517/195/182 81/192/180 +f 4517/195/182 7460/196/102 4502/193/145 +f 82/197/183 86/198/184 87/199/185 +f 83/200/186 87/199/185 4521/201/187 +f 86/198/184 4519/202/188 4520/203/189 +f 87/199/185 4520/203/189 7467/204/190 +f 83/200/186 88/205/191 84/206/192 +f 88/205/191 4524/207/193 4525/208/194 +f 4522/209/195 4523/210/196 88/205/191 +f 4523/210/196 7465/211/197 4524/207/193 +f 82/197/183 84/206/192 89/212/198 +f 85/213/199 89/212/198 4505/166/157 +f 84/206/192 4525/208/194 4526/214/200 +f 89/212/198 4526/214/200 7463/167/158 +f 85/213/199 90/215/201 86/198/184 +f 90/215/201 4518/216/202 4519/202/188 +f 4504/165/156 4503/160/151 90/215/201 +f 4503/160/151 7461/130/125 4518/216/202 +f 91/217/203 95/218/204 96/219/205 +f 92/220/206 96/219/205 4517/195/182 +f 95/218/204 4483/221/99 4482/222/98 +f 96/219/205 4482/222/98 7460/196/102 +f 92/220/206 97/223/207 93/224/208 +f 97/223/207 4527/225/209 4528/226/210 +f 4516/194/181 4515/189/177 97/223/207 +f 4515/189/177 7466/191/179 4527/225/209 +f 91/217/203 93/224/208 98/227/211 +f 94/228/212 98/227/211 4530/229/213 +f 93/224/208 4528/226/210 4529/230/214 +f 98/227/211 4529/230/214 7468/231/215 +f 94/228/212 99/232/216 95/218/204 +f 99/232/216 4484/233/106 4483/221/99 +f 4531/234/217 4532/235/218 99/232/216 +f 4532/235/218 7458/236/107 4484/233/106 +f 100/237/219 104/238/220 105/239/221 +f 101/240/222 105/239/221 4536/241/223 +f 104/238/220 4534/242/224 4535/243/225 +f 105/239/221 4535/243/225 7469/244/226 +f 101/240/222 106/245/227 102/246/228 +f 106/245/227 4520/203/189 4519/202/188 +f 4537/247/229 4538/248/230 106/245/227 +f 4538/248/230 7467/204/190 4520/203/189 +f 100/237/219 102/246/228 107/249/231 +f 103/250/232 107/249/231 4493/129/124 +f 102/246/228 4519/202/188 4518/216/202 +f 107/249/231 4518/216/202 7461/130/125 +f 103/250/232 108/251/233 104/238/220 +f 108/251/233 4533/252/234 4534/242/224 +f 4492/128/123 4491/120/115 108/251/233 +f 4491/120/115 7459/123/118 4533/252/234 +f 109/253/235 113/254/236 114/255/237 +f 110/256/238 114/255/237 4539/257/239 +f 113/254/236 4528/226/210 4527/225/209 +f 114/255/237 4527/225/209 7466/191/179 +f 109/253/235 110/256/238 115/258/240 +f 111/259/241 115/258/240 4542/260/242 +f 110/256/238 4540/261/243 4541/262/244 +f 115/258/240 4541/262/244 7472/263/245 +f 111/259/241 116/264/246 112/265/247 +f 112/265/247 116/264/246 4545/266/248 +f 4543/267/249 4544/268/250 116/264/246 +f 4544/268/250 7470/269/251 4545/266/248 +f 112/265/247 117/270/252 113/254/236 +f 117/270/252 4529/230/214 4528/226/210 +f 4546/271/253 4547/272/254 117/270/252 +f 4547/272/254 7468/231/215 4529/230/214 +f 122/273/255 123/274/256 119/275/257 +f 123/274/256 4551/276/258 4552/277/259 +f 122/273/255 4549/278/260 4550/279/261 +f 4550/279/261 7471/280/262 4551/276/258 +f 118/281/263 119/275/257 124/282/264 +f 120/283/265 124/282/264 4554/284/266 +f 119/275/257 4552/277/259 4553/285/267 +f 124/282/264 4553/285/267 7473/286/268 +f 118/281/263 120/283/265 125/287/269 +f 121/288/270 125/287/269 4538/289/230 +f 120/283/265 4555/290/271 4556/291/272 +f 125/287/269 4556/291/272 7467/292/190 +f 121/288/270 126/293/273 122/273/255 +f 126/293/273 4548/294/274 4549/278/260 +f 4537/295/229 4536/296/223 126/293/273 +f 4536/296/223 7469/297/226 4548/294/274 +f 127/298/275 131/299/276 132/300/277 +f 128/301/278 132/300/277 4557/302/279 +f 131/299/276 4513/183/171 4512/182/170 +f 132/300/277 4512/182/170 7464/186/174 +f 128/301/278 133/303/280 129/304/281 +f 133/303/280 4560/305/282 4561/306/283 +f 4558/307/284 4559/308/285 133/303/280 +f 4559/308/285 7474/309/286 4560/305/282 +f 127/298/275 129/304/281 134/310/287 +f 134/310/287 4541/262/244 4540/261/243 +f 4561/306/283 4562/311/288 134/310/287 +f 4562/311/288 7472/263/245 4541/262/244 +f 130/312/289 135/313/290 131/299/276 +f 135/313/290 4514/190/178 4513/183/171 +f 4540/261/243 4539/257/239 135/313/290 +f 4539/257/239 7466/191/179 4514/190/178 +f 136/314/291 140/315/292 141/316/293 +f 141/316/293 4563/317/294 4564/318/295 +f 4555/290/271 4554/284/266 141/316/293 +f 4554/284/266 7473/286/268 4563/317/294 +f 137/319/296 142/320/297 138/321/298 +f 142/320/297 4566/322/299 4567/323/300 +f 4564/318/295 4565/324/301 142/320/297 +f 4565/324/301 7475/325/302 4566/322/299 +f 136/314/291 138/321/298 143/326/303 +f 139/327/304 143/326/303 4523/328/196 +f 138/321/298 4567/323/300 4568/329/305 +f 143/326/303 4568/329/305 7465/330/197 +f 139/327/304 144/331/306 140/315/292 +f 144/331/306 4556/291/272 4555/290/271 +f 4522/332/195 4521/333/187 144/331/306 +f 4521/333/187 7467/292/190 4556/291/272 +f 145/334/307 149/335/308 150/336/309 +f 146/337/310 150/336/309 4569/338/311 +f 149/335/308 4561/339/283 4560/340/282 +f 150/336/309 4560/340/282 7474/341/286 +f 146/337/310 151/342/312 147/343/313 +f 147/343/313 151/342/312 4572/344/314 +f 146/337/310 4570/345/315 4571/346/316 +f 151/342/312 4571/346/316 7476/347/317 +f 145/334/307 147/343/313 152/348/318 +f 152/348/318 4575/349/319 4576/350/320 +f 4573/351/321 4574/352/322 152/348/318 +f 4574/352/322 7478/353/323 4575/349/319 +f 148/354/324 153/355/325 149/335/308 +f 153/355/325 4562/356/288 4561/339/283 +f 4576/350/320 4577/357/326 153/355/325 +f 4577/357/326 7472/358/245 4562/356/288 +f 154/359/327 158/360/328 159/361/329 +f 159/361/329 4581/362/330 4582/363/331 +f 4579/364/332 4580/365/333 159/361/329 +f 4580/365/333 7479/366/334 4581/362/330 +f 155/367/335 160/368/336 156/369/337 +f 156/369/337 160/368/336 4584/370/338 +f 155/367/335 4582/363/331 4583/371/339 +f 160/368/336 4583/371/339 7477/372/340 +f 154/359/327 156/369/337 161/373/341 +f 157/374/342 161/373/341 4565/324/301 +f 156/369/337 4585/375/343 4586/376/344 +f 161/373/341 4586/376/344 7475/325/302 +f 157/374/342 162/377/345 158/360/328 +f 162/377/345 4578/378/346 4579/364/332 +f 4564/318/295 4563/317/294 162/377/345 +f 4563/317/294 7473/286/268 4578/378/346 +f 167/379/347 168/380/348 164/381/349 +f 168/380/348 4577/357/326 4576/350/320 +f 4543/382/249 4542/383/242 168/380/348 +f 168/380/348 4542/383/242 7472/358/245 +f 163/384/350 164/381/349 169/385/351 +f 165/386/352 169/385/351 4587/387/353 +f 164/381/349 4576/350/320 4575/349/319 +f 169/385/351 4575/349/319 7478/353/323 +f 165/386/352 170/388/354 166/389/355 +f 170/388/354 4590/390/356 4591/391/357 +f 4588/392/358 4589/393/359 170/388/354 +f 4589/393/359 7480/394/360 4590/390/356 +f 163/384/350 166/389/355 171/395/361 +f 167/379/347 171/395/361 4544/396/250 +f 166/389/355 4591/391/357 4592/397/362 +f 171/395/361 4592/397/362 7470/398/251 +f 176/399/363 177/400/364 173/401/365 +f 177/400/364 4596/402/366 4597/403/367 +f 4594/404/368 4595/405/369 177/400/364 +f 4595/405/369 7481/406/370 4596/402/366 +f 172/407/371 173/401/365 178/408/372 +f 174/409/373 178/408/372 4580/365/333 +f 173/401/365 4597/403/367 4598/410/374 +f 178/408/372 4598/410/374 7479/366/334 +f 174/409/373 179/411/375 175/412/376 +f 179/411/375 4553/285/267 4552/277/259 +f 4579/364/332 4578/378/346 179/411/375 +f 179/411/375 4578/378/346 7473/286/268 +f 172/407/371 175/412/376 180/413/377 +f 176/399/363 180/413/377 4593/414/378 +f 175/412/376 4552/277/259 4551/276/258 +f 180/413/377 4551/276/258 7471/280/262 +f 185/415/379 186/416/380 182/417/381 +f 186/416/380 4599/418/382 4600/419/383 +f 4588/392/358 4587/387/353 186/416/380 +f 4587/387/353 7478/353/323 4599/418/382 +f 181/420/384 182/417/381 187/421/385 +f 183/422/386 187/421/385 4602/423/387 +f 182/417/381 4600/419/383 4601/424/388 +f 187/421/385 4601/424/388 7484/425/389 +f 183/422/386 188/426/390 184/427/391 +f 188/426/390 4605/428/392 4606/429/393 +f 4603/430/394 4604/431/395 188/426/390 +f 4604/431/395 7482/432/396 4605/428/392 +f 181/420/384 184/427/391 189/433/397 +f 185/415/379 189/433/397 4589/393/359 +f 184/427/391 4606/429/393 4607/434/398 +f 189/433/397 4607/434/398 7480/394/360 +f 194/435/399 195/436/400 191/437/401 +f 195/436/400 4611/438/402 4612/439/403 +f 4609/440/404 4610/441/405 195/436/400 +f 4610/441/405 7483/442/406 4611/438/402 +f 190/443/407 191/437/401 196/444/408 +f 192/445/409 196/444/408 4614/446/410 +f 191/437/401 4612/439/403 4613/447/411 +f 196/444/408 4613/447/411 7485/448/412 +f 192/445/409 197/449/413 193/450/414 +f 197/449/413 4598/410/374 4597/403/367 +f 4615/451/415 4616/452/416 197/449/413 +f 4616/452/416 7479/366/334 4598/410/374 +f 190/443/407 193/450/414 198/453/417 +f 194/435/399 198/453/417 4608/454/418 +f 193/450/414 4597/403/367 4596/402/366 +f 198/453/417 4596/402/366 7481/406/370 +f 203/455/419 204/456/420 200/457/421 +f 204/456/420 4617/458/422 4618/459/423 +f 4573/460/321 4572/461/314 204/456/420 +f 4572/461/314 7476/462/317 4617/458/422 +f 199/463/424 200/457/421 205/464/425 +f 201/465/426 205/464/425 4620/466/427 +f 200/457/421 4618/459/423 4619/467/428 +f 205/464/425 4619/467/428 7486/468/429 +f 201/465/426 206/469/430 202/470/431 +f 206/469/430 4601/471/388 4600/472/383 +f 4621/473/432 4622/474/433 206/469/430 +f 4622/474/433 7484/475/389 4601/471/388 +f 199/463/424 202/470/431 207/476/434 +f 203/455/419 207/476/434 4574/477/322 +f 202/470/431 4600/472/383 4599/478/382 +f 207/476/434 4599/478/382 7478/479/323 +f 212/480/435 213/481/436 209/482/437 +f 213/481/436 4623/483/438 4624/484/439 +f 4615/451/415 4614/446/410 213/481/436 +f 4614/446/410 7485/448/412 4623/483/438 +f 208/485/440 209/482/437 214/486/441 +f 210/487/442 214/486/441 4626/488/443 +f 209/482/437 4624/484/439 4625/489/444 +f 214/486/441 4625/489/444 7487/490/445 +f 210/487/442 215/491/446 211/492/447 +f 215/491/446 4583/371/339 4582/363/331 +f 4627/493/448 4628/494/449 215/491/446 +f 4628/494/449 7477/372/340 4583/371/339 +f 208/485/440 211/492/447 216/495/450 +f 212/480/435 216/495/450 4616/452/416 +f 211/492/447 4582/363/331 4581/362/330 +f 216/495/450 4581/362/330 7479/366/334 +f 221/496/451 222/497/452 218/498/453 +f 222/497/452 4629/499/454 4630/500/455 +f 4621/473/432 4620/466/427 222/497/452 +f 4620/466/427 7486/468/429 4629/499/454 +f 217/501/456 218/498/453 223/502/457 +f 219/503/458 223/502/457 4632/504/459 +f 218/498/453 4630/500/455 4631/505/460 +f 223/502/457 4631/505/460 7488/506/461 +f 219/503/458 224/507/462 220/508/463 +f 224/507/462 4635/509/464 4636/510/465 +f 4633/511/466 4634/512/467 224/507/462 +f 4634/512/467 7490/513/468 4635/509/464 +f 217/501/456 220/508/463 225/514/469 +f 221/496/451 225/514/469 4622/474/433 +f 220/508/463 4636/510/465 4637/515/470 +f 225/514/469 4637/515/470 7484/475/389 +f 230/516/471 231/517/472 227/518/473 +f 231/517/472 4641/519/474 4642/520/475 +f 4639/521/476 4640/522/477 231/517/472 +f 4640/522/477 7491/523/478 4641/519/474 +f 226/524/479 227/518/473 232/525/480 +f 228/526/481 232/525/480 4644/527/482 +f 227/518/473 4642/520/475 4643/528/483 +f 232/525/480 4643/528/483 7489/529/484 +f 228/526/481 233/530/485 229/531/486 +f 233/530/485 4625/532/444 4624/533/439 +f 4645/534/487 4646/535/488 233/530/485 +f 4646/535/488 7487/536/445 4625/532/444 +f 226/524/479 229/531/486 234/537/489 +f 230/516/471 234/537/489 4638/538/490 +f 229/531/486 4624/533/439 4623/539/438 +f 234/537/489 4623/539/438 7485/540/412 +f 239/541/491 240/542/492 236/543/493 +f 240/542/492 4637/515/470 4636/510/465 +f 4603/544/394 4602/545/387 240/542/492 +f 4602/545/387 7484/475/389 4637/515/470 +f 235/546/494 236/543/493 241/547/495 +f 237/548/496 241/547/495 4647/549/497 +f 236/543/493 4636/510/465 4635/509/464 +f 241/547/495 4635/509/464 7490/513/468 +f 237/548/496 242/550/498 238/551/499 +f 242/550/498 4650/552/500 4651/553/501 +f 4648/554/502 4649/555/503 242/550/498 +f 4649/555/503 7492/556/504 4650/552/500 +f 235/546/494 238/551/499 243/557/505 +f 239/541/491 243/557/505 4604/558/395 +f 238/551/499 4651/553/501 4652/559/506 +f 243/557/505 4652/559/506 7482/560/396 +f 248/561/507 249/562/508 245/563/509 +f 249/562/508 4656/564/510 4657/565/511 +f 4654/566/512 4655/567/513 249/562/508 +f 4655/567/513 7493/568/514 4656/564/510 +f 244/569/515 245/563/509 250/570/516 +f 246/571/517 250/570/516 4640/522/477 +f 245/563/509 4657/565/511 4658/572/518 +f 250/570/516 4658/572/518 7491/523/478 +f 246/571/517 251/573/519 247/574/520 +f 251/573/519 4613/575/411 4612/576/403 +f 4639/521/476 4638/538/490 251/573/519 +f 4638/538/490 7485/540/412 4613/575/411 +f 244/569/515 247/574/520 252/577/521 +f 248/561/507 252/577/521 4653/578/522 +f 247/574/520 4612/576/403 4611/579/402 +f 252/577/521 4611/579/402 7483/580/406 +f 257/581/523 258/582/524 254/583/525 +f 258/582/524 4659/584/526 4660/585/527 +f 4648/554/502 4647/549/497 258/582/524 +f 4647/549/497 7490/513/468 4659/584/526 +f 253/586/528 254/583/525 259/587/529 +f 255/588/530 259/587/529 4463/56/56 +f 254/583/525 4660/585/527 4661/589/531 +f 259/587/529 4661/589/531 7496/25/25 +f 255/588/530 260/590/532 256/591/533 +f 260/590/532 4662/592/534 4663/593/535 +f 4462/55/55 4461/67/67 260/590/532 +f 4461/67/67 7494/70/70 4662/592/534 +f 253/586/528 256/591/533 261/594/536 +f 257/581/523 261/594/536 4649/555/503 +f 256/591/533 4663/593/535 4664/595/537 +f 261/594/536 4664/595/537 7492/556/504 +f 266/596/538 267/597/539 263/598/540 +f 267/597/539 4478/599/89 4477/600/88 +f 4666/601/541 4667/602/542 267/597/539 +f 4667/602/542 7495/603/90 4478/599/89 +f 262/604/543 263/598/540 268/605/544 +f 264/606/545 268/605/544 4668/607/546 +f 263/598/540 4477/600/88 4476/608/85 +f 268/605/544 4476/608/85 7497/609/50 +f 264/606/545 269/610/547 265/611/548 +f 269/610/547 4658/572/518 4657/565/511 +f 4669/612/549 4670/613/550 269/610/547 +f 4670/613/550 7491/523/478 4658/572/518 +f 262/604/543 265/611/548 270/614/551 +f 266/596/538 270/614/551 4665/615/552 +f 265/611/548 4657/565/511 4656/564/510 +f 270/614/551 4656/564/510 7493/568/514 +f 275/616/553 276/617/554 272/618/555 +f 276/617/554 4671/619/556 4672/620/557 +f 4633/511/466 4632/504/459 276/617/554 +f 4632/504/459 7488/506/461 4671/619/556 +f 271/621/558 272/618/555 277/622/559 +f 273/623/560 277/622/559 4439/7/7 +f 272/618/555 4672/620/557 4673/624/561 +f 277/622/559 4673/624/561 7498/8/8 +f 273/623/560 278/625/562 274/626/563 +f 278/625/562 4661/589/531 4660/585/527 +f 4438/6/6 4437/22/22 278/625/562 +f 4437/22/22 7496/25/25 4661/589/531 +f 271/621/558 274/626/563 279/627/564 +f 275/616/553 279/627/564 4634/512/467 +f 274/626/563 4660/585/527 4659/584/526 +f 279/627/564 4659/584/526 7490/513/468 +f 284/628/565 285/629/566 281/630/567 +f 285/629/566 4460/631/49 4459/632/48 +f 4669/612/549 4668/607/546 285/629/566 +f 4668/607/546 7497/609/50 4460/631/49 +f 280/633/568 281/630/567 286/634/569 +f 282/635/570 286/634/569 4674/636/571 +f 281/630/567 4459/632/48 4458/637/43 +f 286/634/569 4458/637/43 7499/638/45 +f 282/635/570 287/639/572 283/640/573 +f 287/639/572 4643/528/483 4642/520/475 +f 4675/641/574 4676/642/575 287/639/572 +f 4676/642/575 7489/529/484 4643/528/483 +f 280/633/568 283/640/573 288/643/576 +f 284/628/565 288/643/576 4670/613/550 +f 283/640/573 4642/520/475 4641/519/474 +f 288/643/576 4641/519/474 7491/523/478 +f 293/644/577 294/645/578 290/646/579 +f 294/645/578 4673/647/561 4672/648/557 +f 4678/649/580 4679/650/581 294/645/578 +f 4679/650/581 7498/651/8 4673/647/561 +f 289/652/582 290/646/579 295/653/583 +f 291/654/584 295/653/583 4680/655/585 +f 290/646/579 4672/648/557 4671/656/556 +f 295/653/583 4671/656/556 7488/657/461 +f 291/654/584 296/658/586 292/659/587 +f 296/658/586 4683/660/588 4684/661/589 +f 4681/662/590 4682/663/591 296/658/586 +f 4682/663/591 7502/664/592 4683/660/588 +f 289/652/582 292/659/587 297/665/593 +f 293/644/577 297/665/593 4677/666/594 +f 4684/661/589 4685/667/595 297/665/593 +f 4685/667/595 7500/668/596 4677/666/594 +f 302/669/597 303/670/598 299/671/599 +f 303/670/598 4689/672/600 4690/673/601 +f 4687/674/602 4688/675/603 303/670/598 +f 4688/675/603 7503/676/604 4689/672/600 +f 298/677/605 299/671/599 304/678/606 +f 300/679/607 304/678/606 4676/642/575 +f 299/671/599 4690/673/601 4691/680/608 +f 304/678/606 4691/680/608 7489/529/484 +f 300/679/607 305/681/609 301/682/610 +f 305/681/609 4692/683/611 4693/684/612 +f 4675/641/574 4674/636/571 305/681/609 +f 4674/636/571 7499/638/45 4692/683/611 +f 298/677/605 301/682/610 306/685/613 +f 306/685/613 4686/686/614 4687/674/602 +f 301/682/610 4693/684/612 4694/687/615 +f 4694/687/615 7501/688/616 4686/686/614 +f 311/689/617 312/690/618 308/691/619 +f 312/690/618 4631/692/460 4630/693/455 +f 4681/662/590 4680/655/585 312/690/618 +f 4680/655/585 7488/657/461 4631/692/460 +f 307/694/620 308/691/619 313/695/621 +f 309/696/622 313/695/621 4695/697/623 +f 308/691/619 4630/693/455 4629/698/454 +f 313/695/621 4629/698/454 7486/699/429 +f 309/696/622 314/700/624 310/701/625 +f 314/700/624 4698/702/626 4699/703/627 +f 4696/704/628 4697/705/629 314/700/624 +f 4697/705/629 7504/706/630 4698/702/626 +f 307/694/620 310/701/625 315/707/631 +f 311/689/617 315/707/631 4682/663/591 +f 310/701/625 4699/703/627 4700/708/632 +f 315/707/631 4700/708/632 7502/664/592 +f 320/709/633 321/710/634 317/711/635 +f 321/710/634 4704/712/636 4705/713/637 +f 4702/714/638 4703/715/639 321/710/634 +f 4703/715/639 7505/716/640 4704/712/636 +f 316/717/641 317/711/635 322/718/642 +f 318/719/643 322/718/642 4646/720/488 +f 317/711/635 4705/713/637 4706/721/644 +f 322/718/642 4706/721/644 7487/722/445 +f 318/719/643 323/723/645 319/724/646 +f 323/723/645 4691/725/608 4690/726/601 +f 4645/727/487 4644/728/482 323/723/645 +f 4644/728/482 7489/729/484 4691/725/608 +f 316/717/641 319/724/646 324/730/647 +f 320/709/633 324/730/647 4701/731/648 +f 319/724/646 4690/726/601 4689/732/600 +f 324/730/647 4689/732/600 7503/733/604 +f 329/734/649 330/735/650 326/736/651 +f 330/735/650 4619/737/428 4618/738/423 +f 4696/739/628 4695/740/623 330/735/650 +f 4695/740/623 7486/741/429 4619/737/428 +f 325/742/652 326/736/651 331/743/653 +f 327/744/654 331/743/653 4707/745/655 +f 326/736/651 4618/738/423 4617/746/422 +f 331/743/653 4617/746/422 7476/347/317 +f 327/744/654 332/747/656 328/748/657 +f 332/747/656 4710/749/658 4711/750/659 +f 4708/751/660 4709/752/661 332/747/656 +f 4709/752/661 7506/753/662 4710/749/658 +f 325/742/652 328/748/657 333/754/663 +f 329/734/649 333/754/663 4697/755/629 +f 328/748/657 4711/750/659 4712/756/664 +f 333/754/663 4712/756/664 7504/757/630 +f 338/758/665 339/759/666 335/760/667 +f 339/759/666 4716/761/668 4717/762/669 +f 4714/763/670 4715/764/671 339/759/666 +f 4715/764/671 7507/765/672 4716/761/668 +f 334/766/673 335/760/667 340/767/674 +f 336/768/675 340/767/674 4628/769/449 +f 335/760/667 4717/762/669 4718/770/676 +f 340/767/674 4718/770/676 7477/771/340 +f 336/768/675 341/772/677 337/773/678 +f 341/772/677 4706/721/644 4705/713/637 +f 4627/774/448 4626/775/443 341/772/677 +f 4626/775/443 7487/722/445 4706/721/644 +f 334/766/673 337/773/678 342/776/679 +f 338/758/665 342/776/679 4713/777/680 +f 337/773/678 4705/713/637 4704/712/636 +f 342/776/679 4704/712/636 7505/716/640 +f 347/778/681 348/779/682 344/780/683 +f 348/779/682 4571/346/316 4570/345/315 +f 4708/751/660 4707/745/655 348/779/682 +f 4707/745/655 7476/347/317 4571/346/316 +f 343/781/684 344/780/683 349/782/685 +f 349/782/685 4719/783/686 4720/784/687 +f 4570/345/315 4569/338/311 349/782/685 +f 4569/338/311 7474/341/286 4719/783/686 +f 345/785/688 350/786/689 346/787/690 +f 350/786/689 4722/788/691 4723/789/692 +f 4720/784/687 4721/790/693 350/786/689 +f 4721/790/693 7508/791/694 4722/788/691 +f 343/781/684 346/787/690 351/792/695 +f 347/778/681 351/792/695 4709/752/661 +f 346/787/690 4723/789/692 4724/793/696 +f 351/792/695 4724/793/696 7506/753/662 +f 356/794/697 357/795/698 353/796/699 +f 357/795/698 4728/797/700 4729/798/701 +f 4726/799/702 4727/800/703 357/795/698 +f 4727/800/703 7509/801/704 4728/797/700 +f 352/802/705 353/796/699 358/803/706 +f 358/803/706 4586/804/344 4585/805/343 +f 4729/798/701 4730/806/707 358/803/706 +f 4730/806/707 7475/807/302 4586/804/344 +f 354/808/708 359/809/709 355/810/710 +f 359/809/709 4718/770/676 4717/762/669 +f 4585/805/343 4584/811/338 359/809/709 +f 4584/811/338 7477/771/340 4718/770/676 +f 352/802/705 355/810/710 360/812/711 +f 356/794/697 360/812/711 4725/813/712 +f 355/810/710 4717/762/669 4716/761/668 +f 360/812/711 4716/761/668 7507/765/672 +f 361/814/713 365/815/714 366/816/715 +f 362/817/716 366/816/715 4559/818/285 +f 365/815/714 4720/819/687 4719/820/686 +f 366/816/715 4719/820/686 7474/821/286 +f 362/817/716 367/822/717 363/823/718 +f 367/822/717 4731/824/719 4732/825/720 +f 4558/826/284 4557/827/279 367/822/717 +f 4557/827/279 7464/828/174 4731/824/719 +f 361/814/713 363/823/718 368/829/721 +f 364/830/722 368/829/721 4734/831/723 +f 363/823/718 4732/825/720 4733/832/724 +f 368/829/721 4733/832/724 7510/833/725 +f 364/830/722 369/834/726 365/815/714 +f 365/815/714 369/834/726 4721/835/693 +f 4735/836/727 4736/837/728 369/834/726 +f 4736/837/728 7508/838/694 4721/835/693 +f 370/839/729 374/840/730 375/841/731 +f 371/842/732 375/841/731 4740/843/733 +f 374/840/730 4738/844/734 4739/845/735 +f 375/841/731 4739/845/735 7511/846/736 +f 371/842/732 376/847/737 372/848/738 +f 376/847/737 4568/849/305 4567/850/300 +f 4741/851/739 4742/852/740 376/847/737 +f 4742/852/740 7465/853/197 4568/849/305 +f 370/839/729 372/848/738 377/854/741 +f 373/855/742 377/854/741 4730/856/707 +f 372/848/738 4567/850/300 4566/857/299 +f 377/854/741 4566/857/299 7475/858/302 +f 373/855/742 378/859/743 374/840/730 +f 378/859/743 4737/860/744 4738/844/734 +f 373/855/742 4729/861/701 4728/862/700 +f 4728/862/700 7509/863/704 4737/860/744 +f 379/864/745 383/865/746 384/866/747 +f 380/867/748 384/866/747 4511/868/173 +f 383/865/746 4732/825/720 4731/824/719 +f 384/866/747 4731/824/719 7464/828/174 +f 380/867/748 385/869/749 381/870/750 +f 385/869/749 4743/871/751 4744/872/752 +f 4510/873/172 4509/874/167 385/869/749 +f 4509/874/167 7462/875/142 4743/871/751 +f 379/864/745 381/870/750 386/876/753 +f 382/877/754 386/876/753 4746/878/755 +f 381/870/750 4744/872/752 4745/879/756 +f 386/876/753 4745/879/756 7514/880/757 +f 382/877/754 387/881/758 383/865/746 +f 387/881/758 4733/832/724 4732/825/720 +f 4747/882/759 4748/883/760 387/881/758 +f 4748/883/760 7510/833/725 4733/832/724 +f 388/884/761 392/885/762 393/886/763 +f 389/887/764 393/886/763 4752/888/765 +f 392/885/762 4750/889/766 4751/890/767 +f 393/886/763 4751/890/767 7515/891/768 +f 389/887/764 394/892/769 390/893/770 +f 394/892/769 4526/894/200 4525/895/194 +f 4753/896/771 4754/897/772 394/892/769 +f 4754/897/772 7463/898/158 4526/894/200 +f 388/884/761 390/893/770 395/899/773 +f 391/900/774 395/899/773 4742/852/740 +f 390/893/770 4525/895/194 4524/901/193 +f 395/899/773 4524/901/193 7465/853/197 +f 391/900/774 396/902/775 392/885/762 +f 396/902/775 4749/903/776 4750/889/766 +f 4741/851/739 4740/843/733 396/902/775 +f 4740/843/733 7511/846/736 4749/903/776 +f 397/904/777 401/905/778 402/906/779 +f 398/907/780 402/906/779 4499/908/141 +f 401/905/778 4744/909/752 4743/910/751 +f 402/906/779 4743/910/751 7462/911/142 +f 398/907/780 403/912/781 399/913/782 +f 403/912/781 4755/914/783 4756/915/784 +f 4498/916/140 4497/917/135 403/912/781 +f 4497/917/135 7452/918/15 4755/914/783 +f 397/904/777 399/913/782 404/919/785 +f 400/920/786 404/919/785 4758/921/787 +f 399/913/782 4756/915/784 4757/922/788 +f 404/919/785 4757/922/788 7516/923/789 +f 400/920/786 405/924/790 401/905/778 +f 405/924/790 4745/925/756 4744/909/752 +f 4759/926/791 4760/927/792 405/924/790 +f 4760/927/792 7514/928/757 4745/925/756 +f 406/929/793 410/930/794 411/931/795 +f 407/932/796 411/931/795 4764/933/797 +f 410/930/794 4762/934/798 4763/935/799 +f 411/931/795 4763/935/799 7517/936/800 +f 407/932/796 412/937/801 408/938/802 +f 412/937/801 4508/939/161 4507/940/155 +f 4765/941/803 4766/942/804 412/937/801 +f 4766/942/804 7453/943/40 4508/939/161 +f 406/929/793 408/938/802 413/944/805 +f 409/945/806 413/944/805 4754/946/772 +f 408/938/802 4507/940/155 4506/947/154 +f 413/944/805 4506/947/154 7463/948/158 +f 409/945/806 414/949/807 410/930/794 +f 414/949/807 4761/950/808 4762/934/798 +f 4753/951/771 4752/952/765 414/949/807 +f 4752/952/765 7515/953/768 4761/950/808 +f 415/954/809 419/955/810 420/956/811 +f 416/957/812 420/956/811 4442/958/14 +f 419/955/810 4756/915/784 4755/914/783 +f 420/956/811 4755/914/783 7452/918/15 +f 416/957/812 421/959/813 417/960/814 +f 421/959/813 4679/961/581 4678/962/580 +f 4441/963/13 4440/964/5 421/959/813 +f 4440/964/5 7498/965/8 4679/961/581 +f 415/954/809 417/960/814 422/966/815 +f 418/967/816 422/966/815 4767/968/817 +f 417/960/814 4678/962/580 4677/969/594 +f 422/966/815 4677/969/594 7500/970/596 +f 418/967/816 423/971/818 419/955/810 +f 423/971/818 4757/922/788 4756/915/784 +f 4768/972/819 4769/973/820 423/971/818 +f 4769/973/820 7516/923/789 4757/922/788 +f 424/974/821 428/975/822 429/976/823 +f 425/977/824 429/976/823 4694/978/615 +f 428/975/822 4771/979/825 4772/980/826 +f 429/976/823 4772/980/826 7501/981/616 +f 425/977/824 430/982/827 426/983/828 +f 430/982/827 4457/984/44 4456/985/37 +f 4693/986/612 4692/987/611 430/982/827 +f 4692/987/611 7499/988/45 4457/984/44 +f 424/974/821 426/983/828 431/989/829 +f 427/990/830 431/989/829 4766/942/804 +f 426/983/828 4456/985/37 4455/991/36 +f 431/989/829 4455/991/36 7453/943/40 +f 427/990/830 432/992/831 428/975/822 +f 432/992/831 4770/993/832 4771/979/825 +f 4765/941/803 4764/933/797 432/992/831 +f 4764/933/797 7517/936/800 4770/993/832 +f 436/994/833 437/995/834 434/996/835 +f 437/995/834 4776/997/836 4777/998/837 +f 4774/999/838 4775/1000/839 437/995/834 +f 4775/1000/839 7512/1001/840 4776/997/836 +f 433/1002/841 434/996/835 438/1003/842 +f 435/1004/843 438/1003/842 4769/1005/820 +f 434/996/835 4777/998/837 4778/1006/844 +f 438/1003/842 4778/1006/844 7516/1007/789 +f 433/1002/841 435/1004/843 439/1008/845 +f 436/994/833 439/1008/845 4773/1009/846 +f 435/1004/843 4768/1010/819 4767/1011/817 +f 439/1008/845 4767/1011/817 7500/1012/596 +f 440/1013/847 443/1014/848 444/1015/849 +f 441/1016/850 444/1015/849 4772/1017/826 +f 443/1014/848 4780/1018/851 4781/1019/852 +f 444/1015/849 4781/1019/852 7501/1020/616 +f 440/1013/847 441/1016/850 445/1021/853 +f 442/1022/854 445/1021/853 4782/1023/855 +f 441/1016/850 4771/1024/825 4770/1025/832 +f 445/1021/853 4770/1025/832 7517/1026/800 +f 442/1022/854 446/1027/856 443/1014/848 +f 446/1027/856 4779/1028/857 4780/1018/851 +f 4783/1029/858 4784/1030/859 446/1027/856 +f 4784/1030/859 7513/1031/860 4779/1028/857 +f 447/1032/861 450/1033/862 451/1034/863 +f 448/1035/864 451/1034/863 4760/1036/792 +f 450/1033/862 4786/1037/865 4787/1038/866 +f 451/1034/863 4787/1038/866 7514/1039/757 +f 447/1032/861 448/1035/864 452/1040/867 +f 452/1040/867 4778/1006/844 4777/998/837 +f 4759/1041/791 4758/1042/787 452/1040/867 +f 4758/1042/787 7516/1007/789 4778/1006/844 +f 449/1043/868 453/1044/869 450/1033/862 +f 453/1044/869 4785/1045/870 4786/1037/865 +f 4777/998/837 4776/997/836 453/1044/869 +f 4776/997/836 7512/1001/840 4785/1045/870 +f 457/1046/871 458/1047/872 455/1048/873 +f 458/1047/872 4784/1030/859 4783/1029/858 +f 4789/1049/874 4790/1050/875 458/1047/872 +f 4790/1050/875 7513/1031/860 4784/1030/859 +f 454/1051/876 455/1048/873 459/1052/877 +f 459/1052/877 4763/1053/799 4762/1054/798 +f 4783/1029/858 4782/1023/855 459/1052/877 +f 4782/1023/855 7517/1026/800 4763/1053/799 +f 454/1051/876 456/1055/878 460/1056/879 +f 457/1046/871 460/1056/879 4788/1057/880 +f 456/1055/878 4762/1054/798 4761/1058/808 +f 460/1056/879 4761/1058/808 7515/1059/768 +f 464/1060/881 465/1061/882 462/1062/883 +f 465/1061/882 4791/1063/884 4792/1064/885 +f 4786/1037/865 4785/1045/870 465/1061/882 +f 4785/1045/870 7512/1001/840 4791/1063/884 +f 461/1065/886 462/1062/883 466/1066/887 +f 463/1067/888 466/1066/887 4748/1068/760 +f 462/1062/883 4792/1064/885 4793/1069/889 +f 466/1066/887 4793/1069/889 7510/1070/725 +f 461/1065/886 463/1067/888 467/1071/890 +f 464/1060/881 467/1071/890 4787/1038/866 +f 4747/1072/759 4746/1073/755 467/1071/890 +f 4746/1073/755 7514/1039/757 4787/1038/866 +f 468/1074/891 471/1075/892 472/1076/893 +f 472/1076/893 4751/1077/767 4750/1078/766 +f 471/1075/892 4789/1079/874 4788/1080/880 +f 4788/1080/880 7515/1081/768 4751/1077/767 +f 468/1074/891 469/1082/894 473/1083/895 +f 470/1084/896 473/1083/895 4794/1085/897 +f 469/1082/894 4750/1078/766 4749/1086/776 +f 473/1083/895 4749/1086/776 7511/1087/736 +f 470/1084/896 474/1088/898 471/1075/892 +f 474/1088/898 4790/1089/875 4789/1079/874 +f 4795/1090/899 4796/1091/900 474/1088/898 +f 4796/1091/900 7513/1092/860 4790/1089/875 +f 478/1093/901 479/1094/902 476/1095/903 +f 479/1094/902 4797/1096/904 4798/1097/905 +f 4792/1064/885 4791/1063/884 479/1094/902 +f 4791/1063/884 7512/1001/840 4797/1096/904 +f 475/1098/906 476/1095/903 480/1099/907 +f 477/1100/908 480/1099/907 4736/1101/728 +f 476/1095/903 4798/1097/905 4799/1102/909 +f 480/1099/907 4799/1102/909 7508/1103/694 +f 475/1098/906 477/1100/908 481/1104/910 +f 478/1093/901 481/1104/910 4793/1069/889 +f 477/1100/908 4735/1105/727 4734/1106/723 +f 481/1104/910 4734/1106/723 7510/1070/725 +f 482/1107/911 485/1108/912 486/1109/913 +f 483/1110/914 486/1109/913 4739/1111/735 +f 485/1108/912 4795/1090/899 4794/1085/897 +f 486/1109/913 4794/1085/897 7511/1087/736 +f 482/1107/911 483/1110/914 487/1112/915 +f 484/1113/916 487/1112/915 4800/1114/917 +f 483/1110/914 4738/1115/734 4737/1116/744 +f 487/1112/915 4737/1116/744 7509/1117/704 +f 484/1113/916 488/1118/918 485/1108/912 +f 488/1118/918 4796/1091/900 4795/1090/899 +f 4801/1119/919 4802/1120/920 488/1118/918 +f 4802/1120/920 7513/1092/860 4796/1091/900 +f 492/1121/921 493/1122/922 490/1123/923 +f 493/1122/922 4803/1124/924 4804/1125/925 +f 4798/1097/905 4797/1096/904 493/1122/922 +f 4797/1096/904 7512/1001/840 4803/1124/924 +f 489/1126/926 490/1123/923 494/1127/927 +f 494/1127/927 4724/1128/696 4723/1129/692 +f 490/1123/923 4804/1125/925 4805/1130/928 +f 4805/1130/928 7506/1131/662 4724/1128/696 +f 489/1126/926 491/1132/929 495/1133/930 +f 492/1121/921 495/1133/930 4799/1102/909 +f 491/1132/929 4723/1129/692 4722/1134/691 +f 495/1133/930 4722/1134/691 7508/1103/694 +f 496/1135/931 499/1136/932 500/1137/933 +f 497/1138/934 500/1137/933 4727/1139/703 +f 499/1136/932 4801/1119/919 4800/1114/917 +f 500/1137/933 4800/1114/917 7509/1117/704 +f 496/1135/931 497/1138/934 501/1140/935 +f 498/1141/936 501/1140/935 4806/1142/937 +f 4726/1143/702 4725/1144/712 501/1140/935 +f 4725/1144/712 7507/1145/672 4806/1142/937 +f 498/1141/936 502/1146/938 499/1136/932 +f 502/1146/938 4802/1120/920 4801/1119/919 +f 4807/1147/939 4808/1148/940 502/1146/938 +f 4808/1148/940 7513/1092/860 4802/1120/920 +f 506/1149/941 507/1150/942 504/1151/943 +f 507/1150/942 4809/1152/944 4810/1153/945 +f 4804/1125/925 4803/1124/924 507/1150/942 +f 4803/1124/924 7512/1001/840 4809/1152/944 +f 503/1154/946 504/1151/943 508/1155/947 +f 508/1155/947 4712/1156/664 4711/1157/659 +f 504/1151/943 4810/1153/945 4811/1158/948 +f 4811/1158/948 7504/1159/630 4712/1156/664 +f 503/1154/946 505/1160/949 509/1161/950 +f 506/1149/941 509/1161/950 4805/1130/928 +f 505/1160/949 4711/1157/659 4710/1162/658 +f 509/1161/950 4710/1162/658 7506/1131/662 +f 510/1163/951 513/1164/952 514/1165/953 +f 511/1166/954 514/1165/953 4715/1167/671 +f 513/1164/952 4807/1147/939 4806/1142/937 +f 514/1165/953 4806/1142/937 7507/1145/672 +f 510/1163/951 511/1166/954 515/1168/955 +f 512/1169/956 515/1168/955 4812/1170/957 +f 4714/1171/670 4713/1172/680 515/1168/955 +f 4713/1172/680 7505/1173/640 4812/1170/957 +f 512/1169/956 516/1174/958 513/1164/952 +f 516/1174/958 4808/1148/940 4807/1147/939 +f 4813/1175/959 4814/1176/960 516/1174/958 +f 4814/1176/960 7513/1092/860 4808/1148/940 +f 520/1177/961 521/1178/962 518/1179/963 +f 521/1178/962 4815/1180/964 4816/1181/965 +f 4810/1153/945 4809/1152/944 521/1178/962 +f 4809/1152/944 7512/1001/840 4815/1180/964 +f 517/1182/966 518/1179/963 522/1183/967 +f 522/1183/967 4700/1184/632 4699/1185/627 +f 518/1179/963 4816/1181/965 4817/1186/968 +f 4817/1186/968 7502/1187/592 4700/1184/632 +f 517/1182/966 519/1188/969 523/1189/970 +f 520/1177/961 523/1189/970 4811/1158/948 +f 519/1188/969 4699/1185/627 4698/1190/626 +f 523/1189/970 4698/1190/626 7504/1159/630 +f 524/1191/971 527/1192/972 528/1193/973 +f 525/1194/974 528/1193/973 4703/1195/639 +f 527/1192/972 4813/1175/959 4812/1170/957 +f 528/1193/973 4812/1170/957 7505/1173/640 +f 524/1191/971 525/1194/974 529/1196/975 +f 526/1197/976 529/1196/975 4818/1198/977 +f 4702/1199/638 4701/1200/648 529/1196/975 +f 4701/1200/648 7503/1201/604 4818/1198/977 +f 526/1197/976 530/1202/978 527/1192/972 +f 530/1202/978 4814/1176/960 4813/1175/959 +f 4819/1203/979 4820/1204/980 530/1202/978 +f 4820/1204/980 7513/1092/860 4814/1176/960 +f 534/1205/981 535/1206/982 532/1207/983 +f 535/1206/982 4775/1000/839 4774/999/838 +f 4816/1181/965 4815/1180/964 535/1206/982 +f 4815/1180/964 7512/1001/840 4775/1000/839 +f 531/1208/984 532/1207/983 536/1209/985 +f 533/1210/986 536/1209/985 4685/1211/595 +f 532/1207/983 4774/999/838 4773/1009/846 +f 536/1209/985 4773/1009/846 7500/1012/596 +f 531/1208/984 533/1210/986 537/1212/987 +f 534/1205/981 537/1212/987 4817/1186/968 +f 533/1210/986 4684/1213/589 4683/1214/588 +f 537/1212/987 4683/1214/588 7502/1187/592 +f 538/1215/988 541/1216/989 542/1217/990 +f 539/1218/991 542/1217/990 4688/1219/603 +f 541/1216/989 4819/1220/979 4818/1221/977 +f 542/1217/990 4818/1221/977 7503/1222/604 +f 538/1215/988 539/1218/991 543/1223/992 +f 540/1224/993 543/1223/992 4781/1019/852 +f 539/1218/991 4687/1225/602 4686/1226/614 +f 543/1223/992 4686/1226/614 7501/1020/616 +f 540/1224/993 544/1227/994 541/1216/989 +f 544/1227/994 4820/1228/980 4819/1220/979 +f 4780/1018/851 4779/1028/857 544/1227/994 +f 4779/1028/857 7513/1031/860 4820/1228/980 +f 549/1229/995 550/1230/996 546/1231/997 +f 550/1230/996 4824/1232/998 4825/1233/999 +f 4822/1234/1000 4823/1235/1001 550/1230/996 +f 4823/1235/1001 7540/1236/1002 4824/1232/998 +f 545/1237/1003 546/1231/997 551/1238/1004 +f 547/1239/1005 551/1238/1004 4827/1240/1006 +f 546/1231/997 4825/1233/999 4826/1241/1007 +f 551/1238/1004 4826/1241/1007 7625/1242/1008 +f 547/1239/1005 552/1243/1009 548/1244/1010 +f 552/1243/1009 4830/1245/1011 4831/1246/1012 +f 4828/1247/1013 4829/1248/1014 552/1243/1009 +f 4829/1248/1014 7627/1249/1015 4830/1245/1011 +f 545/1237/1003 548/1244/1010 553/1250/1016 +f 549/1229/995 553/1250/1016 4821/1251/1017 +f 548/1244/1010 4831/1246/1012 4832/1252/1018 +f 553/1250/1016 4832/1252/1018 7542/1253/1019 +f 558/1254/1020 559/1255/1021 555/1256/1022 +f 559/1255/1021 4833/1257/1023 4834/1258/1024 +f 4831/1246/1012 4830/1245/1011 559/1255/1021 +f 4830/1245/1011 7627/1249/1015 4833/1257/1023 +f 554/1259/1025 555/1256/1022 560/1260/1026 +f 556/1261/1027 560/1260/1026 4836/1262/1028 +f 555/1256/1022 4834/1258/1024 4835/1263/1029 +f 560/1260/1026 4835/1263/1029 7626/1264/1030 +f 556/1261/1027 561/1265/1031 557/1266/1032 +f 561/1265/1031 4839/1267/1033 4840/1268/1034 +f 4837/1269/1035 4838/1270/1036 561/1265/1031 +f 4838/1270/1036 7541/1271/1037 4839/1267/1033 +f 554/1259/1025 557/1266/1032 562/1272/1038 +f 558/1254/1020 562/1272/1038 4832/1252/1018 +f 557/1266/1032 4840/1268/1034 4841/1273/1039 +f 562/1272/1038 4841/1273/1039 7542/1253/1019 +f 567/1274/1040 568/1275/1041 564/1276/1042 +f 568/1275/1041 4845/1277/1043 4846/1278/1044 +f 4843/1279/1045 4844/1280/1046 568/1275/1041 +f 4844/1280/1046 7538/1281/1047 4845/1277/1043 +f 563/1282/1048 564/1276/1042 569/1283/1049 +f 565/1284/1050 569/1283/1049 4848/1285/1051 +f 564/1276/1042 4846/1278/1044 4847/1286/1052 +f 569/1283/1049 4847/1286/1052 7623/1287/1053 +f 565/1284/1050 570/1288/1054 566/1289/1055 +f 570/1288/1054 4826/1241/1007 4825/1233/999 +f 4849/1290/1056 4850/1291/1057 570/1288/1054 +f 4850/1291/1057 7625/1242/1008 4826/1241/1007 +f 563/1282/1048 566/1289/1055 571/1292/1058 +f 567/1274/1040 571/1292/1058 4842/1293/1059 +f 566/1289/1055 4825/1233/999 4824/1232/998 +f 571/1292/1058 4824/1232/998 7540/1236/1002 +f 576/1294/1060 577/1295/1061 573/1296/1062 +f 577/1295/1061 4851/1297/1063 4852/1298/1064 +f 4837/1269/1035 4836/1262/1028 577/1295/1061 +f 4836/1262/1028 7626/1264/1030 4851/1297/1063 +f 572/1299/1065 573/1296/1062 578/1300/1066 +f 574/1301/1067 578/1300/1066 4854/1302/1068 +f 573/1296/1062 4852/1298/1064 4853/1303/1069 +f 578/1300/1066 4853/1303/1069 7624/1304/1070 +f 574/1301/1067 579/1305/1071 575/1306/1072 +f 579/1305/1071 4857/1307/1073 4858/1308/1074 +f 4855/1309/1075 4856/1310/1076 579/1305/1071 +f 4856/1310/1076 7539/1311/1077 4857/1307/1073 +f 572/1299/1065 575/1306/1072 580/1312/1078 +f 576/1294/1060 580/1312/1078 4838/1270/1036 +f 575/1306/1072 4858/1308/1074 4859/1313/1079 +f 580/1312/1078 4859/1313/1079 7541/1271/1037 +f 581/1314/1080 585/1315/1081 586/1316/1082 +f 582/1317/1083 586/1316/1082 4863/1318/1084 +f 4861/1319/1085 4862/1320/1086 586/1316/1082 +f 4862/1320/1086 7536/1321/1087 4863/1318/1084 +f 582/1317/1083 587/1322/1088 583/1323/1089 +f 587/1322/1088 4866/1324/1090 4867/1325/1091 +f 4864/1326/1092 4865/1327/1093 587/1322/1088 +f 4865/1327/1093 7621/1328/1094 4866/1324/1090 +f 581/1314/1080 583/1323/1089 588/1329/1095 +f 588/1329/1095 4847/1330/1052 4846/1331/1044 +f 583/1323/1089 4867/1325/1091 4868/1332/1096 +f 4868/1332/1096 7623/1333/1053 4847/1330/1052 +f 581/1314/1080 584/1334/1097 589/1335/1098 +f 585/1315/1081 589/1335/1098 4860/1336/1099 +f 584/1334/1097 4846/1331/1044 4845/1337/1043 +f 589/1335/1098 4845/1337/1043 7538/1338/1047 +f 590/1339/1100 594/1340/1101 595/1341/1102 +f 591/1342/1103 595/1341/1102 4869/1343/1104 +f 4855/1309/1075 4854/1302/1068 595/1341/1102 +f 4854/1302/1068 7624/1304/1070 4869/1343/1104 +f 591/1342/1103 596/1344/1105 592/1345/1106 +f 596/1344/1105 4872/1346/1107 4873/1347/1108 +f 4870/1348/1109 4871/1349/1110 596/1344/1105 +f 4871/1349/1110 7622/1350/1111 4872/1346/1107 +f 590/1339/1100 592/1345/1106 597/1351/1112 +f 597/1351/1112 4875/1352/1113 4876/1353/1114 +f 592/1345/1106 4873/1347/1108 4874/1354/1115 +f 4874/1354/1115 7537/1355/1116 4875/1352/1113 +f 590/1339/1100 593/1356/1117 598/1357/1118 +f 594/1340/1101 598/1357/1118 4856/1310/1076 +f 593/1356/1117 4876/1353/1114 4877/1358/1119 +f 598/1357/1118 4877/1358/1119 7539/1311/1077 +f 599/1359/1120 603/1360/1121 604/1361/1122 +f 604/1361/1122 4881/1362/1123 4882/1363/1124 +f 4879/1364/1125 4880/1365/1126 604/1361/1122 +f 4880/1365/1126 7534/1366/1127 4881/1362/1123 +f 600/1367/1128 605/1368/1129 601/1369/1130 +f 605/1368/1129 4884/1370/1131 4885/1371/1132 +f 4882/1363/1124 4883/1372/1133 605/1368/1129 +f 4883/1372/1133 7619/1373/1134 4884/1370/1131 +f 599/1359/1120 601/1369/1130 606/1374/1135 +f 602/1375/1136 606/1374/1135 4865/1327/1093 +f 601/1369/1130 4885/1371/1132 4886/1376/1137 +f 606/1374/1135 4886/1376/1137 7621/1328/1094 +f 602/1375/1136 607/1377/1138 603/1360/1121 +f 603/1360/1121 607/1377/1138 4878/1378/1139 +f 4864/1326/1092 4863/1318/1084 607/1377/1138 +f 607/1377/1138 4863/1318/1084 7536/1321/1087 +f 608/1379/1140 612/1380/1141 613/1381/1142 +f 609/1382/1143 613/1381/1142 4887/1383/1144 +f 612/1380/1141 4873/1347/1108 4872/1346/1107 +f 613/1381/1142 4872/1346/1107 7622/1350/1111 +f 609/1382/1143 614/1384/1145 610/1385/1146 +f 614/1384/1145 4890/1386/1147 4891/1387/1148 +f 4888/1388/1149 4889/1389/1150 614/1384/1145 +f 4889/1389/1150 7620/1390/1151 4890/1386/1147 +f 608/1379/1140 610/1385/1146 615/1391/1152 +f 615/1391/1152 4893/1392/1153 4894/1393/1154 +f 4891/1387/1148 4892/1394/1155 615/1391/1152 +f 4892/1394/1155 7535/1395/1156 4893/1392/1153 +f 611/1396/1157 616/1397/1158 612/1380/1141 +f 616/1397/1158 4874/1354/1115 4873/1347/1108 +f 611/1396/1157 4894/1393/1154 4895/1398/1159 +f 616/1397/1158 4895/1398/1159 7537/1355/1116 +f 621/1399/1160 622/1400/1161 618/1401/1162 +f 622/1400/1161 4899/1402/1163 4900/1403/1164 +f 4897/1404/1165 4898/1405/1166 622/1400/1161 +f 4898/1405/1166 7532/1406/1167 4899/1402/1163 +f 618/1401/1162 623/1407/1168 619/1408/1169 +f 623/1407/1168 4902/1409/1170 4903/1410/1171 +f 4900/1403/1164 4901/1411/1172 623/1407/1168 +f 4901/1411/1172 7617/1412/1173 4902/1409/1170 +f 617/1413/1174 619/1408/1169 624/1414/1175 +f 620/1415/1176 624/1414/1175 4883/1372/1133 +f 619/1408/1169 4903/1410/1171 4904/1416/1177 +f 624/1414/1175 4904/1416/1177 7619/1373/1134 +f 617/1413/1174 620/1415/1176 625/1417/1178 +f 621/1399/1160 625/1417/1178 4896/1418/1179 +f 620/1415/1176 4882/1363/1124 4881/1362/1123 +f 625/1417/1178 4881/1362/1123 7534/1366/1127 +f 626/1419/1180 630/1420/1181 631/1421/1182 +f 627/1422/1183 631/1421/1182 4905/1423/1184 +f 630/1420/1181 4891/1424/1148 4890/1425/1147 +f 631/1421/1182 4890/1425/1147 7620/1426/1151 +f 627/1422/1183 632/1427/1185 628/1428/1186 +f 632/1427/1185 4908/1429/1187 4909/1430/1188 +f 4906/1431/1189 4907/1432/1190 632/1427/1185 +f 4907/1432/1190 7618/1433/1191 4908/1429/1187 +f 628/1428/1186 633/1434/1192 629/1435/1193 +f 633/1434/1192 4911/1436/1194 4912/1437/1195 +f 4909/1430/1188 4910/1438/1196 633/1434/1192 +f 4910/1438/1196 7533/1439/1197 4911/1436/1194 +f 626/1419/1180 629/1435/1193 634/1440/1198 +f 630/1420/1181 634/1440/1198 4892/1441/1155 +f 629/1435/1193 4912/1437/1195 4913/1442/1199 +f 634/1440/1198 4913/1442/1199 7535/1443/1156 +f 635/1444/1200 639/1445/1201 640/1446/1202 +f 636/1447/1203 640/1446/1202 4917/1448/1204 +f 639/1445/1201 4915/1449/1205 4916/1450/1206 +f 640/1446/1202 4916/1450/1206 7530/1451/1207 +f 636/1447/1203 641/1452/1208 637/1453/1209 +f 641/1452/1208 4920/1454/1210 4921/1455/1211 +f 4918/1456/1212 4919/1457/1213 641/1452/1208 +f 4919/1457/1213 7543/1458/1214 4920/1454/1210 +f 635/1444/1200 637/1453/1209 642/1459/1215 +f 638/1460/1216 642/1459/1215 4923/1461/1217 +f 637/1453/1209 4921/1455/1211 4922/1462/1218 +f 642/1459/1215 4922/1462/1218 7597/1463/1219 +f 638/1460/1216 643/1464/1220 639/1445/1201 +f 643/1464/1220 4914/1465/1221 4915/1449/1205 +f 4924/1466/1222 4925/1467/1223 643/1464/1220 +f 4925/1467/1223 7615/1468/1224 4914/1465/1221 +f 644/1469/1225 648/1470/1226 649/1471/1227 +f 645/1472/1228 649/1471/1227 4929/1473/1229 +f 648/1470/1226 4927/1474/1230 4928/1475/1231 +f 649/1471/1227 4928/1475/1231 7598/1476/1232 +f 645/1472/1228 650/1477/1233 646/1478/1234 +f 650/1477/1233 4932/1479/1235 4933/1480/1236 +f 4930/1481/1237 4931/1482/1238 650/1477/1233 +f 4931/1482/1238 7544/1483/1239 4932/1479/1235 +f 644/1469/1225 646/1478/1234 651/1484/1240 +f 647/1485/1241 651/1484/1240 4935/1486/1242 +f 646/1478/1234 4933/1480/1236 4934/1487/1243 +f 651/1484/1240 4934/1487/1243 7531/1488/1244 +f 647/1485/1241 652/1489/1245 648/1470/1226 +f 652/1489/1245 4926/1490/1246 4927/1474/1230 +f 4936/1491/1247 4937/1492/1248 652/1489/1245 +f 4937/1492/1248 7616/1493/1249 4926/1490/1246 +f 653/1494/1250 657/1495/1251 658/1496/1252 +f 654/1497/1253 658/1496/1252 4938/1498/1254 +f 657/1495/1251 4921/1499/1211 4920/1500/1210 +f 658/1496/1252 4920/1500/1210 7543/1501/1214 +f 654/1497/1253 659/1502/1255 655/1503/1256 +f 659/1502/1255 4941/1504/1257 4942/1505/1258 +f 4939/1506/1259 4940/1507/1260 659/1502/1255 +f 659/1502/1255 4940/1507/1260 7545/1508/1261 +f 653/1494/1250 655/1503/1256 660/1509/1262 +f 656/1510/1263 660/1509/1262 4944/1511/1264 +f 655/1503/1256 4942/1505/1258 4943/1512/1265 +f 660/1509/1262 4943/1512/1265 7599/1513/1266 +f 656/1510/1263 661/1514/1267 657/1495/1251 +f 661/1514/1267 4922/1515/1218 4921/1499/1211 +f 4945/1516/1268 4946/1517/1269 661/1514/1267 +f 4946/1517/1269 7597/1518/1219 4922/1515/1218 +f 662/1519/1270 666/1520/1271 667/1521/1272 +f 663/1522/1273 667/1521/1272 4950/1523/1274 +f 666/1520/1271 4948/1524/1275 4949/1525/1276 +f 667/1521/1272 4949/1525/1276 7600/1526/1277 +f 663/1522/1273 668/1527/1278 664/1528/1279 +f 668/1527/1278 4953/1529/1280 4954/1530/1281 +f 4951/1531/1282 4952/1532/1283 668/1527/1278 +f 668/1527/1278 4952/1532/1283 7546/1533/1284 +f 662/1519/1270 664/1528/1279 669/1534/1285 +f 665/1535/1286 669/1534/1285 4931/1482/1238 +f 664/1528/1279 4954/1530/1281 4955/1536/1287 +f 669/1534/1285 4955/1536/1287 7544/1483/1239 +f 665/1535/1286 670/1537/1288 666/1520/1271 +f 670/1537/1288 4947/1538/1289 4948/1524/1275 +f 4930/1481/1237 4929/1473/1229 670/1537/1288 +f 4929/1473/1229 7598/1476/1232 4947/1538/1289 +f 671/1539/1290 675/1540/1291 676/1541/1292 +f 672/1542/1293 676/1541/1292 4956/1543/1294 +f 675/1540/1291 4942/1544/1258 4941/1545/1257 +f 676/1541/1292 4941/1545/1257 7545/1546/1261 +f 672/1542/1293 677/1547/1295 673/1548/1296 +f 673/1548/1296 677/1547/1295 4959/1549/1297 +f 4957/1550/1298 4958/1551/1299 677/1547/1295 +f 677/1547/1295 4958/1551/1299 7547/1552/1300 +f 671/1539/1290 673/1548/1296 678/1553/1301 +f 674/1554/1302 678/1553/1301 4962/1555/1303 +f 673/1548/1296 4960/1556/1304 4961/1557/1305 +f 678/1553/1301 4961/1557/1305 7601/1558/1306 +f 674/1554/1302 679/1559/1307 675/1540/1291 +f 679/1559/1307 4943/1560/1265 4942/1544/1258 +f 4963/1561/1308 4964/1562/1309 679/1559/1307 +f 4964/1562/1309 7599/1563/1266 4943/1560/1265 +f 680/1564/1310 684/1565/1311 685/1566/1312 +f 681/1567/1313 685/1566/1312 4968/1568/1314 +f 684/1565/1311 4966/1569/1315 4967/1570/1316 +f 685/1566/1312 4967/1570/1316 7602/1571/1317 +f 681/1567/1313 686/1572/1318 682/1573/1319 +f 686/1572/1318 4971/1574/1320 4972/1575/1321 +f 681/1567/1313 4969/1576/1322 4970/1577/1323 +f 686/1572/1318 4970/1577/1323 7548/1578/1324 +f 680/1564/1310 682/1573/1319 687/1579/1325 +f 683/1580/1326 687/1579/1325 4952/1532/1283 +f 682/1573/1319 4972/1575/1321 4973/1581/1327 +f 687/1579/1325 4973/1581/1327 7546/1533/1284 +f 683/1580/1326 688/1582/1328 684/1565/1311 +f 688/1582/1328 4965/1583/1329 4966/1569/1315 +f 4951/1531/1282 4950/1523/1274 688/1582/1328 +f 4950/1523/1274 7600/1526/1277 4965/1583/1329 +f 693/1584/1330 694/1585/1331 690/1586/1332 +f 694/1585/1331 4974/1587/1333 4975/1588/1334 +f 4960/1556/1304 4959/1549/1297 694/1585/1331 +f 4959/1549/1297 7547/1552/1300 4974/1587/1333 +f 689/1589/1335 690/1586/1332 695/1590/1336 +f 691/1591/1337 695/1590/1336 4977/1592/1338 +f 690/1586/1332 4975/1588/1334 4976/1593/1339 +f 695/1590/1336 4976/1593/1339 7549/1594/1340 +f 691/1591/1337 696/1595/1341 692/1596/1342 +f 692/1596/1342 696/1595/1341 4980/1597/1343 +f 4978/1598/1344 4979/1599/1345 696/1595/1341 +f 696/1595/1341 4979/1599/1345 7603/1600/1346 +f 689/1589/1335 692/1596/1342 697/1601/1347 +f 693/1584/1330 697/1601/1347 4961/1557/1305 +f 4981/1602/1348 4982/1603/1349 697/1601/1347 +f 4982/1603/1349 7601/1558/1306 4961/1557/1305 +f 702/1604/1350 703/1605/1351 699/1606/1352 +f 703/1605/1351 4986/1607/1353 4987/1608/1354 +f 702/1604/1350 4984/1609/1355 4985/1610/1356 +f 703/1605/1351 4985/1610/1356 7604/1611/1357 +f 698/1612/1358 699/1606/1352 704/1613/1359 +f 700/1614/1360 704/1613/1359 4989/1615/1361 +f 699/1606/1352 4987/1608/1354 4988/1616/1362 +f 704/1613/1359 4988/1616/1362 7550/1617/1363 +f 700/1614/1360 705/1618/1364 701/1619/1365 +f 705/1618/1364 4970/1577/1323 4969/1576/1322 +f 4990/1620/1366 4991/1621/1367 705/1618/1364 +f 4991/1621/1367 7548/1578/1324 4970/1577/1323 +f 698/1612/1358 701/1619/1365 706/1622/1368 +f 706/1622/1368 4983/1623/1369 4984/1609/1355 +f 701/1619/1365 4969/1576/1322 4968/1568/1314 +f 4968/1568/1314 7602/1571/1317 4983/1623/1369 +f 711/1624/1370 712/1625/1371 708/1626/1372 +f 712/1625/1371 4992/1627/1373 4993/1628/1374 +f 4978/1598/1344 4977/1592/1338 712/1625/1371 +f 4977/1592/1338 7549/1594/1340 4992/1627/1373 +f 707/1629/1375 708/1626/1372 713/1630/1376 +f 709/1631/1377 713/1630/1376 4995/1632/1378 +f 708/1626/1372 4993/1628/1374 4994/1633/1379 +f 713/1630/1376 4994/1633/1379 7551/1634/1380 +f 709/1631/1377 714/1635/1381 710/1636/1382 +f 714/1635/1381 4998/1637/1383 4999/1638/1384 +f 4996/1639/1385 4997/1640/1386 714/1635/1381 +f 4997/1640/1386 7605/1641/1387 4998/1637/1383 +f 707/1629/1375 710/1636/1382 715/1642/1388 +f 711/1624/1370 715/1642/1388 4979/1599/1345 +f 710/1636/1382 4999/1638/1384 5000/1643/1389 +f 715/1642/1388 5000/1643/1389 7603/1600/1346 +f 720/1644/1390 721/1645/1391 717/1646/1392 +f 721/1645/1391 5004/1647/1393 5005/1648/1394 +f 5002/1649/1395 5003/1650/1396 721/1645/1391 +f 5003/1650/1396 7606/1651/1397 5004/1647/1393 +f 716/1652/1398 717/1646/1392 722/1653/1399 +f 718/1654/1400 722/1653/1399 5007/1655/1401 +f 717/1646/1392 5005/1648/1394 5006/1656/1402 +f 722/1653/1399 5006/1656/1402 7552/1657/1403 +f 718/1654/1400 723/1658/1404 719/1659/1405 +f 723/1658/1404 4988/1660/1362 4987/1661/1354 +f 5008/1662/1406 5009/1663/1407 723/1658/1404 +f 5009/1663/1407 7550/1664/1363 4988/1660/1362 +f 716/1652/1398 719/1659/1405 724/1665/1408 +f 720/1644/1390 724/1665/1408 5001/1666/1409 +f 719/1659/1405 4987/1661/1354 4986/1667/1353 +f 724/1665/1408 4986/1667/1353 7604/1668/1357 +f 729/1669/1410 730/1670/1411 726/1671/1412 +f 730/1670/1411 5010/1672/1413 5011/1673/1414 +f 4996/1639/1385 4995/1632/1378 730/1670/1411 +f 4995/1632/1378 7551/1634/1380 5010/1672/1413 +f 725/1674/1415 726/1671/1412 731/1675/1416 +f 727/1676/1417 731/1675/1416 5013/1677/1418 +f 726/1671/1412 5011/1673/1414 5012/1678/1419 +f 731/1675/1416 5012/1678/1419 7553/1679/1420 +f 727/1676/1417 732/1680/1421 728/1681/1422 +f 732/1680/1421 5016/1682/1423 5017/1683/1424 +f 5014/1684/1425 5015/1685/1426 732/1680/1421 +f 5015/1685/1426 7607/1686/1427 5016/1682/1423 +f 725/1674/1415 728/1681/1422 733/1687/1428 +f 729/1669/1410 733/1687/1428 4997/1640/1386 +f 728/1681/1422 5017/1683/1424 5018/1688/1429 +f 733/1687/1428 5018/1688/1429 7605/1641/1387 +f 738/1689/1430 739/1690/1431 735/1691/1432 +f 739/1690/1431 5022/1692/1433 5023/1693/1434 +f 5020/1694/1435 5021/1695/1436 739/1690/1431 +f 5021/1695/1436 7608/1696/1437 5022/1692/1433 +f 734/1697/1438 735/1691/1432 740/1698/1439 +f 736/1699/1440 740/1698/1439 5025/1700/1441 +f 735/1691/1432 5023/1693/1434 5024/1701/1442 +f 740/1698/1439 5024/1701/1442 7554/1702/1443 +f 736/1699/1440 741/1703/1444 737/1704/1445 +f 741/1703/1444 5006/1656/1402 5005/1648/1394 +f 5026/1705/1446 5027/1706/1447 741/1703/1444 +f 5027/1706/1447 7552/1657/1403 5006/1656/1402 +f 734/1697/1438 737/1704/1445 742/1707/1448 +f 738/1689/1430 742/1707/1448 5019/1708/1449 +f 737/1704/1445 5005/1648/1394 5004/1647/1393 +f 742/1707/1448 5004/1647/1393 7606/1651/1397 +f 747/1709/1450 748/1710/1451 744/1711/1452 +f 748/1710/1451 5028/1712/1453 5029/1713/1454 +f 5014/1684/1425 5013/1677/1418 748/1710/1451 +f 5013/1677/1418 7553/1679/1420 5028/1712/1453 +f 743/1714/1455 744/1711/1452 749/1715/1456 +f 749/1715/1456 5031/1716/1457 5032/1717/1458 +f 744/1711/1452 5029/1713/1454 5030/1718/1459 +f 749/1715/1456 5030/1718/1459 7555/1719/1460 +f 743/1714/1455 745/1720/1461 750/1721/1462 +f 746/1722/1463 750/1721/1462 5034/1723/1464 +f 745/1720/1461 5032/1717/1458 5033/1724/1465 +f 750/1721/1462 5033/1724/1465 7609/1725/1466 +f 746/1722/1463 751/1726/1467 747/1709/1450 +f 747/1709/1450 751/1726/1467 5015/1685/1426 +f 5035/1727/1468 5036/1728/1469 751/1726/1467 +f 5036/1728/1469 7607/1686/1427 5015/1685/1426 +f 752/1729/1470 756/1730/1471 757/1731/1472 +f 753/1732/1473 757/1731/1472 5040/1733/1474 +f 756/1730/1471 5038/1734/1475 5039/1735/1476 +f 757/1731/1472 5039/1735/1476 7610/1736/1477 +f 752/1729/1470 753/1732/1473 758/1737/1478 +f 754/1738/1479 758/1737/1478 5043/1739/1480 +f 5041/1740/1481 5042/1741/1482 758/1737/1478 +f 758/1737/1478 5042/1741/1482 7556/1742/1483 +f 754/1738/1479 759/1743/1484 755/1744/1485 +f 759/1743/1484 5024/1701/1442 5023/1693/1434 +f 5044/1745/1486 5045/1746/1487 759/1743/1484 +f 5045/1746/1487 7554/1702/1443 5024/1701/1442 +f 755/1744/1485 760/1747/1488 756/1730/1471 +f 760/1747/1488 5037/1748/1489 5038/1734/1475 +f 755/1744/1485 5023/1693/1434 5022/1692/1433 +f 5022/1692/1433 7608/1696/1437 5037/1748/1489 +f 765/1749/1490 766/1750/1491 762/1751/1492 +f 766/1750/1491 5046/1752/1493 5047/1753/1494 +f 5032/1717/1458 5031/1716/1457 766/1750/1491 +f 5031/1716/1457 7555/1719/1460 5046/1752/1493 +f 761/1754/1495 762/1751/1492 767/1755/1496 +f 763/1756/1497 767/1755/1496 5049/1757/1498 +f 762/1751/1492 5047/1753/1494 5048/1758/1499 +f 767/1755/1496 5048/1758/1499 7557/1759/1500 +f 763/1756/1497 768/1760/1501 764/1761/1502 +f 768/1760/1501 5052/1762/1503 5053/1763/1504 +f 5050/1764/1505 5051/1765/1506 768/1760/1501 +f 5051/1765/1506 7611/1766/1507 5052/1762/1503 +f 761/1754/1495 764/1761/1502 769/1767/1508 +f 769/1767/1508 5033/1724/1465 5032/1717/1458 +f 5053/1763/1504 5054/1768/1509 769/1767/1508 +f 5054/1768/1509 7609/1725/1466 5033/1724/1465 +f 774/1769/1510 775/1770/1511 771/1771/1512 +f 775/1770/1511 5058/1772/1513 5059/1773/1514 +f 5056/1774/1515 5057/1775/1516 775/1770/1511 +f 5057/1775/1516 7612/1776/1517 5058/1772/1513 +f 770/1777/1518 771/1771/1512 776/1778/1519 +f 772/1779/1520 776/1778/1519 5061/1780/1521 +f 771/1771/1512 5059/1773/1514 5060/1781/1522 +f 776/1778/1519 5060/1781/1522 7558/1782/1523 +f 772/1779/1520 777/1783/1524 773/1784/1525 +f 777/1783/1524 5042/1785/1482 5041/1786/1481 +f 5062/1787/1526 5063/1788/1527 777/1783/1524 +f 5063/1788/1527 7556/1789/1483 5042/1785/1482 +f 770/1777/1518 773/1784/1525 778/1790/1528 +f 778/1790/1528 5055/1791/1529 5056/1774/1515 +f 5041/1786/1481 5040/1792/1474 778/1790/1528 +f 5040/1792/1474 7610/1793/1477 5055/1791/1529 +f 783/1794/1530 784/1795/1531 780/1796/1532 +f 784/1795/1531 5064/1797/1533 5065/1798/1534 +f 5050/1799/1505 5049/1800/1498 784/1795/1531 +f 5049/1800/1498 7557/1801/1500 5064/1797/1533 +f 779/1802/1535 780/1796/1532 785/1803/1536 +f 781/1804/1537 785/1803/1536 5067/1805/1538 +f 780/1796/1532 5065/1798/1534 5066/1806/1539 +f 785/1803/1536 5066/1806/1539 7559/1807/1540 +f 781/1804/1537 786/1808/1541 782/1809/1542 +f 786/1808/1541 5070/1810/1543 5071/1811/1544 +f 5068/1812/1545 5069/1813/1546 786/1808/1541 +f 5069/1813/1546 7613/1814/1547 5070/1810/1543 +f 779/1802/1535 782/1809/1542 787/1815/1548 +f 783/1794/1530 787/1815/1548 5051/1816/1506 +f 782/1809/1542 5071/1811/1544 5072/1817/1549 +f 787/1815/1548 5072/1817/1549 7611/1818/1507 +f 792/1819/1550 793/1820/1551 789/1821/1552 +f 793/1820/1551 5076/1822/1553 5077/1823/1554 +f 5074/1824/1555 5075/1825/1556 793/1820/1551 +f 5075/1825/1556 7614/1826/1557 5076/1822/1553 +f 788/1827/1558 789/1821/1552 794/1828/1559 +f 790/1829/1560 794/1828/1559 5079/1830/1561 +f 789/1821/1552 5077/1823/1554 5078/1831/1562 +f 794/1828/1559 5078/1831/1562 7560/1832/1563 +f 790/1829/1560 795/1833/1564 791/1834/1565 +f 795/1833/1564 5060/1781/1522 5059/1773/1514 +f 5080/1835/1566 5081/1836/1567 795/1833/1564 +f 5081/1836/1567 7558/1782/1523 5060/1781/1522 +f 788/1827/1558 791/1834/1565 796/1837/1568 +f 792/1819/1550 796/1837/1568 5073/1838/1569 +f 791/1834/1565 5059/1773/1514 5058/1772/1513 +f 796/1837/1568 5058/1772/1513 7612/1776/1517 +f 801/1839/1570 802/1840/1571 798/1841/1572 +f 802/1840/1571 5082/1842/1573 5083/1843/1574 +f 5068/1812/1545 5067/1805/1538 802/1840/1571 +f 5067/1805/1538 7559/1807/1540 5082/1842/1573 +f 797/1844/1575 798/1841/1572 803/1845/1576 +f 799/1846/1577 803/1845/1576 5085/1847/1578 +f 798/1841/1572 5083/1843/1574 5084/1848/1579 +f 803/1845/1576 5084/1848/1579 7518/1849/1580 +f 799/1846/1577 804/1850/1581 800/1851/1582 +f 804/1850/1581 5088/1852/1583 5089/1853/1584 +f 5086/1854/1585 5087/1855/1586 804/1850/1581 +f 5087/1855/1586 7519/1856/1587 5088/1852/1583 +f 797/1844/1575 800/1851/1582 805/1857/1588 +f 801/1839/1570 805/1857/1588 5069/1813/1546 +f 800/1851/1582 5089/1853/1584 5090/1858/1589 +f 805/1857/1588 5090/1858/1589 7613/1814/1547 +f 810/1859/1590 811/1860/1591 807/1861/1592 +f 811/1860/1591 5087/1862/1586 5086/1863/1585 +f 5092/1864/1593 5093/1865/1594 811/1860/1591 +f 5093/1865/1594 7519/1866/1587 5087/1862/1586 +f 806/1867/1595 807/1861/1592 812/1868/1596 +f 808/1869/1597 812/1868/1596 5094/1870/1598 +f 807/1861/1592 5086/1863/1585 5085/1871/1578 +f 812/1868/1596 5085/1871/1578 7518/1872/1580 +f 808/1869/1597 813/1873/1599 809/1874/1600 +f 813/1873/1599 5078/1831/1562 5077/1823/1554 +f 5095/1875/1601 5096/1876/1602 813/1873/1599 +f 5096/1876/1602 7560/1832/1563 5078/1831/1562 +f 806/1867/1595 809/1874/1600 814/1877/1603 +f 810/1859/1590 814/1877/1603 5091/1878/1604 +f 809/1874/1600 5077/1823/1554 5076/1822/1553 +f 814/1877/1603 5076/1822/1553 7614/1826/1557 +f 819/1879/1605 820/1880/1606 816/1881/1607 +f 820/1880/1606 5100/1882/1608 5101/1883/1609 +f 5098/1884/1610 5099/1885/1611 820/1880/1606 +f 820/1880/1606 5099/1885/1611 7561/1886/1612 +f 815/1887/1613 816/1881/1607 821/1888/1614 +f 817/1889/1615 821/1888/1614 5103/1890/1616 +f 816/1881/1607 5101/1883/1609 5102/1891/1617 +f 821/1888/1614 5102/1891/1617 7579/1892/1618 +f 817/1889/1615 822/1893/1619 818/1894/1620 +f 822/1893/1619 5072/1895/1549 5071/1896/1544 +f 5104/1897/1621 5105/1898/1622 822/1893/1619 +f 5105/1898/1622 7611/1766/1507 5072/1895/1549 +f 815/1887/1613 818/1894/1620 823/1899/1623 +f 819/1879/1605 823/1899/1623 5097/1900/1624 +f 818/1894/1620 5071/1896/1544 5070/1901/1543 +f 823/1899/1623 5070/1901/1543 7613/1902/1547 +f 828/1903/1625 829/1904/1626 825/1905/1627 +f 829/1904/1626 5106/1906/1628 5107/1907/1629 +f 5074/1908/1555 5073/1909/1569 829/1904/1626 +f 5073/1909/1569 7612/1910/1517 5106/1906/1628 +f 824/1911/1630 825/1905/1627 830/1912/1631 +f 826/1913/1632 830/1912/1631 5109/1914/1633 +f 825/1905/1627 5107/1907/1629 5108/1915/1634 +f 830/1912/1631 5108/1915/1634 7580/1916/1635 +f 826/1913/1632 831/1917/1636 827/1918/1637 +f 831/1917/1636 5112/1919/1638 5113/1920/1639 +f 5110/1921/1640 5111/1922/1641 831/1917/1636 +f 831/1917/1636 5111/1922/1641 7562/1923/1642 +f 824/1911/1630 827/1918/1637 832/1924/1643 +f 828/1903/1625 832/1924/1643 5075/1925/1556 +f 827/1918/1637 5113/1920/1639 5114/1926/1644 +f 832/1924/1643 5114/1926/1644 7614/1927/1557 +f 833/1928/1645 837/1929/1646 838/1930/1647 +f 834/1931/1648 838/1930/1647 5115/1932/1649 +f 5104/1897/1621 5103/1890/1616 838/1930/1647 +f 5103/1890/1616 7579/1892/1618 5115/1932/1649 +f 834/1931/1648 839/1933/1650 835/1934/1651 +f 839/1933/1650 5118/1935/1652 5119/1936/1653 +f 5116/1937/1654 5117/1938/1655 839/1933/1650 +f 5117/1938/1655 7630/1939/1656 5118/1935/1652 +f 833/1928/1645 835/1934/1651 840/1940/1657 +f 836/1941/1658 840/1940/1657 5054/1768/1509 +f 835/1934/1651 5119/1936/1653 5120/1942/1659 +f 840/1940/1657 5120/1942/1659 7609/1725/1466 +f 833/1928/1645 836/1941/1658 841/1943/1660 +f 837/1929/1646 841/1943/1660 5105/1898/1622 +f 836/1941/1658 5053/1763/1504 5052/1762/1503 +f 841/1943/1660 5052/1762/1503 7611/1766/1507 +f 842/1944/1661 846/1945/1662 847/1946/1663 +f 843/1947/1664 847/1946/1663 5121/1948/1665 +f 846/1945/1662 5056/1949/1515 5055/1950/1529 +f 847/1946/1663 5055/1950/1529 7610/1951/1477 +f 843/1947/1664 848/1952/1666 844/1953/1667 +f 848/1952/1666 5124/1954/1668 5125/1955/1669 +f 5122/1956/1670 5123/1957/1671 848/1952/1666 +f 5123/1957/1671 7631/1958/1672 5124/1954/1668 +f 842/1944/1661 844/1953/1667 849/1959/1673 +f 849/1959/1673 5108/1915/1634 5107/1907/1629 +f 844/1953/1667 5125/1955/1669 5126/1960/1674 +f 5126/1960/1674 7580/1916/1635 5108/1915/1634 +f 842/1944/1661 845/1961/1675 850/1962/1676 +f 846/1945/1662 850/1962/1676 5057/1963/1516 +f 845/1961/1675 5107/1907/1629 5106/1906/1628 +f 850/1962/1676 5106/1906/1628 7612/1910/1517 +f 855/1964/1677 856/1965/1678 852/1966/1679 +f 856/1965/1678 5130/1967/1680 5131/1968/1681 +f 5128/1969/1682 5129/1970/1683 856/1965/1678 +f 5129/1970/1683 7577/1971/1684 5130/1967/1680 +f 851/1972/1685 852/1966/1679 857/1973/1686 +f 853/1974/1687 857/1973/1686 5036/1975/1469 +f 852/1966/1679 5131/1968/1681 5132/1976/1688 +f 857/1973/1686 5132/1976/1688 7607/1977/1427 +f 853/1974/1687 858/1978/1689 854/1979/1690 +f 858/1978/1689 5120/1980/1659 5119/1981/1653 +f 5035/1982/1468 5034/1983/1464 858/1978/1689 +f 5034/1983/1464 7609/1984/1466 5120/1980/1659 +f 851/1972/1685 854/1979/1690 859/1985/1691 +f 855/1964/1677 859/1985/1691 5127/1986/1692 +f 854/1979/1690 5119/1981/1653 5118/1987/1652 +f 859/1985/1691 5118/1987/1652 7630/1988/1656 +f 864/1989/1693 865/1990/1694 861/1991/1695 +f 865/1990/1694 5039/1992/1476 5038/1993/1475 +f 5122/1956/1670 5121/1948/1665 865/1990/1694 +f 5121/1948/1665 7610/1951/1477 5039/1992/1476 +f 860/1994/1696 861/1991/1695 866/1995/1697 +f 862/1996/1698 866/1995/1697 5133/1997/1699 +f 861/1991/1695 5038/1993/1475 5037/1998/1489 +f 866/1995/1697 5037/1998/1489 7608/1999/1437 +f 862/1996/1698 867/2000/1700 863/2001/1701 +f 867/2000/1700 5136/2002/1702 5137/2003/1703 +f 5134/2004/1704 5135/2005/1705 867/2000/1700 +f 5135/2005/1705 7578/2006/1706 5136/2002/1702 +f 860/1994/1696 863/2001/1701 868/2007/1707 +f 864/1989/1693 868/2007/1707 5123/1957/1671 +f 863/2001/1701 5137/2003/1703 5138/2008/1708 +f 868/2007/1707 5138/2008/1708 7631/1958/1672 +f 869/2009/1709 873/2010/1710 874/2011/1711 +f 870/2012/1712 874/2011/1711 5142/2013/1713 +f 873/2010/1710 5140/2014/1714 5141/2015/1715 +f 874/2011/1711 5141/2015/1715 7575/2016/1716 +f 870/2012/1712 875/2017/1717 871/2018/1718 +f 875/2017/1717 5018/2019/1429 5017/2020/1424 +f 5143/2021/1719 5144/2022/1720 875/2017/1717 +f 5144/2022/1720 7605/2023/1387 5018/2019/1429 +f 871/2018/1718 876/2024/1721 872/2025/1722 +f 876/2024/1721 5132/1976/1688 5131/1968/1681 +f 5017/2020/1424 5016/2026/1423 876/2024/1721 +f 5016/2026/1423 7607/1977/1427 5132/1976/1688 +f 869/2009/1709 872/2025/1722 877/2027/1723 +f 873/2010/1710 877/2027/1723 5139/2028/1724 +f 872/2025/1722 5131/1968/1681 5130/1967/1680 +f 877/2027/1723 5130/1967/1680 7577/1971/1684 +f 882/2029/1725 883/2030/1726 879/2031/1727 +f 883/2030/1726 5021/2032/1436 5020/2033/1435 +f 5134/2004/1704 5133/1997/1699 883/2030/1726 +f 5133/1997/1699 7608/1999/1437 5021/2032/1436 +f 879/2031/1727 884/2034/1728 880/2035/1729 +f 884/2034/1728 5145/2036/1730 5146/2037/1731 +f 5020/2033/1435 5019/2038/1449 884/2034/1728 +f 5019/2038/1449 7606/2039/1397 5145/2036/1730 +f 878/2040/1732 880/2035/1729 885/2041/1733 +f 881/2042/1734 885/2041/1733 5148/2043/1735 +f 880/2035/1729 5146/2037/1731 5147/2044/1736 +f 885/2041/1733 5147/2044/1736 7576/2045/1737 +f 878/2040/1732 881/2042/1734 886/2046/1738 +f 882/2029/1725 886/2046/1738 5135/2005/1705 +f 881/2042/1734 5149/2047/1739 5150/2048/1740 +f 886/2046/1738 5150/2048/1740 7578/2006/1706 +f 891/2049/1741 892/2050/1742 888/2051/1743 +f 892/2050/1742 5154/2052/1744 5155/2053/1745 +f 5152/2054/1746 5153/2055/1747 892/2050/1742 +f 5153/2055/1747 7573/2056/1748 5154/2052/1744 +f 887/2057/1749 888/2051/1743 893/2058/1750 +f 893/2058/1750 5000/2059/1389 4999/2060/1384 +f 888/2051/1743 5155/2053/1745 5156/2061/1751 +f 893/2058/1750 5156/2061/1751 7603/2062/1346 +f 887/2057/1749 889/2063/1752 894/2064/1753 +f 890/2065/1754 894/2064/1753 5144/2022/1720 +f 889/2063/1752 4999/2060/1384 4998/2066/1383 +f 894/2064/1753 4998/2066/1383 7605/2023/1387 +f 890/2065/1754 895/2067/1755 891/2049/1741 +f 895/2067/1755 5151/2068/1756 5152/2054/1746 +f 5143/2021/1719 5142/2013/1713 895/2067/1755 +f 5142/2013/1713 7575/2016/1716 5151/2068/1756 +f 896/2069/1757 900/2070/1758 901/2071/1759 +f 897/2072/1760 901/2071/1759 5003/2073/1396 +f 900/2070/1758 5146/2037/1731 5145/2036/1730 +f 901/2071/1759 5145/2036/1730 7606/2039/1397 +f 896/2069/1757 897/2072/1760 902/2074/1761 +f 898/2075/1762 902/2074/1761 5157/2076/1763 +f 5002/2077/1395 5001/2078/1409 902/2074/1761 +f 902/2074/1761 5001/2078/1409 7604/1611/1357 +f 898/2075/1762 903/2079/1764 899/2080/1765 +f 903/2079/1764 5160/2081/1766 5161/2082/1767 +f 5158/2083/1768 5159/2084/1769 903/2079/1764 +f 5159/2084/1769 7574/2085/1770 5160/2081/1766 +f 899/2080/1765 904/2086/1771 900/2070/1758 +f 904/2086/1771 5147/2044/1736 5146/2037/1731 +f 5161/2082/1767 5162/2087/1772 904/2086/1771 +f 5162/2087/1772 7576/2045/1737 5147/2044/1736 +f 909/2088/1773 910/2089/1774 906/2090/1775 +f 910/2089/1774 5166/2091/1776 5167/2092/1777 +f 5164/2093/1778 5165/2094/1779 910/2089/1774 +f 5165/2094/1779 7571/2095/1780 5166/2091/1776 +f 905/2096/1781 906/2090/1775 911/2097/1782 +f 907/2098/1783 911/2097/1782 4982/1603/1349 +f 906/2090/1775 5167/2092/1777 5168/2099/1784 +f 911/2097/1782 5168/2099/1784 7601/1558/1306 +f 907/2098/1783 912/2100/1785 908/2101/1786 +f 912/2100/1785 5156/2102/1751 5155/2103/1745 +f 4981/1602/1348 4980/1597/1343 912/2100/1785 +f 4980/1597/1343 7603/1600/1346 5156/2102/1751 +f 905/2096/1781 908/2101/1786 913/2104/1787 +f 909/2088/1773 913/2104/1787 5163/2105/1788 +f 908/2101/1786 5155/2103/1745 5154/2106/1744 +f 913/2104/1787 5154/2106/1744 7573/2107/1748 +f 918/2108/1789 919/2109/1790 915/2110/1791 +f 919/2109/1790 4985/1610/1356 4984/1609/1355 +f 5158/2083/1768 5157/2076/1763 919/2109/1790 +f 5157/2076/1763 7604/1611/1357 4985/1610/1356 +f 914/2111/1792 915/2110/1791 920/2112/1793 +f 916/2113/1794 920/2112/1793 5169/2114/1795 +f 915/2110/1791 4984/1609/1355 4983/1623/1369 +f 920/2112/1793 4983/1623/1369 7602/1571/1317 +f 916/2113/1794 921/2115/1796 917/2116/1797 +f 921/2115/1796 5172/2117/1798 5173/2118/1799 +f 5170/2119/1800 5171/2120/1801 921/2115/1796 +f 5171/2120/1801 7572/2121/1802 5172/2117/1798 +f 914/2111/1792 917/2116/1797 922/2122/1803 +f 918/2108/1789 922/2122/1803 5159/2084/1769 +f 917/2116/1797 5173/2118/1799 5174/2123/1804 +f 922/2122/1803 5174/2123/1804 7574/2085/1770 +f 927/2124/1805 928/2125/1806 924/2126/1807 +f 928/2125/1806 5178/2127/1808 5179/2128/1809 +f 5176/2129/1810 5177/2130/1811 928/2125/1806 +f 5177/2130/1811 7569/2131/1812 5178/2127/1808 +f 923/2132/1813 924/2126/1807 929/2133/1814 +f 925/2134/1815 929/2133/1814 4964/1562/1309 +f 924/2126/1807 5179/2128/1809 5180/2135/1816 +f 929/2133/1814 5180/2135/1816 7599/1563/1266 +f 925/2134/1815 930/2136/1817 926/2137/1818 +f 930/2136/1817 5168/2099/1784 5167/2092/1777 +f 4963/1561/1308 4962/1555/1303 930/2136/1817 +f 4962/1555/1303 7601/1558/1306 5168/2099/1784 +f 923/2132/1813 926/2137/1818 931/2138/1819 +f 927/2124/1805 931/2138/1819 5175/2139/1820 +f 926/2137/1818 5167/2092/1777 5166/2091/1776 +f 931/2138/1819 5166/2091/1776 7571/2095/1780 +f 936/2140/1821 937/2141/1822 933/2142/1823 +f 937/2141/1822 4967/1570/1316 4966/1569/1315 +f 5170/2119/1800 5169/2114/1795 937/2141/1822 +f 5169/2114/1795 7602/1571/1317 4967/1570/1316 +f 932/2143/1824 933/2142/1823 938/2144/1825 +f 934/2145/1826 938/2144/1825 5181/2146/1827 +f 933/2142/1823 4966/1569/1315 4965/1583/1329 +f 938/2144/1825 4965/1583/1329 7600/1526/1277 +f 934/2145/1826 939/2147/1828 935/2148/1829 +f 939/2147/1828 5184/2149/1830 5185/2150/1831 +f 5182/2151/1832 5183/2152/1833 939/2147/1828 +f 5183/2152/1833 7570/2153/1834 5184/2149/1830 +f 932/2143/1824 935/2148/1829 940/2154/1835 +f 936/2140/1821 940/2154/1835 5171/2120/1801 +f 935/2148/1829 5185/2150/1831 5186/2155/1836 +f 940/2154/1835 5186/2155/1836 7572/2121/1802 +f 945/2156/1837 946/2157/1838 942/2158/1839 +f 946/2157/1838 5190/2159/1840 5191/2160/1841 +f 5188/2161/1842 5189/2162/1843 946/2157/1838 +f 5189/2162/1843 7567/2163/1844 5190/2159/1840 +f 941/2164/1845 942/2158/1839 947/2165/1846 +f 943/2166/1847 947/2165/1846 4946/2167/1269 +f 942/2158/1839 5191/2160/1841 5192/2168/1848 +f 947/2165/1846 5192/2168/1848 7597/2169/1219 +f 943/2166/1847 948/2170/1849 944/2171/1850 +f 948/2170/1849 5180/2135/1816 5179/2128/1809 +f 4945/2172/1268 4944/2173/1264 948/2170/1849 +f 4944/2173/1264 7599/1563/1266 5180/2135/1816 +f 941/2164/1845 944/2171/1850 949/2174/1851 +f 945/2156/1837 949/2174/1851 5187/2175/1852 +f 944/2171/1850 5179/2128/1809 5178/2127/1808 +f 949/2174/1851 5178/2127/1808 7569/2131/1812 +f 954/2176/1853 955/2177/1854 951/2178/1855 +f 955/2177/1854 4949/1525/1276 4948/1524/1275 +f 5182/2151/1832 5181/2146/1827 955/2177/1854 +f 5181/2146/1827 7600/1526/1277 4949/1525/1276 +f 950/2179/1856 951/2178/1855 956/2180/1857 +f 952/2181/1858 956/2180/1857 5193/2182/1859 +f 951/2178/1855 4948/1524/1275 4947/1538/1289 +f 956/2180/1857 4947/1538/1289 7598/1476/1232 +f 952/2181/1858 957/2183/1860 953/2184/1861 +f 957/2183/1860 5196/2185/1862 5197/2186/1863 +f 5194/2187/1864 5195/2188/1865 957/2183/1860 +f 5195/2188/1865 7568/2189/1866 5196/2185/1862 +f 950/2179/1856 953/2184/1861 958/2190/1867 +f 954/2176/1853 958/2190/1867 5183/2152/1833 +f 953/2184/1861 5197/2186/1863 5198/2191/1868 +f 958/2190/1867 5198/2191/1868 7570/2153/1834 +f 959/2192/1869 963/2193/1870 964/2194/1871 +f 960/2195/1872 964/2194/1871 5202/2196/1873 +f 963/2193/1870 5200/2197/1874 5201/2198/1875 +f 964/2194/1871 5201/2198/1875 7565/2199/1876 +f 960/2195/1872 965/2200/1877 961/2201/1878 +f 961/2201/1878 965/2200/1877 4925/2202/1223 +f 5203/2203/1879 5204/2204/1880 965/2200/1877 +f 5204/2204/1880 7615/2205/1224 4925/2202/1223 +f 961/2201/1878 966/2206/1881 962/2207/1882 +f 966/2206/1881 5192/2168/1848 5191/2160/1841 +f 4924/2208/1222 4923/2209/1217 966/2206/1881 +f 4923/2209/1217 7597/2169/1219 5192/2168/1848 +f 959/2192/1869 962/2207/1882 967/2210/1883 +f 963/2193/1870 967/2210/1883 5199/2211/1884 +f 962/2207/1882 5191/2160/1841 5190/2159/1840 +f 967/2210/1883 5190/2159/1840 7567/2163/1844 +f 972/2212/1885 973/2213/1886 969/2214/1887 +f 973/2213/1886 4928/2215/1231 4927/2216/1230 +f 5194/2217/1864 5193/2218/1859 973/2213/1886 +f 5193/2218/1859 7598/2219/1232 4928/2215/1231 +f 969/2214/1887 974/2220/1888 970/2221/1889 +f 974/2220/1888 5205/2222/1890 5206/2223/1891 +f 969/2214/1887 4927/2216/1230 4926/2224/1246 +f 4926/2224/1246 7616/2225/1249 5205/2222/1890 +f 968/2226/1892 970/2221/1889 975/2227/1893 +f 971/2228/1894 975/2227/1893 5208/2229/1895 +f 970/2221/1889 5206/2223/1891 5207/2230/1896 +f 975/2227/1893 5207/2230/1896 7566/2231/1897 +f 968/2226/1892 971/2228/1894 976/2232/1898 +f 972/2212/1885 976/2232/1898 5195/2233/1865 +f 971/2228/1894 5209/2234/1899 5210/2235/1900 +f 976/2232/1898 5210/2235/1900 7568/2236/1866 +f 977/2237/1901 981/2238/1902 982/2239/1903 +f 978/2240/1904 982/2239/1903 5211/2241/1905 +f 5203/2203/1879 5202/2196/1873 982/2239/1903 +f 5202/2196/1873 7565/2199/1876 5211/2241/1905 +f 978/2240/1904 983/2242/1906 979/2243/1907 +f 983/2242/1906 5214/2244/1908 5215/2245/1909 +f 5212/2246/1910 5213/2247/1911 983/2242/1906 +f 5213/2247/1911 7632/2248/1912 5214/2244/1908 +f 977/2237/1901 979/2243/1907 984/2249/1913 +f 980/2250/1914 984/2249/1913 5217/2251/1915 +f 979/2243/1907 5215/2245/1909 5216/2252/1916 +f 5216/2252/1916 7628/2253/1917 5217/2251/1915 +f 980/2250/1914 985/2254/1918 981/2238/1902 +f 985/2254/1918 5204/2204/1880 5203/2203/1879 +f 5218/2255/1919 5219/2256/1920 985/2254/1918 +f 5219/2256/1920 7615/2205/1224 5204/2204/1880 +f 986/2257/1921 990/2258/1922 991/2259/1923 +f 987/2260/1924 991/2259/1923 5223/2261/1925 +f 990/2258/1922 5221/2262/1926 5222/2263/1927 +f 5222/2263/1927 7628/2253/1917 5223/2261/1925 +f 987/2260/1924 992/2264/1928 988/2265/1929 +f 992/2264/1928 5226/2266/1930 5227/2267/1931 +f 5224/2268/1932 5225/2269/1933 992/2264/1928 +f 5225/2269/1933 7633/2270/1934 5226/2266/1930 +f 986/2257/1921 988/2265/1929 993/2271/1935 +f 993/2271/1935 5207/2230/1896 5206/2223/1891 +f 988/2265/1929 5227/2267/1931 5228/2272/1936 +f 5228/2272/1936 7566/2231/1897 5207/2230/1896 +f 989/2273/1937 994/2274/1938 990/2258/1922 +f 994/2274/1938 5220/2275/1939 5221/2262/1926 +f 5206/2223/1891 5205/2222/1890 994/2274/1938 +f 5205/2222/1890 7616/2225/1249 5220/2275/1939 +f 999/2276/1940 1000/2277/1941 996/2278/1942 +f 1000/2277/1941 5099/1885/1611 5098/1884/1610 +f 5230/2279/1943 5231/2280/1944 1000/2277/1941 +f 5231/2280/1944 7561/1886/1612 5099/1885/1611 +f 995/2281/1945 996/2278/1942 1001/2282/1946 +f 997/2283/1947 1001/2282/1946 5090/2284/1589 +f 996/2278/1942 5098/1884/1610 5097/1900/1624 +f 5097/1900/1624 7613/1902/1547 5090/2284/1589 +f 997/2283/1947 1002/2285/1948 998/2286/1949 +f 1002/2285/1948 5232/2287/1950 5233/2288/1951 +f 5089/2289/1584 5088/2290/1583 1002/2285/1948 +f 5088/2290/1583 7519/2291/1587 5232/2287/1950 +f 995/2281/1945 998/2286/1949 1003/2292/1952 +f 999/2276/1940 1003/2292/1952 5229/2293/1953 +f 998/2286/1949 5233/2288/1951 5234/2294/1954 +f 5234/2294/1954 7563/2295/1955 5229/2293/1953 +f 1008/2296/1956 1009/2297/1957 1005/2298/1958 +f 1009/2297/1957 5093/2299/1594 5092/2300/1593 +f 5236/2301/1959 5237/2302/1960 1009/2297/1957 +f 5237/2302/1960 7519/2303/1587 5093/2299/1594 +f 1004/2304/1961 1005/2298/1958 1010/2305/1962 +f 1006/2306/1963 1010/2305/1962 5114/1926/1644 +f 1005/2298/1958 5092/2300/1593 5091/2307/1604 +f 5091/2307/1604 7614/1927/1557 5114/1926/1644 +f 1006/2306/1963 1011/2308/1964 1007/2309/1965 +f 1011/2308/1964 5238/2310/1966 5239/2311/1967 +f 5113/1920/1639 5112/1919/1638 1011/2308/1964 +f 5112/1919/1638 7562/1923/1642 5238/2310/1966 +f 1004/2304/1961 1007/2309/1965 1012/2312/1968 +f 1008/2296/1956 1012/2312/1968 5235/2313/1969 +f 1007/2309/1965 5239/2311/1967 5240/2314/1970 +f 5240/2314/1970 7564/2315/1971 5235/2313/1969 +f 1013/2316/1972 1017/2317/1973 1018/2318/1974 +f 1014/2319/1975 1018/2318/1974 5234/2294/1954 +f 1017/2317/1973 5242/2320/1976 5243/2321/1977 +f 1018/2318/1974 5243/2321/1977 7563/2295/1955 +f 1014/2319/1975 1019/2322/1978 1015/2323/1979 +f 1019/2322/1978 5244/2324/1980 5245/2325/1981 +f 5233/2288/1951 5232/2287/1950 1019/2322/1978 +f 5232/2287/1950 7519/2291/1587 5244/2324/1980 +f 1013/2316/1972 1015/2323/1979 1020/2326/1982 +f 1016/2327/1983 1020/2326/1982 5247/2328/1984 +f 1015/2323/1979 5245/2325/1981 5246/2329/1985 +f 1020/2326/1982 5246/2329/1985 7629/2330/1986 +f 1016/2327/1983 1021/2331/1987 1017/2317/1973 +f 1021/2331/1987 5241/2332/1988 5242/2320/1976 +f 5248/2333/1989 5249/2334/1990 1021/2331/1987 +f 5249/2334/1990 7634/2335/1991 5241/2332/1988 +f 1022/2336/1992 1026/2337/1993 1027/2338/1994 +f 1023/2339/1995 1027/2338/1994 5246/2340/1985 +f 1026/2337/1993 5251/2341/1996 5252/2342/1997 +f 1027/2338/1994 5252/2342/1997 7629/2343/1986 +f 1023/2339/1995 1028/2344/1998 1024/2345/1999 +f 1028/2344/1998 5237/2302/1960 5236/2301/1959 +f 5245/2346/1981 5244/2347/1980 1028/2344/1998 +f 5244/2347/1980 7519/2303/1587 5237/2302/1960 +f 1022/2336/1992 1024/2345/1999 1029/2348/2000 +f 1025/2349/2001 1029/2348/2000 5253/2350/2002 +f 1024/2345/1999 5236/2301/1959 5235/2313/1969 +f 1029/2348/2000 5235/2313/1969 7564/2315/1971 +f 1025/2349/2001 1030/2351/2003 1026/2337/1993 +f 1030/2351/2003 5250/2352/2004 5251/2341/1996 +f 5254/2353/2005 5255/2354/2006 1030/2351/2003 +f 5255/2354/2006 7635/2355/2007 5250/2352/2004 +f 1035/2356/2008 1036/2357/2009 1032/2358/2010 +f 1036/2357/2009 5216/2252/1916 5215/2245/1909 +f 5257/2359/2011 5258/2360/2012 1036/2357/2009 +f 5258/2360/2012 7628/2253/1917 5216/2252/1916 +f 1031/2361/2013 1032/2358/2010 1037/2362/2014 +f 1033/2363/2015 1037/2362/2014 5259/2364/2016 +f 1032/2358/2010 5215/2245/1909 5214/2244/1908 +f 1037/2362/2014 5214/2244/1908 7632/2248/1912 +f 1033/2363/2015 1038/2365/2017 1034/2366/2018 +f 1034/2366/2018 1038/2365/2017 5249/2334/1990 +f 5260/2367/2019 5261/2368/2020 1038/2365/2017 +f 1038/2365/2017 5261/2368/2020 7634/2335/1991 +f 1031/2361/2013 1034/2366/2018 1039/2369/2021 +f 1035/2356/2008 1039/2369/2021 5256/2370/2022 +f 1034/2366/2018 5248/2333/1989 5247/2328/1984 +f 1039/2369/2021 5247/2328/1984 7629/2330/1986 +f 1044/2371/2023 1045/2372/2024 1041/2373/2025 +f 1045/2372/2024 5262/2374/2026 5263/2375/2027 +f 1044/2371/2023 5251/2376/1996 5250/2377/2004 +f 1045/2372/2024 5250/2377/2004 7635/2378/2007 +f 1040/2379/2028 1041/2373/2025 1046/2380/2029 +f 1042/2381/2030 1046/2380/2029 5225/2269/1933 +f 1041/2373/2025 5263/2375/2027 5264/2382/2031 +f 1046/2380/2029 5264/2382/2031 7633/2270/1934 +f 1042/2381/2030 1047/2383/2032 1043/2384/2033 +f 1047/2383/2032 5258/2360/2012 5257/2359/2011 +f 5224/2268/1932 5223/2261/1925 1047/2383/2032 +f 5223/2261/1925 7628/2253/1917 5258/2360/2012 +f 1040/2379/2028 1043/2384/2033 1048/2385/2034 +f 1044/2371/2023 1048/2385/2034 5252/2386/1997 +f 1043/2384/2033 5257/2359/2011 5256/2370/2022 +f 1048/2385/2034 5256/2370/2022 7629/2330/1986 +f 1049/2387/2035 1053/2388/2036 1054/2389/2037 +f 1050/2390/2038 1054/2389/2037 5268/2391/2039 +f 1053/2388/2036 5266/2392/2040 5267/2393/2041 +f 1054/2389/2037 5267/2393/2041 7586/2394/2042 +f 1050/2390/2038 1055/2395/2043 1051/2396/2044 +f 1055/2395/2043 5271/2397/2045 5272/2398/2046 +f 5269/2399/2047 5270/2400/2048 1055/2395/2043 +f 5270/2400/2048 7588/2401/2049 5271/2397/2045 +f 1049/2387/2035 1051/2396/2044 1056/2402/2050 +f 1052/2403/2051 1056/2402/2050 4829/1248/1014 +f 1051/2396/2044 5272/2398/2046 5273/2404/2052 +f 1056/2402/2050 5273/2404/2052 7627/1249/1015 +f 1052/2403/2051 1057/2405/2053 1053/2388/2036 +f 1057/2405/2053 5265/2406/2054 5266/2392/2040 +f 4828/1247/1013 4827/1240/1006 1057/2405/2053 +f 4827/1240/1006 7625/1242/1008 5265/2406/2054 +f 1058/2407/2055 1062/2408/2056 1063/2409/2057 +f 1059/2410/2058 1063/2409/2057 5273/2404/2052 +f 1062/2408/2056 4834/1258/1024 4833/1257/1023 +f 1063/2409/2057 4833/1257/1023 7627/1249/1015 +f 1059/2410/2058 1064/2411/2059 1060/2412/2060 +f 1064/2411/2059 5274/2413/2061 5275/2414/2062 +f 5272/2398/2046 5271/2397/2045 1064/2411/2059 +f 5271/2397/2045 7588/2401/2049 5274/2413/2061 +f 1058/2407/2055 1060/2412/2060 1065/2415/2063 +f 1061/2416/2064 1065/2415/2063 5277/2417/2065 +f 1060/2412/2060 5275/2414/2062 5276/2418/2066 +f 1065/2415/2063 5276/2418/2066 7587/2419/2067 +f 1061/2416/2064 1066/2420/2068 1062/2408/2056 +f 1066/2420/2068 4835/1263/1029 4834/1258/1024 +f 5278/2421/2069 5279/2422/2070 1066/2420/2068 +f 5279/2422/2070 7626/1264/1030 4835/1263/1029 +f 1067/2423/2071 1071/2424/2072 1072/2425/2073 +f 1068/2426/2074 1072/2425/2073 5283/2427/2075 +f 5281/2428/2076 5282/2429/2077 1072/2425/2073 +f 5282/2429/2077 7584/2430/2078 5283/2427/2075 +f 1068/2426/2074 1073/2431/2079 1069/2432/2080 +f 1073/2431/2079 5267/2393/2041 5266/2392/2040 +f 5284/2433/2081 5285/2434/2082 1073/2431/2079 +f 5285/2434/2082 7586/2394/2042 5267/2393/2041 +f 1067/2423/2071 1069/2432/2080 1074/2435/2083 +f 1070/2436/2084 1074/2435/2083 4850/1291/1057 +f 1069/2432/2080 5266/2392/2040 5265/2406/2054 +f 1074/2435/2083 5265/2406/2054 7625/1242/1008 +f 1070/2436/2084 1075/2437/2085 1071/2424/2072 +f 1075/2437/2085 5280/2438/2086 5281/2428/2076 +f 4849/1290/1056 4848/1285/1051 1075/2437/2085 +f 4848/1285/1051 7623/1287/1053 5280/2438/2086 +f 1076/2439/2087 1080/2440/2088 1081/2441/2089 +f 1077/2442/2090 1081/2441/2089 5279/2422/2070 +f 1080/2440/2088 4852/1298/1064 4851/1297/1063 +f 1081/2441/2089 4851/1297/1063 7626/1264/1030 +f 1077/2442/2090 1082/2443/2091 1078/2444/2092 +f 1082/2443/2091 5286/2445/2093 5287/2446/2094 +f 5278/2421/2069 5277/2417/2065 1082/2443/2091 +f 5277/2417/2065 7587/2419/2067 5286/2445/2093 +f 1076/2439/2087 1078/2444/2092 1083/2447/2095 +f 1083/2447/2095 5289/2448/2096 5290/2449/2097 +f 1078/2444/2092 5287/2446/2094 5288/2450/2098 +f 5288/2450/2098 7585/2451/2099 5289/2448/2096 +f 1079/2452/2100 1084/2453/2101 1080/2440/2088 +f 1084/2453/2101 4853/1303/1069 4852/1298/1064 +f 5290/2449/2097 5291/2454/2102 1084/2453/2101 +f 5291/2454/2102 7624/1304/1070 4853/1303/1069 +f 1089/2455/2103 1090/2456/2104 1086/2457/2105 +f 1090/2456/2104 5295/2458/2106 5296/2459/2107 +f 5293/2460/2108 5294/2461/2109 1090/2456/2104 +f 5294/2461/2109 7582/2462/2110 5295/2458/2106 +f 1085/2463/2111 1086/2457/2105 1091/2464/2112 +f 1087/2465/2113 1091/2464/2112 5282/2466/2077 +f 1086/2457/2105 5296/2459/2107 5297/2467/2114 +f 1091/2464/2112 5297/2467/2114 7584/2468/2078 +f 1087/2465/2113 1092/2469/2115 1088/2470/2116 +f 1092/2469/2115 4868/1332/1096 4867/1325/1091 +f 5281/2471/2076 5280/2472/2086 1092/2469/2115 +f 5280/2472/2086 7623/1333/1053 4868/1332/1096 +f 1085/2463/2111 1088/2470/2116 1093/2473/2117 +f 1089/2455/2103 1093/2473/2117 5292/2474/2118 +f 1088/2470/2116 4867/1325/1091 4866/1324/1090 +f 1093/2473/2117 4866/1324/1090 7621/1328/1094 +f 1098/2475/2119 1099/2476/2120 1095/2477/2121 +f 1099/2476/2120 5291/2454/2102 5290/2449/2097 +f 4870/1348/1109 4869/1343/1104 1099/2476/2120 +f 4869/1343/1104 7624/1304/1070 5291/2454/2102 +f 1094/2478/2122 1095/2477/2121 1100/2479/2123 +f 1096/2480/2124 1100/2479/2123 5298/2481/2125 +f 1095/2477/2121 5290/2449/2097 5289/2448/2096 +f 1100/2479/2123 5289/2448/2096 7585/2451/2099 +f 1096/2480/2124 1101/2482/2126 1097/2483/2127 +f 1101/2482/2126 5301/2484/2128 5302/2485/2129 +f 5299/2486/2130 5300/2487/2131 1101/2482/2126 +f 5300/2487/2131 7583/2488/2132 5301/2484/2128 +f 1094/2478/2122 1097/2483/2127 1102/2489/2133 +f 1098/2475/2119 1102/2489/2133 4871/1349/1110 +f 1097/2483/2127 5302/2485/2129 5303/2490/2134 +f 1102/2489/2133 5303/2490/2134 7622/1350/1111 +f 1103/2491/2135 1107/2492/2136 1108/2493/2137 +f 1104/2494/2138 1108/2493/2137 5304/2495/2139 +f 1107/2492/2136 4903/1410/1171 4902/1409/1170 +f 1108/2493/2137 4902/1409/1170 7617/1412/1173 +f 1104/2494/2138 1109/2496/2140 1105/2497/2141 +f 1109/2496/2140 5307/2498/2142 5308/2499/2143 +f 5305/2500/2144 5306/2501/2145 1109/2496/2140 +f 5306/2501/2145 7638/2502/2146 5307/2498/2142 +f 1103/2491/2135 1105/2497/2141 1110/2503/2147 +f 1106/2504/2148 1110/2503/2147 5310/2505/2149 +f 1105/2497/2141 5308/2499/2143 5309/2506/2150 +f 1110/2503/2147 5309/2506/2150 7636/2507/2151 +f 1106/2504/2148 1111/2508/2152 1107/2492/2136 +f 1111/2508/2152 4904/1416/1177 4903/1410/1171 +f 5311/2509/2153 5312/2510/2154 1111/2508/2152 +f 5312/2510/2154 7619/1373/1134 4904/1416/1177 +f 1112/2511/2155 1116/2512/2156 1117/2513/2157 +f 1113/2514/2158 1117/2513/2157 5316/2515/2159 +f 1116/2512/2156 5314/2516/2160 5315/2517/2161 +f 1117/2513/2157 5315/2517/2161 7637/2518/2162 +f 1113/2514/2158 1118/2519/2163 1114/2520/2164 +f 1118/2519/2163 5319/2521/2165 5320/2522/2166 +f 5317/2523/2167 5318/2524/2168 1118/2519/2163 +f 5318/2524/2168 7639/2525/2169 5319/2521/2165 +f 1112/2511/2155 1114/2520/2164 1119/2526/2170 +f 1115/2527/2171 1119/2526/2170 4907/2528/1190 +f 1114/2520/2164 5320/2522/2166 5321/2529/2172 +f 1119/2526/2170 5321/2529/2172 7618/2530/1191 +f 1115/2527/2171 1120/2531/2173 1116/2512/2156 +f 1120/2531/2173 5313/2532/2174 5314/2516/2160 +f 4906/2533/1189 4905/2534/1184 1120/2531/2173 +f 4905/2534/1184 7620/2535/1151 5313/2532/2174 +f 1121/2536/2175 1125/2537/2176 1126/2538/2177 +f 1122/2539/2178 1126/2538/2177 5294/2461/2109 +f 1125/2537/2176 5323/2540/2179 5324/2541/2180 +f 1126/2538/2177 5324/2541/2180 7582/2462/2110 +f 1122/2539/2178 1127/2542/2181 1123/2543/2182 +f 1127/2542/2181 4886/1376/1137 4885/1371/1132 +f 5293/2460/2108 5292/2474/2118 1127/2542/2181 +f 5292/2474/2118 7621/1328/1094 4886/1376/1137 +f 1121/2536/2175 1123/2543/2182 1128/2544/2183 +f 1124/2545/2184 1128/2544/2183 5312/2510/2154 +f 1123/2543/2182 4885/1371/1132 4884/1370/1131 +f 1128/2544/2183 4884/1370/1131 7619/1373/1134 +f 1124/2545/2184 1129/2546/2185 1125/2537/2176 +f 1129/2546/2185 5322/2547/2186 5323/2540/2179 +f 5311/2509/2153 5310/2505/2149 1129/2546/2185 +f 5310/2505/2149 7636/2507/2151 5322/2547/2186 +f 1130/2548/2187 1134/2549/2188 1135/2550/2189 +f 1131/2551/2190 1135/2550/2189 4889/1389/1150 +f 1134/2549/2188 5314/2552/2160 5313/2553/2174 +f 1135/2550/2189 5313/2553/2174 7620/1390/1151 +f 1131/2551/2190 1136/2554/2191 1132/2555/2192 +f 1136/2554/2191 5303/2490/2134 5302/2485/2129 +f 4888/1388/1149 4887/1383/1144 1136/2554/2191 +f 4887/1383/1144 7622/1350/1111 5303/2490/2134 +f 1130/2548/2187 1132/2555/2192 1137/2556/2193 +f 1133/2557/2194 1137/2556/2193 5325/2558/2195 +f 1132/2555/2192 5302/2485/2129 5301/2484/2128 +f 1137/2556/2193 5301/2484/2128 7583/2488/2132 +f 1133/2557/2194 1138/2559/2196 1134/2549/2188 +f 1138/2559/2196 5315/2560/2161 5314/2552/2160 +f 5326/2561/2197 5327/2562/2198 1138/2559/2196 +f 5327/2562/2198 7637/2563/2162 5315/2560/2161 +f 1143/2564/2199 1144/2565/2200 1140/2566/2201 +f 1144/2565/2200 5331/2567/2202 5332/2568/2203 +f 5329/2569/2204 5330/2570/2205 1144/2565/2200 +f 5330/2570/2205 7595/2571/2206 5331/2567/2202 +f 1139/2572/2207 1140/2566/2201 1145/2573/2208 +f 1141/2574/2209 1145/2573/2208 5334/2575/2210 +f 1140/2566/2201 5332/2568/2203 5333/2576/2211 +f 1145/2573/2208 5333/2576/2211 7641/2577/2212 +f 1141/2574/2209 1146/2578/2213 1142/2579/2214 +f 1146/2578/2213 5337/2580/2215 5338/2581/2216 +f 5335/2582/2217 5336/2583/2218 1146/2578/2213 +f 5336/2583/2218 7640/2584/2219 5337/2580/2215 +f 1139/2572/2207 1142/2579/2214 1147/2585/2220 +f 1143/2564/2199 1147/2585/2220 5328/2586/2221 +f 1142/2579/2214 5338/2581/2216 5339/2587/2222 +f 5339/2587/2222 7638/2502/2146 5328/2586/2221 +f 1152/2588/2223 1153/2589/2224 1149/2590/2225 +f 1153/2589/2224 5336/2583/2218 5335/2582/2217 +f 5341/2591/2226 5342/2592/2227 1153/2589/2224 +f 5342/2592/2227 7640/2584/2219 5336/2583/2218 +f 1148/2593/2228 1149/2590/2225 1154/2594/2229 +f 1150/2595/2230 1154/2594/2229 5343/2596/2231 +f 1149/2590/2225 5335/2582/2217 5334/2575/2210 +f 1154/2594/2229 5334/2575/2210 7641/2577/2212 +f 1150/2595/2230 1155/2597/2232 1151/2598/2233 +f 1155/2597/2232 5346/2599/2234 5347/2600/2235 +f 5344/2601/2236 5345/2602/2237 1155/2597/2232 +f 5345/2602/2237 7596/2603/2238 5346/2599/2234 +f 1148/2593/2228 1151/2598/2233 1156/2604/2239 +f 1152/2588/2223 1156/2604/2239 5340/2605/2240 +f 1151/2598/2233 5347/2600/2235 5348/2606/2241 +f 5348/2606/2241 7639/2525/2169 5340/2605/2240 +f 1161/2607/2242 1162/2608/2243 1158/2609/2244 +f 1162/2608/2243 5309/2506/2150 5308/2499/2143 +f 5350/2610/2245 5351/2611/2246 1162/2608/2243 +f 5351/2611/2246 7636/2507/2151 5309/2506/2150 +f 1157/2612/2247 1158/2609/2244 1163/2613/2248 +f 1159/2614/2249 1163/2613/2248 5339/2587/2222 +f 1158/2609/2244 5308/2499/2143 5307/2498/2142 +f 1163/2613/2248 5307/2498/2142 7638/2502/2146 +f 1159/2614/2249 1164/2615/2250 1160/2616/2251 +f 1164/2615/2250 5352/2617/2252 5353/2618/2253 +f 5338/2581/2216 5337/2580/2215 1164/2615/2250 +f 5337/2580/2215 7640/2584/2219 5352/2617/2252 +f 1157/2612/2247 1160/2616/2251 1165/2619/2254 +f 1161/2607/2242 1165/2619/2254 5349/2620/2255 +f 1160/2616/2251 5353/2618/2253 5354/2621/2256 +f 1165/2619/2254 5354/2621/2256 7520/2622/2257 +f 1170/2623/2258 1171/2624/2259 1167/2625/2260 +f 1171/2624/2259 5342/2592/2227 5341/2591/2226 +f 5353/2618/2253 5352/2617/2252 1171/2624/2259 +f 5352/2617/2252 7640/2584/2219 5342/2592/2227 +f 1166/2626/2261 1167/2625/2260 1172/2627/2262 +f 1168/2628/2263 1172/2627/2262 5318/2524/2168 +f 1167/2625/2260 5341/2591/2226 5340/2605/2240 +f 1172/2627/2262 5340/2605/2240 7639/2525/2169 +f 1168/2628/2263 1173/2629/2264 1169/2630/2265 +f 1173/2629/2264 5355/2631/2266 5356/2632/2267 +f 5317/2523/2167 5316/2515/2159 1173/2629/2264 +f 5316/2515/2159 7637/2518/2162 5355/2631/2266 +f 1166/2626/2261 1169/2630/2265 1174/2633/2268 +f 1170/2623/2258 1174/2633/2268 5354/2621/2256 +f 1169/2630/2265 5356/2632/2267 5357/2634/2269 +f 1174/2633/2268 5357/2634/2269 7520/2622/2257 +f 1179/2635/2270 1180/2636/2271 1176/2637/2272 +f 1180/2636/2271 5361/2638/2273 5362/2639/2274 +f 5359/2640/2275 5360/2641/2276 1180/2636/2271 +f 5360/2641/2276 7581/2642/2277 5361/2638/2273 +f 1175/2643/2278 1176/2637/2272 1181/2644/2279 +f 1181/2644/2279 5324/2541/2180 5323/2540/2179 +f 1176/2637/2272 5362/2639/2274 5363/2645/2280 +f 1181/2644/2279 5363/2645/2280 7582/2462/2110 +f 1175/2643/2278 1177/2646/2281 1182/2647/2282 +f 1178/2648/2283 1182/2647/2282 5351/2611/2246 +f 1177/2646/2281 5323/2540/2179 5322/2547/2186 +f 1182/2647/2282 5322/2547/2186 7636/2507/2151 +f 1175/2643/2278 1178/2648/2283 1183/2649/2284 +f 1179/2635/2270 1183/2649/2284 5358/2650/2285 +f 5350/2610/2245 5349/2620/2255 1183/2649/2284 +f 1183/2649/2284 5349/2620/2255 7520/2622/2257 +f 1184/2651/2286 1188/2652/2287 1189/2653/2288 +f 1185/2654/2289 1189/2653/2288 5327/2655/2198 +f 1188/2652/2287 5356/2632/2267 5355/2631/2266 +f 1189/2653/2288 5355/2631/2266 7637/2518/2162 +f 1184/2651/2286 1185/2654/2289 1190/2656/2290 +f 1186/2657/2291 1190/2656/2290 5364/2658/2292 +f 5326/2659/2197 5325/2660/2195 1190/2656/2290 +f 1190/2656/2290 5325/2660/2195 7583/2661/2132 +f 1186/2657/2291 1191/2662/2293 1187/2663/2294 +f 1191/2662/2293 5360/2641/2276 5359/2640/2275 +f 5365/2664/2295 5366/2665/2296 1191/2662/2293 +f 5366/2665/2296 7581/2642/2277 5360/2641/2276 +f 1184/2651/2286 1187/2663/2294 1192/2666/2297 +f 1192/2666/2297 5357/2634/2269 5356/2632/2267 +f 1187/2663/2294 5359/2640/2275 5358/2650/2285 +f 1192/2666/2297 5358/2650/2285 7520/2622/2257 +f 1193/2667/2298 1197/2668/2299 1198/2669/2300 +f 1194/2670/2301 1198/2669/2300 5370/2671/2302 +f 1197/2668/2299 5368/2672/2303 5369/2673/2304 +f 1198/2669/2300 5369/2673/2304 7593/2674/2305 +f 1194/2670/2301 1199/2675/2306 1195/2676/2307 +f 1199/2675/2306 5373/2677/2308 5374/2678/2309 +f 5371/2679/2310 5372/2680/2311 1199/2675/2306 +f 5372/2680/2311 7644/2681/2312 5373/2677/2308 +f 1193/2667/2298 1195/2676/2307 1200/2682/2313 +f 1196/2683/2314 1200/2682/2313 5376/2684/2315 +f 1195/2676/2307 5374/2678/2309 5375/2685/2316 +f 1200/2682/2313 5375/2685/2316 7642/2686/2317 +f 1196/2683/2314 1201/2687/2318 1197/2668/2299 +f 1201/2687/2318 5367/2688/2319 5368/2672/2303 +f 5377/2689/2320 5378/2690/2321 1201/2687/2318 +f 5378/2690/2321 7595/2691/2206 5367/2688/2319 +f 1202/2692/2322 1206/2693/2323 1207/2694/2324 +f 1203/2695/2325 1207/2694/2324 5382/2696/2326 +f 1206/2693/2323 5380/2697/2327 5381/2698/2328 +f 1207/2694/2324 5381/2698/2328 7643/2699/2329 +f 1203/2695/2325 1208/2700/2330 1204/2701/2331 +f 1208/2700/2330 5385/2702/2332 5386/2703/2333 +f 5383/2704/2334 5384/2705/2335 1208/2700/2330 +f 5384/2705/2335 7645/2706/2336 5385/2702/2332 +f 1202/2692/2322 1204/2701/2331 1209/2707/2337 +f 1205/2708/2338 1209/2707/2337 5388/2709/2339 +f 1204/2701/2331 5386/2703/2333 5387/2710/2340 +f 1209/2707/2337 5387/2710/2340 7594/2711/2341 +f 1205/2708/2338 1210/2712/2342 1206/2693/2323 +f 1210/2712/2342 5379/2713/2343 5380/2697/2327 +f 5389/2714/2344 5390/2715/2345 1210/2712/2342 +f 5390/2715/2345 7596/2716/2238 5379/2713/2343 +f 1215/2717/2346 1216/2718/2347 1212/2719/2348 +f 1216/2718/2347 5394/2720/2349 5395/2721/2350 +f 5392/2722/2351 5393/2723/2352 1216/2718/2347 +f 5393/2723/2352 7591/2724/2353 5394/2720/2349 +f 1211/2725/2354 1212/2719/2348 1217/2726/2355 +f 1213/2727/2356 1217/2726/2355 5397/2728/2357 +f 1212/2719/2348 5395/2721/2350 5396/2729/2358 +f 1217/2726/2355 5396/2729/2358 7646/2730/2359 +f 1213/2727/2356 1218/2731/2360 1214/2732/2361 +f 1214/2732/2361 1218/2731/2360 5372/2733/2311 +f 5398/2734/2362 5399/2735/2363 1218/2731/2360 +f 1218/2731/2360 5399/2735/2363 7644/2736/2312 +f 1214/2732/2361 1219/2737/2364 1215/2717/2346 +f 1219/2737/2364 5391/2738/2365 5392/2722/2351 +f 5371/2739/2310 5370/2740/2302 1219/2737/2364 +f 5370/2740/2302 7593/2741/2305 5391/2738/2365 +f 1224/2742/2366 1225/2743/2367 1221/2744/2368 +f 1225/2743/2367 5400/2745/2369 5401/2746/2370 +f 1224/2742/2366 5386/2747/2333 5385/2748/2332 +f 1225/2743/2367 5385/2748/2332 7645/2749/2336 +f 1220/2750/2371 1221/2744/2368 1226/2751/2372 +f 1222/2752/2373 1226/2751/2372 5403/2753/2374 +f 1221/2744/2368 5401/2746/2370 5402/2754/2375 +f 1226/2751/2372 5402/2754/2375 7647/2755/2376 +f 1222/2752/2373 1227/2756/2377 1223/2757/2378 +f 1227/2756/2377 5406/2758/2379 5407/2759/2380 +f 5404/2760/2381 5405/2761/2382 1227/2756/2377 +f 5405/2761/2382 7592/2762/2383 5406/2758/2379 +f 1223/2757/2378 1228/2763/2384 1224/2742/2366 +f 1228/2763/2384 5387/2764/2340 5386/2747/2333 +f 5407/2759/2380 5408/2765/2385 1228/2763/2384 +f 5408/2765/2385 7594/2766/2341 5387/2764/2340 +f 1233/2767/2386 1234/2768/2387 1230/2769/2388 +f 1234/2768/2387 5412/2770/2389 5413/2771/2390 +f 5410/2772/2391 5411/2773/2392 1234/2768/2387 +f 5411/2773/2392 7590/2774/2393 5412/2770/2389 +f 1229/2775/2394 1230/2769/2388 1235/2776/2395 +f 1231/2777/2396 1235/2776/2395 5415/2778/2397 +f 1230/2769/2388 5413/2771/2390 5414/2779/2398 +f 1235/2776/2395 5414/2779/2398 7648/2780/2399 +f 1231/2777/2396 1236/2781/2400 1232/2782/2401 +f 1236/2781/2400 5396/2729/2358 5395/2721/2350 +f 5416/2783/2402 5417/2784/2403 1236/2781/2400 +f 5417/2784/2403 7646/2730/2359 5396/2729/2358 +f 1229/2775/2394 1232/2782/2401 1237/2785/2404 +f 1233/2767/2386 1237/2785/2404 5409/2786/2405 +f 1232/2782/2401 5395/2721/2350 5394/2720/2349 +f 1237/2785/2404 5394/2720/2349 7591/2724/2353 +f 1242/2787/2406 1243/2788/2407 1239/2789/2408 +f 1243/2788/2407 5418/2790/2409 5419/2791/2410 +f 5404/2792/2381 5403/2793/2374 1243/2788/2407 +f 5403/2793/2374 7647/2794/2376 5418/2790/2409 +f 1238/2795/2411 1239/2789/2408 1244/2796/2412 +f 1240/2797/2413 1244/2796/2412 5421/2798/2414 +f 1239/2789/2408 5419/2791/2410 5420/2799/2415 +f 1244/2796/2412 5420/2799/2415 7649/2800/2416 +f 1240/2797/2413 1245/2801/2417 1241/2802/2418 +f 1245/2801/2417 5424/2803/2419 5425/2804/2420 +f 5422/2805/2421 5423/2806/2422 1245/2801/2417 +f 5423/2806/2422 7590/2807/2393 5424/2803/2419 +f 1238/2795/2411 1241/2802/2418 1246/2808/2423 +f 1242/2787/2406 1246/2808/2423 5405/2809/2382 +f 1241/2802/2418 5425/2804/2420 5426/2810/2424 +f 1246/2808/2423 5426/2810/2424 7592/2811/2383 +f 1247/2812/2425 1251/2813/2426 1252/2814/2427 +f 1248/2815/2428 1252/2814/2427 5430/2816/2429 +f 1251/2813/2426 5428/2817/2430 5429/2818/2431 +f 1252/2814/2427 5429/2818/2431 7589/2819/2432 +f 1247/2812/2425 1248/2815/2428 1253/2820/2433 +f 1249/2821/2434 1253/2820/2433 5433/2822/2435 +f 5431/2823/2436 5432/2824/2437 1253/2820/2433 +f 5432/2824/2437 7522/2825/2438 5433/2822/2435 +f 1249/2821/2434 1254/2826/2439 1250/2827/2440 +f 1254/2826/2439 5414/2828/2398 5413/2829/2390 +f 5434/2830/2441 5435/2831/2442 1254/2826/2439 +f 5435/2831/2442 7648/2832/2399 5414/2828/2398 +f 1247/2812/2425 1250/2827/2440 1255/2833/2443 +f 1251/2813/2426 1255/2833/2443 5427/2834/2444 +f 1250/2827/2440 5413/2829/2390 5412/2835/2389 +f 1255/2833/2443 5412/2835/2389 7590/2836/2393 +f 1260/2837/2445 1261/2838/2446 1257/2839/2447 +f 1261/2838/2446 5436/2840/2448 5437/2841/2449 +f 5422/2842/2421 5421/2843/2414 1261/2838/2446 +f 5421/2843/2414 7649/2844/2416 5436/2840/2448 +f 1256/2845/2450 1257/2839/2447 1262/2846/2451 +f 1262/2846/2451 5432/2847/2437 5431/2848/2436 +f 1257/2839/2447 5437/2841/2449 5438/2849/2452 +f 5438/2849/2452 7522/2850/2438 5432/2847/2437 +f 1256/2845/2450 1258/2851/2453 1263/2852/2454 +f 1259/2853/2455 1263/2852/2454 5429/2854/2431 +f 1258/2851/2453 5431/2848/2436 5430/2855/2429 +f 1263/2852/2454 5430/2855/2429 7589/2856/2432 +f 1256/2845/2450 1259/2853/2455 1264/2857/2456 +f 1260/2837/2445 1264/2857/2456 5423/2858/2422 +f 1259/2853/2455 5428/2859/2430 5427/2860/2444 +f 1264/2857/2456 5427/2860/2444 7590/2861/2393 +f 1269/2862/2457 1270/2863/2458 1266/2864/2459 +f 1270/2863/2458 5333/2865/2211 5332/2866/2203 +f 5440/2867/2460 5441/2868/2461 1270/2863/2458 +f 5441/2868/2461 7641/2869/2212 5333/2865/2211 +f 1265/2870/2462 1266/2864/2459 1271/2871/2463 +f 1267/2872/2464 1271/2871/2463 5378/2690/2321 +f 1266/2864/2459 5332/2866/2203 5331/2873/2202 +f 1271/2871/2463 5331/2873/2202 7595/2691/2206 +f 1267/2872/2464 1272/2874/2465 1268/2875/2466 +f 1272/2874/2465 5442/2876/2467 5443/2877/2468 +f 5377/2689/2320 5376/2684/2315 1272/2874/2465 +f 5376/2684/2315 7642/2686/2317 5442/2876/2467 +f 1265/2870/2462 1268/2875/2466 1273/2878/2469 +f 1269/2862/2457 1273/2878/2469 5439/2879/2470 +f 1268/2875/2466 5443/2877/2468 5444/2880/2471 +f 1273/2878/2469 5444/2880/2471 7521/2881/2472 +f 1278/2882/2473 1279/2883/2474 1275/2884/2475 +f 1279/2883/2474 5381/2698/2328 5380/2697/2327 +f 5446/2885/2476 5447/2886/2477 1279/2883/2474 +f 5447/2886/2477 7643/2699/2329 5381/2698/2328 +f 1274/2887/2478 1275/2884/2475 1280/2888/2479 +f 1276/2889/2480 1280/2888/2479 5345/2890/2237 +f 1275/2884/2475 5380/2697/2327 5379/2713/2343 +f 1280/2888/2479 5379/2713/2343 7596/2716/2238 +f 1276/2889/2480 1281/2891/2481 1277/2892/2482 +f 1281/2891/2481 5441/2893/2461 5440/2894/2460 +f 5344/2895/2236 5343/2896/2231 1281/2891/2481 +f 5343/2896/2231 7641/2897/2212 5441/2893/2461 +f 1274/2887/2478 1277/2892/2482 1282/2898/2483 +f 1278/2882/2473 1282/2898/2483 5445/2899/2484 +f 1277/2892/2482 5440/2894/2460 5439/2900/2470 +f 1282/2898/2483 5439/2900/2470 7521/2901/2472 +f 1287/2902/2485 1288/2903/2486 1284/2904/2487 +f 1288/2903/2486 5444/2905/2471 5443/2906/2468 +f 5449/2907/2488 5450/2908/2489 1288/2903/2486 +f 5450/2908/2489 7521/2901/2472 5444/2905/2471 +f 1283/2909/2490 1284/2904/2487 1289/2910/2491 +f 1285/2911/2492 1289/2910/2491 5451/2912/2493 +f 1284/2904/2487 5443/2906/2468 5442/2913/2467 +f 1289/2910/2491 5442/2913/2467 7642/2914/2317 +f 1285/2911/2492 1290/2915/2494 1286/2916/2495 +f 1290/2915/2494 5454/2917/2496 5455/2918/2497 +f 1285/2911/2492 5452/2919/2498 5453/2920/2499 +f 1290/2915/2494 5453/2920/2499 7657/2921/2500 +f 1283/2909/2490 1286/2916/2495 1291/2922/2501 +f 1287/2902/2485 1291/2922/2501 5448/2923/2502 +f 1286/2916/2495 5455/2918/2497 5456/2924/2503 +f 1291/2922/2501 5456/2924/2503 7659/2925/2504 +f 1296/2926/2505 1297/2927/2506 1293/2928/2507 +f 1293/2928/2507 1297/2927/2506 5460/2929/2508 +f 5458/2930/2509 5459/2931/2510 1297/2927/2506 +f 1297/2927/2506 5459/2931/2510 7658/2932/2511 +f 1292/2933/2512 1293/2928/2507 1298/2934/2513 +f 1294/2935/2514 1298/2934/2513 5447/2886/2477 +f 1293/2928/2507 5461/2936/2515 5462/2937/2516 +f 1298/2934/2513 5462/2937/2516 7643/2699/2329 +f 1294/2935/2514 1299/2938/2517 1295/2939/2518 +f 1299/2938/2517 5450/2908/2489 5449/2907/2488 +f 5446/2885/2476 5445/2899/2484 1299/2938/2517 +f 5445/2899/2484 7521/2901/2472 5450/2908/2489 +f 1292/2933/2512 1295/2939/2518 1300/2940/2519 +f 1296/2926/2505 1300/2940/2519 5457/2941/2520 +f 1295/2939/2518 5449/2907/2488 5448/2923/2502 +f 1300/2940/2519 5448/2923/2502 7659/2925/2504 +f 1301/2942/2521 1305/2943/2522 1306/2944/2523 +f 1302/2945/2524 1306/2944/2523 5463/2946/2525 +f 1305/2943/2522 5434/2830/2441 5433/2822/2435 +f 1306/2944/2523 5433/2822/2435 7522/2825/2438 +f 1302/2945/2524 1307/2947/2526 1303/2948/2527 +f 1303/2948/2527 1307/2947/2526 5466/2949/2528 +f 5464/2950/2529 5465/2951/2530 1307/2947/2526 +f 5465/2951/2530 7650/2952/2531 5466/2949/2528 +f 1301/2942/2521 1303/2948/2527 1308/2953/2532 +f 1304/2954/2533 1308/2953/2532 5469/2955/2534 +f 1303/2948/2527 5467/2956/2535 5468/2957/2536 +f 1308/2953/2532 5468/2957/2536 7651/2958/2537 +f 1304/2954/2533 1309/2959/2538 1305/2943/2522 +f 1309/2959/2538 5435/2831/2442 5434/2830/2441 +f 5470/2960/2539 5471/2961/2540 1309/2959/2538 +f 5471/2961/2540 7648/2832/2399 5435/2831/2442 +f 1310/2962/2541 1314/2963/2542 1315/2964/2543 +f 1311/2965/2544 1315/2964/2543 5475/2966/2545 +f 1314/2963/2542 5473/2967/2546 5474/2968/2547 +f 1315/2964/2543 5474/2968/2547 7652/2969/2548 +f 1311/2965/2544 1316/2970/2549 1312/2971/2550 +f 1316/2970/2549 5465/2972/2530 5464/2973/2529 +f 1311/2965/2544 5476/2974/2551 5477/2975/2552 +f 5477/2975/2552 7650/2976/2531 5465/2972/2530 +f 1310/2962/2541 1312/2971/2550 1317/2977/2553 +f 1313/2978/2554 1317/2977/2553 5438/2849/2452 +f 1312/2971/2550 5464/2973/2529 5463/2979/2525 +f 1317/2977/2553 5463/2979/2525 7522/2850/2438 +f 1313/2978/2554 1318/2980/2555 1314/2963/2542 +f 1318/2980/2555 5472/2981/2556 5473/2967/2546 +f 5437/2841/2449 5436/2840/2448 1318/2980/2555 +f 5436/2840/2448 7649/2844/2416 5472/2981/2556 +f 1323/2982/2557 1324/2983/2558 1320/2984/2559 +f 1320/2984/2559 1324/2983/2558 5471/2985/2540 +f 5416/2986/2402 5415/2987/2397 1324/2983/2558 +f 5415/2987/2397 7648/2988/2399 5471/2985/2540 +f 1320/2984/2559 1325/2989/2560 1321/2990/2561 +f 1325/2989/2560 5478/2991/2562 5479/2992/2563 +f 5470/2993/2539 5469/2994/2534 1325/2989/2560 +f 5469/2994/2534 7651/2995/2537 5478/2991/2562 +f 1321/2990/2561 1326/2996/2564 1322/2997/2565 +f 1326/2996/2564 5481/2998/2566 5482/2999/2567 +f 5479/2992/2563 5480/3000/2568 1326/2996/2564 +f 5480/3000/2568 7653/3001/2569 5481/2998/2566 +f 1319/3002/2570 1322/2997/2565 1327/3003/2571 +f 1323/2982/2557 1327/3003/2571 5417/3004/2403 +f 1322/2997/2565 5482/2999/2567 5483/3005/2572 +f 1327/3003/2571 5483/3005/2572 7646/3006/2359 +f 1332/3007/2573 1333/3008/2574 1329/3009/2575 +f 1333/3008/2574 5487/3010/2576 5488/3011/2577 +f 5485/3012/2578 5486/3013/2579 1333/3008/2574 +f 5486/3013/2579 7654/3014/2580 5487/3010/2576 +f 1329/3009/2575 1334/3015/2581 1330/3016/2582 +f 1334/3015/2581 5474/3017/2547 5473/3018/2546 +f 5488/3011/2577 5489/3019/2583 1334/3015/2581 +f 5489/3019/2583 7652/3020/2548 5474/3017/2547 +f 1330/3016/2582 1335/3021/2584 1331/3022/2585 +f 1335/3021/2584 5420/3023/2415 5419/3024/2410 +f 1330/3016/2582 5473/3018/2546 5472/3025/2556 +f 5472/3025/2556 7649/3026/2416 5420/3023/2415 +f 1328/3027/2586 1331/3022/2585 1336/3028/2587 +f 1332/3007/2573 1336/3028/2587 5484/3029/2588 +f 1331/3022/2585 5419/3024/2410 5418/3030/2409 +f 1336/3028/2587 5418/3030/2409 7647/2755/2376 +f 1341/3031/2589 1342/3032/2590 1338/3033/2591 +f 1342/3032/2590 5483/3005/2572 5482/2999/2567 +f 5398/3034/2362 5397/3035/2357 1342/3032/2590 +f 5397/3035/2357 7646/3006/2359 5483/3005/2572 +f 1337/3036/2592 1338/3033/2591 1343/3037/2593 +f 1339/3038/2594 1343/3037/2593 5490/3039/2595 +f 1338/3033/2591 5482/2999/2567 5481/2998/2566 +f 1343/3037/2593 5481/2998/2566 7653/3001/2569 +f 1339/3038/2594 1344/3040/2596 1340/3041/2597 +f 1344/3040/2596 5493/3042/2598 5494/3043/2599 +f 1339/3038/2594 5491/3044/2600 5492/3045/2601 +f 5492/3045/2601 7655/3046/2602 5493/3042/2598 +f 1337/3036/2592 1340/3041/2597 1345/3047/2603 +f 1341/3031/2589 1345/3047/2603 5399/3048/2363 +f 1340/3041/2597 5494/3043/2599 5495/3049/2604 +f 1345/3047/2603 5495/3049/2604 7644/3050/2312 +f 1350/3051/2605 1351/3052/2606 1347/3053/2607 +f 1347/3053/2607 1351/3052/2606 5499/3054/2608 +f 5497/3055/2609 5498/3056/2610 1351/3052/2606 +f 5498/3056/2610 7656/3057/2611 5499/3054/2608 +f 1346/3058/2612 1347/3053/2607 1352/3059/2613 +f 1348/3060/2614 1352/3059/2613 5486/3013/2579 +f 1347/3053/2607 5500/3061/2615 5501/3062/2616 +f 1352/3059/2613 5501/3062/2616 7654/3014/2580 +f 1348/3060/2614 1353/3063/2617 1349/3064/2618 +f 1353/3063/2617 5402/2754/2375 5401/2746/2370 +f 5485/3012/2578 5484/3029/2588 1353/3063/2617 +f 5484/3029/2588 7647/2755/2376 5402/2754/2375 +f 1346/3058/2612 1349/3064/2618 1354/3065/2619 +f 1350/3051/2605 1354/3065/2619 5496/3066/2620 +f 1349/3064/2618 5401/2746/2370 5400/2745/2369 +f 1354/3065/2619 5400/2745/2369 7645/2749/2336 +f 1355/3067/2621 1359/3068/2622 1360/3069/2623 +f 1356/3070/2624 1360/3069/2623 5495/3071/2604 +f 1359/3068/2622 5374/2678/2309 5373/2677/2308 +f 1360/3069/2623 5373/2677/2308 7644/2681/2312 +f 1355/3067/2621 1356/3070/2624 1361/3072/2625 +f 1357/3073/2626 1361/3072/2625 5502/3074/2627 +f 1356/3070/2624 5494/3075/2599 5493/3076/2598 +f 1361/3072/2625 5493/3076/2598 7655/3077/2602 +f 1357/3073/2626 1362/3078/2628 1358/3079/2629 +f 1362/3078/2628 5453/3080/2499 5452/3081/2498 +f 5503/3082/2630 5504/3083/2631 1362/3078/2628 +f 5504/3083/2631 7657/3084/2500 5453/3080/2499 +f 1358/3079/2629 1363/3085/2632 1359/3068/2622 +f 1363/3085/2632 5375/2685/2316 5374/2678/2309 +f 5452/3081/2498 5451/3086/2493 1363/3085/2632 +f 5451/3086/2493 7642/2686/2317 5375/2685/2316 +f 1368/3087/2633 1369/3088/2634 1365/3089/2635 +f 1369/3088/2634 5505/3090/2636 5506/3091/2637 +f 5461/2936/2515 5460/2929/2508 1369/3088/2634 +f 5460/2929/2508 7658/2932/2511 5505/3090/2636 +f 1364/3092/2638 1365/3089/2635 1370/3093/2639 +f 1366/3094/2640 1370/3093/2639 5498/3095/2610 +f 1365/3089/2635 5506/3091/2637 5507/3096/2641 +f 1370/3093/2639 5507/3096/2641 7656/3097/2611 +f 1364/3092/2638 1366/3094/2640 1371/3098/2642 +f 1367/3099/2643 1371/3098/2642 5384/2705/2335 +f 1366/3094/2640 5497/3100/2609 5496/3101/2620 +f 1371/3098/2642 5496/3101/2620 7645/2706/2336 +f 1367/3099/2643 1372/3102/2644 1368/3087/2633 +f 1372/3102/2644 5462/2937/2516 5461/2936/2515 +f 5383/2704/2334 5382/2696/2326 1372/3102/2644 +f 5382/2696/2326 7643/2699/2329 5462/2937/2516 +f 1377/3103/2645 1378/3104/2646 1374/3105/2647 +f 1378/3104/2646 5508/3106/2648 5509/3107/2649 +f 5467/3108/2535 5466/3109/2528 1378/3104/2646 +f 5466/3109/2528 7650/3110/2531 5508/3106/2648 +f 1373/3111/2650 1374/3105/2647 1379/3112/2651 +f 1379/3112/2651 5492/3045/2601 5491/3044/2600 +f 1374/3105/2647 5509/3107/2649 5510/3113/2652 +f 1379/3112/2651 5510/3113/2652 7655/3046/2602 +f 1373/3111/2650 1375/3114/2653 1380/3115/2654 +f 1376/3116/2655 1380/3115/2654 5480/3000/2568 +f 1375/3114/2653 5491/3044/2600 5490/3039/2595 +f 1380/3115/2654 5490/3039/2595 7653/3001/2569 +f 1373/3111/2650 1376/3116/2655 1381/3117/2656 +f 1377/3103/2645 1381/3117/2656 5468/3118/2536 +f 1376/3116/2655 5479/2992/2563 5478/2991/2562 +f 1381/3117/2656 5478/2991/2562 7651/2995/2537 +f 1382/3119/2657 1386/3120/2658 1387/3121/2659 +f 1383/3122/2660 1387/3121/2659 5501/3123/2616 +f 1386/3120/2658 5488/3124/2577 5487/3125/2576 +f 1387/3121/2659 5487/3125/2576 7654/3126/2580 +f 1382/3119/2657 1383/3122/2660 1388/3127/2661 +f 1384/3128/2662 1388/3127/2661 5511/3129/2663 +f 5500/3130/2615 5499/3131/2608 1388/3127/2661 +f 1388/3127/2661 5499/3131/2608 7656/3132/2611 +f 1384/3128/2662 1389/3133/2664 1385/3134/2665 +f 1389/3133/2664 5477/2975/2552 5476/2974/2551 +f 5512/3135/2666 5513/3136/2667 1389/3133/2664 +f 5513/3136/2667 7650/2976/2531 5477/2975/2552 +f 1382/3119/2657 1385/3134/2665 1390/3137/2668 +f 1386/3120/2658 1390/3137/2668 5489/3138/2583 +f 1385/3134/2665 5476/2974/2551 5475/2966/2545 +f 1390/3137/2668 5475/2966/2545 7652/2969/2548 +f 1391/3139/2669 1395/3140/2670 1396/3141/2671 +f 1392/3142/2672 1396/3141/2671 5514/3143/2673 +f 1395/3140/2670 5509/3144/2649 5508/3145/2648 +f 5508/3145/2648 7650/3146/2531 5514/3143/2673 +f 1392/3142/2672 1397/3147/2674 1393/3148/2675 +f 1397/3147/2674 5456/2924/2503 5455/2918/2497 +f 5515/3149/2676 5516/3150/2677 1397/3147/2674 +f 5516/3150/2677 7659/2925/2504 5456/2924/2503 +f 1391/3139/2669 1393/3148/2675 1398/3151/2678 +f 1394/3152/2679 1398/3151/2678 5504/3153/2631 +f 1393/3148/2675 5455/2918/2497 5454/2917/2496 +f 1398/3151/2678 5454/2917/2496 7657/2921/2500 +f 1394/3152/2679 1399/3154/2680 1395/3140/2670 +f 1399/3154/2680 5510/3155/2652 5509/3144/2649 +f 5503/3156/2630 5502/3157/2627 1399/3154/2680 +f 5502/3157/2627 7655/3158/2602 5510/3155/2652 +f 1400/3159/2681 1404/3160/2682 1405/3161/2683 +f 1401/3162/2684 1405/3161/2683 5459/2931/2510 +f 1404/3160/2682 5506/3091/2637 5505/3090/2636 +f 1405/3161/2683 5505/3090/2636 7658/2932/2511 +f 1401/3162/2684 1406/3163/2685 1402/3164/2686 +f 1406/3163/2685 5516/3150/2677 5515/3149/2676 +f 5458/2930/2509 5457/2941/2520 1406/3163/2685 +f 5457/2941/2520 7659/2925/2504 5516/3150/2677 +f 1400/3159/2681 1402/3164/2686 1407/3165/2687 +f 1403/3166/2688 1407/3165/2687 5513/3167/2667 +f 1402/3164/2686 5515/3149/2676 5514/3143/2673 +f 5514/3143/2673 7650/3146/2531 5513/3167/2667 +f 1403/3166/2688 1408/3168/2689 1404/3160/2682 +f 1408/3168/2689 5507/3096/2641 5506/3091/2637 +f 5512/3169/2666 5511/3170/2663 1408/3168/2689 +f 5511/3170/2663 7656/3097/2611 5507/3096/2641 +f 1409/3171/2690 1413/3172/2691 1414/3173/2692 +f 1410/3174/2693 1414/3173/2692 5411/3175/2392 +f 1413/3172/2691 5518/3176/2694 5519/3177/2695 +f 5519/3177/2695 7590/3178/2393 5411/3175/2392 +f 1410/3174/2693 1415/3179/2696 1411/3180/2697 +f 1415/3179/2696 5520/3181/2698 5521/3182/2699 +f 5410/3183/2391 5409/3184/2405 1415/3179/2696 +f 5409/3184/2405 7591/3185/2353 5520/3181/2698 +f 1409/3171/2690 1411/3180/2697 1416/3186/2700 +f 1412/3187/2701 1416/3186/2700 5219/3188/1920 +f 1411/3180/2697 5521/3182/2699 5522/3189/2702 +f 1416/3186/2700 5522/3189/2702 7615/3190/1224 +f 1412/3187/2701 1417/3191/2703 1413/3172/2691 +f 1417/3191/2703 5517/3192/2704 5518/3176/2694 +f 5218/3193/1919 5217/3194/1915 1417/3191/2703 +f 5217/3194/1915 7628/3195/1917 5517/3192/2704 +f 1418/3196/2705 1422/3197/2706 1423/3198/2707 +f 1419/3199/2708 1423/3198/2707 5523/3200/2709 +f 1422/3197/2706 5221/2262/1926 5220/2275/1939 +f 1423/3198/2707 5220/2275/1939 7616/2225/1249 +f 1419/3199/2708 1424/3201/2710 1420/3202/2711 +f 1424/3201/2710 5426/3203/2424 5425/3204/2420 +f 5524/3205/2712 5525/3206/2713 1424/3201/2710 +f 5525/3206/2713 7592/3207/2383 5426/3203/2424 +f 1418/3196/2705 1420/3202/2711 1425/3208/2714 +f 1421/3209/2715 1425/3208/2714 5519/3210/2695 +f 1420/3202/2711 5425/3204/2420 5424/3211/2419 +f 5424/3211/2419 7590/3212/2393 5519/3210/2695 +f 1421/3209/2715 1426/3213/2716 1422/3197/2706 +f 1426/3213/2716 5222/2263/1927 5221/2262/1926 +f 5518/3214/2694 5517/3215/2704 1426/3213/2716 +f 5517/3215/2704 7628/2253/1917 5222/2263/1927 +f 1427/3216/2717 1431/3217/2718 1432/3218/2719 +f 1428/3219/2720 1432/3218/2719 5393/3220/2352 +f 1431/3217/2718 5521/3182/2699 5520/3181/2698 +f 1432/3218/2719 5520/3181/2698 7591/3185/2353 +f 1427/3216/2717 1428/3219/2720 1433/3221/2721 +f 1429/3222/2722 1433/3221/2721 5526/3223/2723 +f 1428/3219/2720 5392/3224/2351 5391/3225/2365 +f 1433/3221/2721 5391/3225/2365 7593/3226/2305 +f 1429/3222/2722 1434/3227/2724 1430/3228/2725 +f 1434/3227/2724 5529/3229/2726 5530/3230/2727 +f 5527/3231/2728 5528/3232/2729 1434/3227/2724 +f 5528/3232/2729 7662/3233/2730 5529/3229/2726 +f 1427/3216/2717 1430/3228/2725 1435/3234/2731 +f 1435/3234/2731 5522/3189/2702 5521/3182/2699 +f 1430/3228/2725 5530/3230/2727 5531/3235/2732 +f 5531/3235/2732 7615/3190/1224 5522/3189/2702 +f 1440/3236/2733 1441/3237/2734 1437/3238/2735 +f 1441/3237/2734 5535/3239/2736 5536/3240/2737 +f 5533/3241/2738 5534/3242/2739 1441/3237/2734 +f 5534/3242/2739 7663/3243/2740 5535/3239/2736 +f 1436/3244/2741 1437/3238/2735 1442/3245/2742 +f 1438/3246/2743 1442/3245/2742 5408/3247/2385 +f 1437/3238/2735 5536/3240/2737 5537/3248/2744 +f 1442/3245/2742 5537/3248/2744 7594/3249/2341 +f 1436/3244/2741 1438/3246/2743 1443/3250/2745 +f 1439/3251/2746 1443/3250/2745 5525/3206/2713 +f 1438/3246/2743 5407/3252/2380 5406/3253/2379 +f 1443/3250/2745 5406/3253/2379 7592/3207/2383 +f 1436/3244/2741 1439/3251/2746 1444/3254/2747 +f 1440/3236/2733 1444/3254/2747 5532/3255/2748 +f 5524/3205/2712 5523/3200/2709 1444/3254/2747 +f 5523/3200/2709 7616/2225/1249 5532/3255/2748 +f 1449/3256/2749 1450/3257/2750 1446/3258/2751 +f 1450/3257/2750 5369/3259/2304 5368/3260/2303 +f 5527/3261/2728 5526/3262/2723 1450/3257/2750 +f 5526/3262/2723 7593/3263/2305 5369/3259/2304 +f 1445/3264/2752 1446/3258/2751 1451/3265/2753 +f 1447/3266/2754 1451/3265/2753 5538/3267/2755 +f 1446/3258/2751 5368/3260/2303 5367/3268/2319 +f 5367/3268/2319 7595/2571/2206 5538/3267/2755 +f 1447/3266/2754 1452/3269/2756 1448/3270/2757 +f 1452/3269/2756 5541/3271/2758 5542/3272/2759 +f 5539/3273/2760 5540/3274/2761 1452/3269/2756 +f 5540/3274/2761 7664/3275/2762 5541/3271/2758 +f 1445/3264/2752 1448/3270/2757 1453/3276/2763 +f 1449/3256/2749 1453/3276/2763 5528/3277/2729 +f 1448/3270/2757 5542/3272/2759 5543/3278/2764 +f 1453/3276/2763 5543/3278/2764 7662/3279/2730 +f 1458/3280/2765 1459/3281/2766 1455/3282/2767 +f 1459/3281/2766 5547/3283/2768 5548/3284/2769 +f 5545/3285/2770 5546/3286/2771 1459/3281/2766 +f 5546/3286/2771 7665/3287/2772 5547/3283/2768 +f 1454/3288/2773 1455/3282/2767 1460/3289/2774 +f 1456/3290/2775 1460/3289/2774 5390/2715/2345 +f 1455/3282/2767 5548/3284/2769 5549/3291/2776 +f 5549/3291/2776 7596/2716/2238 5390/2715/2345 +f 1456/3290/2775 1461/3292/2777 1457/3293/2778 +f 1461/3292/2777 5537/3294/2744 5536/3295/2737 +f 5389/2714/2344 5388/2709/2339 1461/3292/2777 +f 5388/2709/2339 7594/2711/2341 5537/3294/2744 +f 1454/3288/2773 1457/3293/2778 1462/3296/2779 +f 1458/3280/2765 1462/3296/2779 5544/3297/2780 +f 1457/3293/2778 5536/3295/2737 5535/3298/2736 +f 1462/3296/2779 5535/3298/2736 7663/3299/2740 +f 1467/3300/2781 1468/3301/2782 1464/3302/2783 +f 1468/3301/2782 5330/2570/2205 5329/2569/2204 +f 5539/3273/2760 5538/3267/2755 1468/3301/2782 +f 5538/3267/2755 7595/2571/2206 5330/2570/2205 +f 1463/3303/2784 1464/3302/2783 1469/3304/2785 +f 1465/3305/2786 1469/3304/2785 5306/2501/2145 +f 1464/3302/2783 5329/2569/2204 5328/2586/2221 +f 1469/3304/2785 5328/2586/2221 7638/2502/2146 +f 1465/3305/2786 1470/3306/2787 1466/3307/2788 +f 1470/3306/2787 5550/3308/2789 5551/3309/2790 +f 5305/2500/2144 5304/2495/2139 1470/3306/2787 +f 5304/2495/2139 7617/1412/1173 5550/3308/2789 +f 1463/3303/2784 1466/3307/2788 1471/3310/2791 +f 1467/3300/2781 1471/3310/2791 5540/3274/2761 +f 1466/3307/2788 5551/3309/2790 5552/3311/2792 +f 1471/3310/2791 5552/3311/2792 7664/3275/2762 +f 1476/3312/2793 1477/3313/2794 1473/3314/2795 +f 1477/3313/2794 5321/2529/2172 5320/2522/2166 +f 5554/3315/2796 5555/3316/2797 1477/3313/2794 +f 5555/3316/2797 7618/2530/1191 5321/2529/2172 +f 1472/3317/2798 1473/3314/2795 1478/3318/2799 +f 1474/3319/2800 1478/3318/2799 5348/2606/2241 +f 1473/3314/2795 5320/2522/2166 5319/2521/2165 +f 1478/3318/2799 5319/2521/2165 7639/2525/2169 +f 1474/3319/2800 1479/3320/2801 1475/3321/2802 +f 1479/3320/2801 5549/3322/2776 5548/3323/2769 +f 5347/2600/2235 5346/2599/2234 1479/3320/2801 +f 5346/2599/2234 7596/2603/2238 5549/3322/2776 +f 1472/3317/2798 1475/3321/2802 1480/3324/2803 +f 1476/3312/2793 1480/3324/2803 5553/3325/2804 +f 1475/3321/2802 5548/3323/2769 5547/3326/2768 +f 1480/3324/2803 5547/3326/2768 7665/3327/2772 +f 1481/3328/2805 1485/3329/2806 1486/3330/2807 +f 1482/3331/2808 1486/3330/2807 5556/3332/2809 +f 1485/3329/2806 4900/1403/1164 4899/1402/1163 +f 1486/3330/2807 4899/1402/1163 7532/1406/1167 +f 1481/3328/2805 1482/3331/2808 1487/3333/2810 +f 1483/3334/2811 1487/3333/2810 5559/3335/2812 +f 5557/3336/2813 5558/3337/2814 1487/3333/2810 +f 5558/3337/2814 7660/3338/2815 5559/3335/2812 +f 1483/3334/2811 1488/3339/2816 1484/3340/2817 +f 1488/3339/2816 5552/3311/2792 5551/3309/2790 +f 5560/3341/2818 5561/3342/2819 1488/3339/2816 +f 5561/3342/2819 7664/3275/2762 5552/3311/2792 +f 1481/3328/2805 1484/3340/2817 1489/3343/2820 +f 1485/3329/2806 1489/3343/2820 4901/1411/1172 +f 1484/3340/2817 5551/3309/2790 5550/3308/2789 +f 1489/3343/2820 5550/3308/2789 7617/1412/1173 +f 1494/3344/2821 1495/3345/2822 1491/3346/2823 +f 1495/3345/2822 5562/3347/2824 5563/3348/2825 +f 5554/3349/2796 5553/3350/2804 1495/3345/2822 +f 5553/3350/2804 7665/3351/2772 5562/3347/2824 +f 1490/3352/2826 1491/3346/2823 1496/3353/2827 +f 1496/3353/2827 5565/3354/2828 5566/3355/2829 +f 1491/3346/2823 5563/3348/2825 5564/3356/2830 +f 5564/3356/2830 7661/3357/2831 5565/3354/2828 +f 1490/3352/2826 1492/3358/2832 1497/3359/2833 +f 1493/3360/2834 1497/3359/2833 4910/1438/1196 +f 1492/3358/2832 5566/3355/2829 5567/3361/2835 +f 1497/3359/2833 5567/3361/2835 7533/1439/1197 +f 1490/3352/2826 1493/3360/2834 1498/3362/2836 +f 1494/3344/2821 1498/3362/2836 5555/3363/2797 +f 1493/3360/2834 4909/1430/1188 4908/1429/1187 +f 1498/3362/2836 4908/1429/1187 7618/1433/1191 +f 1503/3364/2837 1504/3365/2838 1500/3366/2839 +f 1504/3365/2838 5568/3367/2840 5569/3368/2841 +f 5560/3341/2818 5559/3335/2812 1504/3365/2838 +f 1504/3365/2838 5559/3335/2812 7660/3338/2815 +f 1499/3369/2842 1500/3366/2839 1505/3370/2843 +f 1501/3371/2844 1505/3370/2843 5571/3372/2845 +f 1500/3366/2839 5569/3368/2841 5570/3373/2846 +f 1505/3370/2843 5570/3373/2846 7666/3374/2847 +f 1501/3371/2844 1506/3375/2848 1502/3376/2849 +f 1506/3375/2848 5543/3278/2764 5542/3272/2759 +f 5572/3377/2850 5573/3378/2851 1506/3375/2848 +f 5573/3378/2851 7662/3279/2730 5543/3278/2764 +f 1499/3369/2842 1502/3376/2849 1507/3379/2852 +f 1503/3364/2837 1507/3379/2852 5561/3342/2819 +f 1502/3376/2849 5542/3272/2759 5541/3271/2758 +f 1507/3379/2852 5541/3271/2758 7664/3275/2762 +f 1512/3380/2853 1513/3381/2854 1509/3382/2855 +f 1513/3381/2854 5574/3383/2856 5575/3384/2857 +f 5545/3385/2770 5544/3386/2780 1513/3381/2854 +f 5544/3386/2780 7663/3387/2740 5574/3383/2856 +f 1508/3388/2858 1509/3382/2855 1514/3389/2859 +f 1510/3390/2860 1514/3389/2859 5577/3391/2861 +f 1509/3382/2855 5575/3384/2857 5576/3392/2862 +f 1514/3389/2859 5576/3392/2862 7667/3393/2863 +f 1510/3390/2860 1515/3394/2864 1511/3395/2865 +f 1515/3394/2864 5564/3356/2830 5563/3348/2825 +f 5578/3396/2866 5579/3397/2867 1515/3394/2864 +f 1515/3394/2864 5579/3397/2867 7661/3357/2831 +f 1508/3388/2858 1511/3395/2865 1516/3398/2868 +f 1512/3380/2853 1516/3398/2868 5546/3399/2771 +f 1511/3395/2865 5563/3348/2825 5562/3347/2824 +f 1516/3398/2868 5562/3347/2824 7665/3351/2772 +f 1517/3400/2869 1521/3401/2870 1522/3402/2871 +f 1518/3403/2872 1522/3402/2871 4916/3404/1206 +f 1521/3401/2870 5581/3405/2873 5582/3406/2874 +f 5582/3406/2874 7530/3407/1207 4916/3404/1206 +f 1518/3403/2872 1523/3408/2875 1519/3409/2876 +f 1523/3408/2875 5531/3410/2732 5530/3411/2727 +f 4915/3412/1205 4914/3413/1221 1523/3408/2875 +f 4914/3413/1221 7615/3414/1224 5531/3410/2732 +f 1517/3400/2869 1519/3409/2876 1524/3415/2877 +f 1520/3416/2878 1524/3415/2877 5573/3378/2851 +f 1519/3409/2876 5530/3411/2727 5529/3417/2726 +f 1524/3415/2877 5529/3417/2726 7662/3279/2730 +f 1520/3416/2878 1525/3418/2879 1521/3401/2870 +f 1525/3418/2879 5580/3419/2880 5581/3405/2873 +f 5572/3377/2850 5571/3372/2845 1525/3418/2879 +f 5571/3372/2845 7666/3374/2847 5580/3419/2880 +f 1526/3420/2881 1530/3421/2882 1531/3422/2883 +f 1527/3423/2884 1531/3422/2883 5534/3424/2739 +f 1530/3421/2882 5575/3384/2857 5574/3383/2856 +f 1531/3422/2883 5574/3383/2856 7663/3387/2740 +f 1527/3423/2884 1532/3425/2885 1528/3426/2886 +f 1532/3425/2885 4937/3427/1248 4936/3428/1247 +f 5533/3429/2738 5532/3430/2748 1532/3425/2885 +f 5532/3430/2748 7616/3431/1249 4937/3427/1248 +f 1526/3420/2881 1528/3426/2886 1533/3432/2887 +f 1529/3433/2888 1533/3432/2887 5583/3434/2889 +f 1528/3426/2886 4936/3428/1247 4935/3435/1242 +f 4935/3435/1242 7531/3436/1244 5583/3434/2889 +f 1529/3433/2888 1534/3437/2890 1530/3421/2882 +f 1534/3437/2890 5576/3392/2862 5575/3384/2857 +f 5584/3438/2891 5585/3439/2892 1534/3437/2890 +f 5585/3439/2892 7667/3393/2863 5576/3392/2862 +f 1539/3440/2893 1540/3441/2894 1536/3442/2895 +f 1540/3441/2894 5363/3443/2280 5362/3444/2274 +f 5587/3445/2896 5588/3446/2897 1540/3441/2894 +f 5588/3446/2897 7582/3447/2110 5363/3443/2280 +f 1535/3448/2898 1536/3442/2895 1541/3449/2899 +f 1537/3450/2900 1541/3449/2899 5589/3451/2901 +f 1536/3442/2895 5362/3444/2274 5361/3452/2273 +f 1541/3449/2899 5361/3452/2273 7581/3453/2277 +f 1537/3450/2900 1542/3454/2902 1538/3455/2903 +f 1542/3454/2902 5592/3456/2904 5593/3457/2905 +f 5590/3458/2906 5591/3459/2907 1542/3454/2902 +f 5591/3459/2907 7523/3460/2908 5592/3456/2904 +f 1535/3448/2898 1538/3455/2903 1543/3461/2909 +f 1539/3440/2893 1543/3461/2909 5586/3462/2910 +f 1538/3455/2903 5593/3457/2905 5594/3463/2911 +f 1543/3461/2909 5594/3463/2911 7673/3464/2912 +f 1548/3465/2913 1549/3466/2914 1545/3467/2915 +f 1549/3466/2914 5591/3459/2907 5590/3458/2906 +f 5596/3468/2916 5597/3469/2917 1549/3466/2914 +f 5597/3469/2917 7523/3460/2908 5591/3459/2907 +f 1544/3470/2918 1545/3467/2915 1550/3471/2919 +f 1546/3472/2920 1550/3471/2919 5366/3473/2296 +f 1545/3467/2915 5590/3458/2906 5589/3451/2901 +f 1550/3471/2919 5589/3451/2901 7581/3453/2277 +f 1546/3472/2920 1551/3474/2921 1547/3475/2922 +f 1551/3474/2921 5598/3476/2923 5599/3477/2924 +f 5365/3478/2295 5364/3479/2292 1551/3474/2921 +f 5364/3479/2292 7583/2488/2132 5598/3476/2923 +f 1544/3470/2918 1547/3475/2922 1552/3480/2925 +f 1548/3465/2913 1552/3480/2925 5595/3481/2926 +f 1547/3475/2922 5599/3477/2924 5600/3482/2927 +f 1552/3480/2925 5600/3482/2927 7674/3483/2928 +f 1557/3484/2929 1558/3485/2930 1554/3486/2931 +f 1558/3485/2930 5297/3487/2114 5296/3488/2107 +f 5602/3489/2932 5603/3490/2933 1558/3485/2930 +f 5603/3490/2933 7584/2430/2078 5297/3487/2114 +f 1553/3491/2934 1554/3486/2931 1559/3492/2935 +f 1555/3493/2936 1559/3492/2935 5588/3446/2897 +f 1554/3486/2931 5296/3488/2107 5295/3494/2106 +f 1559/3492/2935 5295/3494/2106 7582/3447/2110 +f 1555/3493/2936 1560/3495/2937 1556/3496/2938 +f 1556/3496/2938 1560/3495/2937 5604/3497/2939 +f 5587/3445/2896 5586/3462/2910 1560/3495/2937 +f 1560/3495/2937 5586/3462/2910 7673/3464/2912 +f 1553/3491/2934 1556/3496/2938 1561/3498/2940 +f 1557/3484/2929 1561/3498/2940 5601/3499/2941 +f 5605/3500/2942 5606/3501/2943 1561/3498/2940 +f 5606/3501/2943 7671/3502/2944 5601/3499/2941 +f 1566/3503/2945 1567/3504/2946 1563/3505/2947 +f 1567/3504/2946 5600/3506/2927 5599/3507/2924 +f 1566/3503/2945 5608/3508/2948 5609/3509/2949 +f 1567/3504/2946 5609/3509/2949 7674/3510/2928 +f 1562/3511/2950 1563/3505/2947 1568/3512/2951 +f 1564/3513/2952 1568/3512/2951 5300/3514/2131 +f 1563/3505/2947 5599/3507/2924 5598/3515/2923 +f 1568/3512/2951 5598/3515/2923 7583/3516/2132 +f 1564/3513/2952 1569/3517/2953 1565/3518/2954 +f 1569/3517/2953 5610/3519/2955 5611/3520/2956 +f 5299/3521/2130 5298/3522/2125 1569/3517/2953 +f 5298/3522/2125 7585/3523/2099 5610/3519/2955 +f 1562/3511/2950 1565/3518/2954 1570/3524/2957 +f 1570/3524/2957 5607/3525/2958 5608/3508/2948 +f 1565/3518/2954 5611/3520/2956 5612/3526/2959 +f 5612/3526/2959 7672/3527/2960 5607/3525/2958 +f 1571/3528/2961 1575/3529/2962 1576/3530/2963 +f 1572/3531/2964 1576/3530/2963 5285/2434/2082 +f 1575/3529/2962 5614/3532/2965 5615/3533/2966 +f 1576/3530/2963 5615/3533/2966 7586/2394/2042 +f 1572/3531/2964 1577/3534/2967 1573/3535/2968 +f 1573/3535/2968 1577/3534/2967 5603/3490/2933 +f 5284/2433/2081 5283/2427/2075 1577/3534/2967 +f 1577/3534/2967 5283/2427/2075 7584/2430/2078 +f 1571/3528/2961 1573/3535/2968 1578/3536/2969 +f 1574/3537/2970 1578/3536/2969 5616/3538/2971 +f 5602/3489/2932 5601/3499/2941 1578/3536/2969 +f 1578/3536/2969 5601/3499/2941 7671/3502/2944 +f 1574/3537/2970 1579/3539/2972 1575/3529/2962 +f 1579/3539/2972 5613/3540/2973 5614/3532/2965 +f 5617/3541/2974 5618/3542/2975 1579/3539/2972 +f 5618/3542/2975 7669/3543/2976 5613/3540/2973 +f 1580/3544/2977 1584/3545/2978 1585/3546/2979 +f 1585/3546/2979 5612/3526/2959 5611/3520/2956 +f 1584/3545/2978 5620/3547/2980 5621/3548/2981 +f 1585/3546/2979 5621/3548/2981 7672/3527/2960 +f 1581/3549/2982 1586/3550/2983 1582/3551/2984 +f 1586/3550/2983 5288/3552/2098 5287/3553/2094 +f 1581/3549/2982 5611/3520/2956 5610/3519/2955 +f 1586/3550/2983 5610/3519/2955 7585/3523/2099 +f 1580/3544/2977 1582/3551/2984 1587/3554/2985 +f 1583/3555/2986 1587/3554/2985 5622/3556/2987 +f 1582/3551/2984 5287/3553/2094 5286/3557/2093 +f 1587/3554/2985 5286/3557/2093 7587/3558/2067 +f 1583/3555/2986 1588/3559/2988 1584/3545/2978 +f 1588/3559/2988 5619/3560/2989 5620/3547/2980 +f 5623/3561/2990 5624/3562/2991 1588/3559/2988 +f 5624/3562/2991 7670/3563/2992 5619/3560/2989 +f 1589/3564/2993 1593/3565/2994 1594/3566/2995 +f 1590/3567/2996 1594/3566/2995 5270/3568/2048 +f 1593/3565/2994 5626/3569/2997 5627/3570/2998 +f 1594/3566/2995 5627/3570/2998 7588/3571/2049 +f 1590/3567/2996 1595/3572/2999 1591/3573/3000 +f 1595/3572/2999 5615/3574/2966 5614/3575/2965 +f 5269/3576/2047 5268/3577/2039 1595/3572/2999 +f 5268/3577/2039 7586/3578/2042 5615/3574/2966 +f 1589/3564/2993 1591/3573/3000 1596/3579/3001 +f 1592/3580/3002 1596/3579/3001 5628/3581/3003 +f 1591/3573/3000 5614/3575/2965 5613/3582/2973 +f 1596/3579/3001 5613/3582/2973 7669/3583/2976 +f 1592/3580/3002 1597/3584/3004 1593/3565/2994 +f 1597/3584/3004 5625/3585/3005 5626/3569/2997 +f 5629/3586/3006 5630/3587/3007 1597/3584/3004 +f 5630/3587/3007 7668/3588/3008 5625/3585/3005 +f 1598/3589/3009 1602/3590/3010 1603/3591/3011 +f 1599/3592/3012 1603/3591/3011 5624/3562/2991 +f 1602/3590/3010 5632/3593/3013 5633/3594/3014 +f 1603/3591/3011 5633/3594/3014 7670/3563/2992 +f 1599/3592/3012 1604/3595/3015 1600/3596/3016 +f 1604/3595/3015 5276/3597/2066 5275/3598/2062 +f 5623/3561/2990 5622/3556/2987 1604/3595/3015 +f 5622/3556/2987 7587/3558/2067 5276/3597/2066 +f 1598/3589/3009 1600/3596/3016 1605/3599/3017 +f 1601/3600/3018 1605/3599/3017 5627/3570/2998 +f 1600/3596/3016 5275/3598/2062 5274/3601/2061 +f 1605/3599/3017 5274/3601/2061 7588/3571/2049 +f 1601/3600/3018 1606/3602/3019 1602/3590/3010 +f 1606/3602/3019 5631/3603/3020 5632/3593/3013 +f 5626/3569/2997 5625/3585/3005 1606/3602/3019 +f 5625/3585/3005 7668/3588/3008 5631/3603/3020 +f 1611/3604/3021 1612/3605/3022 1608/3606/3023 +f 1608/3606/3023 1612/3605/3022 5630/3607/3007 +f 5635/3608/3024 5636/3609/3025 1612/3605/3022 +f 1612/3605/3022 5636/3609/3025 7668/3610/3008 +f 1607/3611/3026 1608/3606/3023 1613/3612/3027 +f 1609/3613/3028 1613/3612/3027 5637/3614/3029 +f 5629/3615/3006 5628/3616/3003 1613/3612/3027 +f 5628/3616/3003 7669/3617/2976 5637/3614/3029 +f 1609/3613/3028 1614/3618/3030 1610/3619/3031 +f 1614/3618/3030 5640/3620/3032 5641/3621/3033 +f 5638/3622/3034 5639/3623/3035 1614/3618/3030 +f 5639/3623/3035 7680/3624/3036 5640/3620/3032 +f 1607/3611/3026 1610/3619/3031 1615/3625/3037 +f 1611/3604/3021 1615/3625/3037 5634/3626/3038 +f 1610/3619/3031 5641/3621/3033 5642/3627/3039 +f 1615/3625/3037 5642/3627/3039 7682/3628/3040 +f 1620/3629/3041 1621/3630/3042 1617/3631/3043 +f 1621/3630/3042 5646/3632/3044 5647/3633/3045 +f 5644/3634/3046 5645/3635/3047 1621/3630/3042 +f 5645/3635/3047 7681/3636/3048 5646/3632/3044 +f 1616/3637/3049 1617/3631/3043 1622/3638/3050 +f 1622/3638/3050 5633/3639/3014 5632/3640/3013 +f 1617/3631/3043 5647/3633/3045 5648/3641/3051 +f 5648/3641/3051 7670/3642/2992 5633/3639/3014 +f 1618/3643/3052 1623/3644/3053 1619/3645/3054 +f 1623/3644/3053 5636/3646/3025 5635/3647/3024 +f 1618/3643/3052 5632/3640/3013 5631/3648/3020 +f 1623/3644/3053 5631/3648/3020 7668/3649/3008 +f 1616/3637/3049 1619/3645/3054 1624/3650/3055 +f 1620/3629/3041 1624/3650/3055 5643/3651/3056 +f 1619/3645/3054 5635/3647/3024 5634/3652/3038 +f 1624/3650/3055 5634/3652/3038 7682/3653/3040 +f 1625/3654/3057 1629/3655/3058 1630/3656/3059 +f 1626/3657/3060 1630/3656/3059 5618/3658/2975 +f 5638/3622/3034 5637/3614/3029 1630/3656/3059 +f 1630/3656/3059 5637/3614/3029 7669/3617/2976 +f 1626/3657/3060 1631/3659/3061 1627/3660/3062 +f 1631/3659/3061 5649/3661/3063 5650/3662/3064 +f 5617/3663/2974 5616/3664/2971 1631/3659/3061 +f 5616/3664/2971 7671/3665/2944 5649/3661/3063 +f 1625/3654/3057 1627/3660/3062 1632/3666/3065 +f 1632/3666/3065 5652/3667/3066 5653/3668/3067 +f 1627/3660/3062 5650/3662/3064 5651/3669/3068 +f 1632/3666/3065 5651/3669/3068 7678/3670/3069 +f 1625/3654/3057 1628/3671/3070 1633/3672/3071 +f 1629/3655/3058 1633/3672/3071 5639/3623/3035 +f 1628/3671/3070 5653/3668/3067 5654/3673/3072 +f 1633/3672/3071 5654/3673/3072 7680/3624/3036 +f 1634/3674/3073 1638/3675/3074 1639/3676/3075 +f 1635/3677/3076 1639/3676/3075 5658/3678/3077 +f 5656/3679/3078 5657/3680/3079 1639/3676/3075 +f 1639/3676/3075 5657/3680/3079 7679/3681/3080 +f 1635/3677/3076 1640/3682/3081 1636/3683/3082 +f 1640/3682/3081 5621/3684/2981 5620/3685/2980 +f 5659/3686/3083 5660/3687/3084 1640/3682/3081 +f 5660/3687/3084 7672/3688/2960 5621/3684/2981 +f 1634/3674/3073 1636/3683/3082 1641/3689/3085 +f 1641/3689/3085 5648/3641/3051 5647/3633/3045 +f 1636/3683/3082 5620/3685/2980 5619/3690/2989 +f 1641/3689/3085 5619/3690/2989 7670/3642/2992 +f 1634/3674/3073 1637/3691/3086 1642/3692/3087 +f 1638/3675/3074 1642/3692/3087 5655/3693/3088 +f 1637/3691/3086 5647/3633/3045 5646/3632/3044 +f 1642/3692/3087 5646/3632/3044 7681/3636/3048 +f 1643/3694/3089 1647/3695/3090 1648/3696/3091 +f 1644/3697/3092 1648/3696/3091 5606/3698/2943 +f 1647/3695/3090 5650/3662/3064 5649/3661/3063 +f 1648/3696/3091 5649/3661/3063 7671/3665/2944 +f 1644/3697/3092 1649/3699/3093 1645/3700/3094 +f 1649/3699/3093 5661/3701/3095 5662/3702/3096 +f 5605/3703/2942 5604/3704/2939 1649/3699/3093 +f 5604/3704/2939 7673/3705/2912 5661/3701/3095 +f 1643/3694/3089 1645/3700/3094 1650/3706/3097 +f 1646/3707/3098 1650/3706/3097 5664/3708/3099 +f 1645/3700/3094 5662/3702/3096 5663/3709/3100 +f 1650/3706/3097 5663/3709/3100 7676/3710/3101 +f 1646/3707/3098 1651/3711/3102 1647/3695/3090 +f 1651/3711/3102 5651/3669/3068 5650/3662/3064 +f 5665/3712/3103 5666/3713/3104 1651/3711/3102 +f 5666/3713/3104 7678/3670/3069 5651/3669/3068 +f 1652/3714/3105 1656/3715/3106 1657/3716/3107 +f 1653/3717/3108 1657/3716/3107 5670/3718/3109 +f 1656/3715/3106 5668/3719/3110 5669/3720/3111 +f 1657/3716/3107 5669/3720/3111 7677/3721/3112 +f 1653/3717/3108 1658/3722/3113 1654/3723/3114 +f 1658/3722/3113 5609/3724/2949 5608/3725/2948 +f 5671/3726/3115 5672/3727/3116 1658/3722/3113 +f 5672/3727/3116 7674/3728/2928 5609/3724/2949 +f 1652/3714/3105 1654/3723/3114 1659/3729/3117 +f 1655/3730/3118 1659/3729/3117 5660/3731/3084 +f 1654/3723/3114 5608/3725/2948 5607/3732/2958 +f 1659/3729/3117 5607/3732/2958 7672/3733/2960 +f 1655/3730/3118 1660/3734/3119 1656/3715/3106 +f 1660/3734/3119 5667/3735/3120 5668/3719/3110 +f 5659/3736/3083 5658/3737/3077 1660/3734/3119 +f 5658/3737/3077 7679/3738/3080 5667/3735/3120 +f 1665/3739/3121 1666/3740/3122 1662/3741/3123 +f 1666/3740/3122 5594/3742/2911 5593/3743/2905 +f 5662/3744/3096 5661/3745/3095 1666/3740/3122 +f 5661/3745/3095 7673/3746/2912 5594/3742/2911 +f 1661/3747/3124 1662/3741/3123 1667/3748/3125 +f 1663/3749/3126 1667/3748/3125 5673/3750/3127 +f 1662/3741/3123 5593/3743/2905 5592/3751/2904 +f 1667/3748/3125 5592/3751/2904 7523/3752/2908 +f 1663/3749/3126 1668/3753/3128 1664/3754/3129 +f 1668/3753/3128 5676/3755/3130 5677/3756/3131 +f 5674/3757/3132 5675/3758/3133 1668/3753/3128 +f 5675/3758/3133 7675/3759/3134 5676/3755/3130 +f 1661/3747/3124 1664/3754/3129 1669/3760/3135 +f 1665/3739/3121 1669/3760/3135 5663/3761/3100 +f 1664/3754/3129 5677/3756/3131 5678/3762/3136 +f 1669/3760/3135 5678/3762/3136 7676/3763/3101 +f 1674/3764/3137 1675/3765/3138 1671/3766/3139 +f 1675/3765/3138 5675/3767/3133 5674/3768/3132 +f 5680/3769/3140 5681/3770/3141 1675/3765/3138 +f 5681/3770/3141 7675/3771/3134 5675/3767/3133 +f 1670/3772/3142 1671/3766/3139 1676/3773/3143 +f 1672/3774/3144 1676/3773/3143 5597/3469/2917 +f 1671/3766/3139 5674/3768/3132 5673/3775/3127 +f 1676/3773/3143 5673/3775/3127 7523/3460/2908 +f 1672/3774/3144 1677/3776/3145 1673/3777/3146 +f 1677/3776/3145 5672/3778/3116 5671/3779/3115 +f 5596/3468/2916 5595/3481/2926 1677/3776/3145 +f 5595/3481/2926 7674/3483/2928 5672/3778/3116 +f 1670/3772/3142 1673/3777/3146 1678/3780/3147 +f 1674/3764/3137 1678/3780/3147 5679/3781/3148 +f 1673/3777/3146 5671/3779/3115 5670/3782/3109 +f 1678/3780/3147 5670/3782/3109 7677/3783/3112 +f 1679/3784/3149 1683/3785/3150 1684/3786/3151 +f 1680/3787/3152 1684/3786/3151 5682/3788/3153 +f 1683/3785/3150 5677/3789/3131 5676/3790/3130 +f 1684/3786/3151 5676/3790/3130 7675/3791/3134 +f 1680/3787/3152 1685/3792/3154 1681/3793/3155 +f 1685/3792/3154 5642/3794/3039 5641/3795/3033 +f 5683/3796/3156 5684/3797/3157 1685/3792/3154 +f 5684/3797/3157 7682/3798/3040 5642/3794/3039 +f 1679/3784/3149 1681/3793/3155 1686/3799/3158 +f 1682/3800/3159 1686/3799/3158 5685/3801/3160 +f 1681/3793/3155 5641/3795/3033 5640/3802/3032 +f 1686/3799/3158 5640/3802/3032 7680/3803/3036 +f 1682/3800/3159 1687/3804/3161 1683/3785/3150 +f 1687/3804/3161 5678/3805/3136 5677/3789/3131 +f 5686/3806/3162 5687/3807/3163 1687/3804/3161 +f 5687/3807/3163 7676/3808/3101 5678/3805/3136 +f 1688/3809/3164 1692/3810/3165 1693/3811/3166 +f 1689/3812/3167 1693/3811/3166 5645/3813/3047 +f 1692/3810/3165 5689/3814/3168 5690/3815/3169 +f 1693/3811/3166 5690/3815/3169 7681/3816/3048 +f 1689/3812/3167 1694/3817/3170 1690/3818/3171 +f 1694/3817/3170 5684/3797/3157 5683/3796/3156 +f 5644/3819/3046 5643/3820/3056 1694/3817/3170 +f 5643/3820/3056 7682/3798/3040 5684/3797/3157 +f 1688/3809/3164 1690/3818/3171 1695/3821/3172 +f 1691/3822/3173 1695/3821/3172 5681/3823/3141 +f 1690/3818/3171 5683/3796/3156 5682/3788/3153 +f 1695/3821/3172 5682/3788/3153 7675/3791/3134 +f 1691/3822/3173 1696/3824/3174 1692/3810/3165 +f 1696/3824/3174 5688/3825/3175 5689/3814/3168 +f 5680/3826/3140 5679/3827/3148 1696/3824/3174 +f 5679/3827/3148 7677/3828/3112 5688/3825/3175 +f 1700/3829/3176 1701/3830/3177 1698/3831/3178 +f 1698/3831/3178 1701/3830/3177 5687/3807/3163 +f 5665/3832/3103 5664/3833/3099 1701/3830/3177 +f 5664/3833/3099 7676/3808/3101 5687/3807/3163 +f 1698/3831/3178 1702/3834/3179 1699/3835/3180 +f 1702/3834/3179 5654/3836/3072 5653/3837/3067 +f 5686/3806/3162 5685/3801/3160 1702/3834/3179 +f 5685/3801/3160 7680/3803/3036 5654/3836/3072 +f 1697/3838/3181 1699/3835/3180 1703/3839/3182 +f 1700/3829/3176 1703/3839/3182 5666/3840/3104 +f 1699/3835/3180 5653/3837/3067 5652/3841/3066 +f 1703/3839/3182 5652/3841/3066 7678/3842/3069 +f 1704/3843/3183 1707/3844/3184 1708/3845/3185 +f 1705/3846/3186 1708/3845/3185 5657/3847/3079 +f 1707/3844/3184 5668/3848/3110 5667/3849/3120 +f 1708/3845/3185 5667/3849/3120 7679/3850/3080 +f 1705/3846/3186 1709/3851/3187 1706/3852/3188 +f 1709/3851/3187 5690/3853/3169 5689/3854/3168 +f 5656/3855/3078 5655/3856/3088 1709/3851/3187 +f 5655/3856/3088 7681/3857/3048 5690/3853/3169 +f 1706/3852/3188 1710/3858/3189 1707/3844/3184 +f 1710/3858/3189 5669/3859/3111 5668/3848/3110 +f 1706/3852/3188 5689/3854/3168 5688/3860/3175 +f 5688/3860/3175 7677/3861/3112 5669/3859/3111 +f 1711/3862/3190 1715/3863/3191 1716/3864/3192 +f 1712/3865/3193 1716/3864/3192 5261/3866/2020 +f 1715/3863/3191 5692/3867/3194 5693/3868/3195 +f 5693/3868/3195 7634/3869/1991 5261/3866/2020 +f 1712/3865/3193 1717/3870/3196 1713/3871/3197 +f 1717/3870/3196 5694/3872/3198 5695/3873/3199 +f 5260/3874/2019 5259/3875/2016 1717/3870/3196 +f 5259/3875/2016 7632/3876/1912 5694/3872/3198 +f 1711/3862/3190 1713/3871/3197 1718/3877/3200 +f 1714/3878/3201 1718/3877/3200 5697/3879/3202 +f 1713/3871/3197 5695/3873/3199 5696/3880/3203 +f 1718/3877/3200 5696/3880/3203 7685/3881/3204 +f 1714/3878/3201 1719/3882/3205 1715/3863/3191 +f 1719/3882/3205 5691/3883/3206 5692/3867/3194 +f 5698/3884/3207 5699/3885/3208 1719/3882/3205 +f 5699/3885/3208 7683/3886/3209 5691/3883/3206 +f 1720/3887/3210 1724/3888/3211 1725/3889/3212 +f 1721/3890/3213 1725/3889/3212 5703/3891/3214 +f 1724/3888/3211 5701/3892/3215 5702/3893/3216 +f 1725/3889/3212 5702/3893/3216 7686/3894/3217 +f 1721/3890/3213 1726/3895/3218 1722/3896/3219 +f 1726/3895/3218 5264/2382/2031 5263/2375/2027 +f 5704/3897/3220 5705/3898/3221 1726/3895/3218 +f 5705/3898/3221 7633/2270/1934 5264/2382/2031 +f 1720/3887/3210 1722/3896/3219 1727/3899/3222 +f 1723/3900/3223 1727/3899/3222 5706/3901/3224 +f 1722/3896/3219 5263/2375/2027 5262/2374/2026 +f 5262/2374/2026 7635/2378/2007 5706/3901/3224 +f 1723/3900/3223 1728/3902/3225 1724/3888/3211 +f 1728/3902/3225 5700/3903/3226 5701/3892/3215 +f 5707/3904/3227 5708/3905/3228 1728/3902/3225 +f 5708/3905/3228 7684/3906/3229 5700/3903/3226 +f 1733/3907/3230 1734/3908/3231 1730/3909/3232 +f 1734/3908/3231 5243/3910/1977 5242/3911/1976 +f 5710/3912/3233 5711/3913/3234 1734/3908/3231 +f 5711/3913/3234 7563/3914/1955 5243/3910/1977 +f 1729/3915/3235 1730/3909/3232 1735/3916/3236 +f 1731/3917/3237 1735/3916/3236 5693/3868/3195 +f 1730/3909/3232 5242/3911/1976 5241/3918/1988 +f 1735/3916/3236 5241/3918/1988 7634/3869/1991 +f 1729/3915/3235 1731/3917/3237 1736/3919/3238 +f 1732/3920/3239 1736/3919/3238 5712/3921/3240 +f 1731/3917/3237 5692/3867/3194 5691/3883/3206 +f 1736/3919/3238 5691/3883/3206 7683/3886/3209 +f 1732/3920/3239 1737/3922/3241 1733/3907/3230 +f 1737/3922/3241 5709/3923/3242 5710/3912/3233 +f 5713/3924/3243 5714/3925/3244 1737/3922/3241 +f 5714/3925/3244 7705/3926/3245 5709/3923/3242 +f 1738/3927/3246 1742/3928/3247 1743/3929/3248 +f 1739/3930/3249 1743/3929/3248 5708/3905/3228 +f 1742/3928/3247 5716/3931/3250 5717/3932/3251 +f 1743/3929/3248 5717/3932/3251 7684/3906/3229 +f 1738/3927/3246 1739/3930/3249 1744/3933/3252 +f 1740/3934/3253 1744/3933/3252 5255/3935/2006 +f 1739/3930/3249 5707/3904/3227 5706/3901/3224 +f 1744/3933/3252 5706/3901/3224 7635/2378/2007 +f 1740/3934/3253 1745/3936/3254 1741/3937/3255 +f 1745/3936/3254 5718/3938/3256 5719/3939/3257 +f 5254/3940/2005 5253/3941/2002 1745/3936/3254 +f 5253/3941/2002 7564/3942/1971 5718/3938/3256 +f 1741/3937/3255 1746/3943/3258 1742/3928/3247 +f 1746/3943/3258 5715/3944/3259 5716/3931/3250 +f 5719/3939/3257 5720/3945/3260 1746/3943/3258 +f 5720/3945/3260 7706/3946/3261 5715/3944/3259 +f 1751/3947/3262 1752/3948/3263 1748/3949/3264 +f 1748/3949/3264 1752/3948/3263 5231/2280/1944 +f 1751/3947/3262 5722/3950/3265 5723/3951/3266 +f 1752/3948/3263 5723/3951/3266 7561/1886/1612 +f 1747/3952/3267 1748/3949/3264 1753/3953/3268 +f 1749/3954/3269 1753/3953/3268 5711/3955/3234 +f 5230/2279/1943 5229/2293/1953 1753/3953/3268 +f 1753/3953/3268 5229/2293/1953 7563/2295/1955 +f 1749/3954/3269 1754/3956/3270 1750/3957/3271 +f 1754/3956/3270 5724/3958/3272 5725/3959/3273 +f 5710/3960/3233 5709/3961/3242 1754/3956/3270 +f 1754/3956/3270 5709/3961/3242 7705/3962/3245 +f 1747/3952/3267 1750/3957/3271 1755/3963/3274 +f 1751/3947/3262 1755/3963/3274 5721/3964/3275 +f 1750/3957/3271 5725/3959/3273 5726/3965/3276 +f 1755/3963/3274 5726/3965/3276 7707/3966/3277 +f 1760/3967/3278 1761/3968/3279 1757/3969/3280 +f 1761/3968/3279 5720/3945/3260 5719/3939/3257 +f 5728/3970/3281 5729/3971/3282 1761/3968/3279 +f 1761/3968/3279 5729/3971/3282 7706/3946/3261 +f 1756/3972/3283 1757/3969/3280 1762/3973/3284 +f 1762/3973/3284 5240/3974/1970 5239/3975/1967 +f 1757/3969/3280 5719/3939/3257 5718/3938/3256 +f 1762/3973/3284 5718/3938/3256 7564/3942/1971 +f 1758/3976/3285 1763/3977/3286 1759/3978/3287 +f 1759/3978/3287 1763/3977/3286 5730/3979/3288 +f 1758/3976/3285 5239/3975/1967 5238/3980/1966 +f 1763/3977/3286 5238/3980/1966 7562/3981/1642 +f 1756/3972/3283 1759/3978/3287 1764/3982/3289 +f 1760/3967/3278 1764/3982/3289 5727/3983/3290 +f 1759/3978/3287 5731/3984/3291 5732/3985/3292 +f 1764/3982/3289 5732/3985/3292 7708/3986/3293 +f 1765/3987/3294 1769/3988/3295 1770/3989/3296 +f 1766/3990/3297 1770/3989/3296 5213/3991/1911 +f 1769/3988/3295 5695/3873/3199 5694/3872/3198 +f 1770/3989/3296 5694/3872/3198 7632/3876/1912 +f 1765/3987/3294 1766/3990/3297 1771/3992/3298 +f 1767/3993/3299 1771/3992/3298 5733/3994/3300 +f 5212/3995/1910 5211/3996/1905 1771/3992/3298 +f 1771/3992/3298 5211/3996/1905 7565/3997/1876 +f 1767/3993/3299 1772/3998/3301 1768/3999/3302 +f 1772/3998/3301 5736/4000/3303 5737/4001/3304 +f 5734/4002/3305 5735/4003/3306 1772/3998/3301 +f 5735/4003/3306 7703/4004/3307 5736/4000/3303 +f 1765/3987/3294 1768/3999/3302 1773/4005/3308 +f 1773/4005/3308 5696/3880/3203 5695/3873/3199 +f 1768/3999/3302 5737/4001/3304 5738/4006/3309 +f 5738/4006/3309 7685/3881/3204 5696/3880/3203 +f 1778/4007/3310 1779/4008/3311 1775/4009/3312 +f 1779/4008/3311 5742/4010/3313 5743/4011/3314 +f 5740/4012/3315 5741/4013/3316 1779/4008/3311 +f 5741/4013/3316 7704/4014/3317 5742/4010/3313 +f 1774/4015/3318 1775/4009/3312 1780/4016/3319 +f 1780/4016/3319 5228/2272/1936 5227/2267/1931 +f 1775/4009/3312 5743/4011/3314 5744/4017/3320 +f 1780/4016/3319 5744/4017/3320 7566/2231/1897 +f 1774/4015/3318 1776/4018/3321 1781/4019/3322 +f 1777/4020/3323 1781/4019/3322 5705/3898/3221 +f 1776/4018/3321 5227/2267/1931 5226/2266/1930 +f 1781/4019/3322 5226/2266/1930 7633/2270/1934 +f 1774/4015/3318 1777/4020/3323 1782/4021/3324 +f 1778/4007/3310 1782/4021/3324 5739/4022/3325 +f 5704/3897/3220 5703/3891/3214 1782/4021/3324 +f 5703/3891/3214 7686/3894/3217 5739/4022/3325 +f 1783/4023/3326 1787/4024/3327 1788/4025/3328 +f 1784/4026/3329 1788/4025/3328 5201/4027/1875 +f 1787/4024/3327 5734/4002/3305 5733/3994/3300 +f 5733/3994/3300 7565/3997/1876 5201/4027/1875 +f 1784/4026/3329 1789/4028/3330 1785/4029/3331 +f 1789/4028/3330 5745/4030/3332 5746/4031/3333 +f 5200/4032/1874 5199/4033/1884 1789/4028/3330 +f 5199/4033/1884 7567/4034/1844 5745/4030/3332 +f 1783/4023/3326 1785/4029/3331 1790/4035/3334 +f 1786/4036/3335 1790/4035/3334 5748/4037/3336 +f 1785/4029/3331 5746/4031/3333 5747/4038/3337 +f 1790/4035/3334 5747/4038/3337 7701/4039/3338 +f 1786/4036/3335 1791/4040/3339 1787/4024/3327 +f 1787/4024/3327 1791/4040/3339 5735/4003/3306 +f 5749/4041/3340 5750/4042/3341 1791/4040/3339 +f 1791/4040/3339 5750/4042/3341 7703/4004/3307 +f 1792/4043/3342 1796/4044/3343 1797/4045/3344 +f 1793/4046/3345 1797/4045/3344 5754/4047/3346 +f 1796/4044/3343 5752/4048/3347 5753/4049/3348 +f 1797/4045/3344 5753/4049/3348 7702/4050/3349 +f 1793/4046/3345 1798/4051/3350 1794/4052/3351 +f 1798/4051/3350 5210/4053/1900 5209/4054/1899 +f 5755/4055/3352 5756/4056/3353 1798/4051/3350 +f 5756/4056/3353 7568/2189/1866 5210/4053/1900 +f 1792/4043/3342 1794/4052/3351 1799/4057/3354 +f 1795/4058/3355 1799/4057/3354 5744/4059/3320 +f 1794/4052/3351 5209/4054/1899 5208/4060/1895 +f 5208/4060/1895 7566/4061/1897 5744/4059/3320 +f 1795/4058/3355 1800/4062/3356 1796/4044/3343 +f 1800/4062/3356 5751/4063/3357 5752/4048/3347 +f 1795/4058/3355 5743/4064/3314 5742/4065/3313 +f 1800/4062/3356 5742/4065/3313 7704/4066/3317 +f 1801/4067/3358 1805/4068/3359 1806/4069/3360 +f 1802/4070/3361 1806/4069/3360 5189/2162/1843 +f 1805/4068/3359 5746/4071/3333 5745/4072/3332 +f 1806/4069/3360 5745/4072/3332 7567/2163/1844 +f 1802/4070/3361 1807/4073/3362 1803/4074/3363 +f 1807/4073/3362 5757/4075/3364 5758/4076/3365 +f 5188/2161/1842 5187/2175/1852 1807/4073/3362 +f 5187/2175/1852 7569/2131/1812 5757/4075/3364 +f 1801/4067/3358 1803/4074/3363 1808/4077/3366 +f 1804/4078/3367 1808/4077/3366 5760/4079/3368 +f 1803/4074/3363 5758/4076/3365 5759/4080/3369 +f 1808/4077/3366 5759/4080/3369 7699/4081/3370 +f 1804/4078/3367 1809/4082/3371 1805/4068/3359 +f 1809/4082/3371 5747/4083/3337 5746/4071/3333 +f 5761/4084/3372 5762/4085/3373 1809/4082/3371 +f 5762/4085/3373 7701/4086/3338 5747/4083/3337 +f 1810/4087/3374 1814/4088/3375 1815/4089/3376 +f 1811/4090/3377 1815/4089/3376 5766/4091/3378 +f 1814/4088/3375 5764/4092/3379 5765/4093/3380 +f 1815/4089/3376 5765/4093/3380 7700/4094/3381 +f 1811/4090/3377 1816/4095/3382 1812/4096/3383 +f 1816/4095/3382 5198/2191/1868 5197/2186/1863 +f 5767/4097/3384 5768/4098/3385 1816/4095/3382 +f 5768/4098/3385 7570/2153/1834 5198/2191/1868 +f 1810/4087/3374 1812/4096/3383 1817/4099/3386 +f 1813/4100/3387 1817/4099/3386 5756/4056/3353 +f 1812/4096/3383 5197/2186/1863 5196/2185/1862 +f 1817/4099/3386 5196/2185/1862 7568/2189/1866 +f 1813/4100/3387 1818/4101/3388 1814/4088/3375 +f 1818/4101/3388 5763/4102/3389 5764/4092/3379 +f 5755/4055/3352 5754/4047/3346 1818/4101/3388 +f 5754/4047/3346 7702/4050/3349 5763/4102/3389 +f 1819/4103/3390 1823/4104/3391 1824/4105/3392 +f 1820/4106/3393 1824/4105/3392 5177/2130/1811 +f 1823/4104/3391 5758/4076/3365 5757/4075/3364 +f 1824/4105/3392 5757/4075/3364 7569/2131/1812 +f 1820/4106/3393 1825/4107/3394 1821/4108/3395 +f 1825/4107/3394 5769/4109/3396 5770/4110/3397 +f 5176/2129/1810 5175/2139/1820 1825/4107/3394 +f 5175/2139/1820 7571/2095/1780 5769/4109/3396 +f 1819/4103/3390 1821/4108/3395 1826/4111/3398 +f 1822/4112/3399 1826/4111/3398 5772/4113/3400 +f 1821/4108/3395 5770/4110/3397 5771/4114/3401 +f 1826/4111/3398 5771/4114/3401 7697/4115/3402 +f 1822/4112/3399 1827/4116/3403 1823/4104/3391 +f 1827/4116/3403 5759/4080/3369 5758/4076/3365 +f 5773/4117/3404 5774/4118/3405 1827/4116/3403 +f 5774/4118/3405 7699/4081/3370 5759/4080/3369 +f 1828/4119/3406 1832/4120/3407 1833/4121/3408 +f 1829/4122/3409 1833/4121/3408 5778/4123/3410 +f 1832/4120/3407 5776/4124/3411 5777/4125/3412 +f 1833/4121/3408 5777/4125/3412 7698/4126/3413 +f 1829/4122/3409 1834/4127/3414 1830/4128/3415 +f 1834/4127/3414 5186/2155/1836 5185/2150/1831 +f 5779/4129/3416 5780/4130/3417 1834/4127/3414 +f 5780/4130/3417 7572/2121/1802 5186/2155/1836 +f 1828/4119/3406 1830/4128/3415 1835/4131/3418 +f 1831/4132/3419 1835/4131/3418 5768/4098/3385 +f 1830/4128/3415 5185/2150/1831 5184/2149/1830 +f 1835/4131/3418 5184/2149/1830 7570/2153/1834 +f 1831/4132/3419 1836/4133/3420 1832/4120/3407 +f 1836/4133/3420 5775/4134/3421 5776/4124/3411 +f 5767/4097/3384 5766/4091/3378 1836/4133/3420 +f 5766/4091/3378 7700/4094/3381 5775/4134/3421 +f 1837/4135/3422 1841/4136/3423 1842/4137/3424 +f 1838/4138/3425 1842/4137/3424 5165/2094/1779 +f 1841/4136/3423 5770/4110/3397 5769/4109/3396 +f 1842/4137/3424 5769/4109/3396 7571/2095/1780 +f 1838/4138/3425 1843/4139/3426 1839/4140/3427 +f 1843/4139/3426 5781/4141/3428 5782/4142/3429 +f 5164/2093/1778 5163/2105/1788 1843/4139/3426 +f 5163/2105/1788 7573/2107/1748 5781/4141/3428 +f 1837/4135/3422 1839/4140/3427 1844/4143/3430 +f 1840/4144/3431 1844/4143/3430 5784/4145/3432 +f 1839/4140/3427 5782/4142/3429 5783/4146/3433 +f 1844/4143/3430 5783/4146/3433 7695/4147/3434 +f 1840/4144/3431 1845/4148/3435 1841/4136/3423 +f 1845/4148/3435 5771/4114/3401 5770/4110/3397 +f 5785/4149/3436 5786/4150/3437 1845/4148/3435 +f 5786/4150/3437 7697/4115/3402 5771/4114/3401 +f 1846/4151/3438 1850/4152/3439 1851/4153/3440 +f 1847/4154/3441 1851/4153/3440 5790/4155/3442 +f 1850/4152/3439 5788/4156/3443 5789/4157/3444 +f 1851/4153/3440 5789/4157/3444 7696/4158/3445 +f 1847/4154/3441 1852/4159/3446 1848/4160/3447 +f 1852/4159/3446 5174/2123/1804 5173/2118/1799 +f 5791/4161/3448 5792/4162/3449 1852/4159/3446 +f 5792/4162/3449 7574/2085/1770 5174/2123/1804 +f 1846/4151/3438 1848/4160/3447 1853/4163/3450 +f 1849/4164/3451 1853/4163/3450 5780/4130/3417 +f 1848/4160/3447 5173/2118/1799 5172/2117/1798 +f 1853/4163/3450 5172/2117/1798 7572/2121/1802 +f 1849/4164/3451 1854/4165/3452 1850/4152/3439 +f 1854/4165/3452 5787/4166/3453 5788/4156/3443 +f 5779/4129/3416 5778/4123/3410 1854/4165/3452 +f 5778/4123/3410 7698/4126/3413 5787/4166/3453 +f 1859/4167/3454 1860/4168/3455 1856/4169/3456 +f 1860/4168/3455 5153/4170/1747 5152/4171/1746 +f 1859/4167/3454 5782/4142/3429 5781/4141/3428 +f 1860/4168/3455 5781/4141/3428 7573/2107/1748 +f 1855/4172/3457 1856/4169/3456 1861/4173/3458 +f 1857/4174/3459 1861/4173/3458 5793/4175/3460 +f 1856/4169/3456 5152/4171/1746 5151/4176/1756 +f 1861/4173/3458 5151/4176/1756 7575/4177/1716 +f 1857/4174/3459 1862/4178/3461 1858/4179/3462 +f 1862/4178/3461 5796/4180/3463 5797/4181/3464 +f 5794/4182/3465 5795/4183/3466 1862/4178/3461 +f 5795/4183/3466 7693/4184/3467 5796/4180/3463 +f 1855/4172/3457 1858/4179/3462 1863/4185/3468 +f 1863/4185/3468 5783/4146/3433 5782/4142/3429 +f 1858/4179/3462 5797/4181/3464 5798/4186/3469 +f 1863/4185/3468 5798/4186/3469 7695/4147/3434 +f 1868/4187/3470 1869/4188/3471 1865/4189/3472 +f 1869/4188/3471 5802/4190/3473 5803/4191/3474 +f 5800/4192/3475 5801/4193/3476 1869/4188/3471 +f 5801/4193/3476 7694/4194/3477 5802/4190/3473 +f 1864/4195/3478 1865/4189/3472 1870/4196/3479 +f 1866/4197/3480 1870/4196/3479 5162/4198/1772 +f 1865/4189/3472 5803/4191/3474 5804/4199/3481 +f 1870/4196/3479 5804/4199/3481 7576/4200/1737 +f 1866/4197/3480 1871/4201/3482 1867/4202/3483 +f 1867/4202/3483 1871/4201/3482 5792/4203/3449 +f 5161/4204/1767 5160/4205/1766 1871/4201/3482 +f 1871/4201/3482 5160/4205/1766 7574/4206/1770 +f 1864/4195/3478 1867/4202/3483 1872/4207/3484 +f 1868/4187/3470 1872/4207/3484 5799/4208/3485 +f 5791/4209/3448 5790/4210/3442 1872/4207/3484 +f 1872/4207/3484 5790/4210/3442 7696/4211/3445 +f 1877/4212/3486 1878/4213/3487 1874/4214/3488 +f 1878/4213/3487 5141/4215/1715 5140/4216/1714 +f 5794/4182/3465 5793/4175/3460 1878/4213/3487 +f 5793/4175/3460 7575/4177/1716 5141/4215/1715 +f 1873/4217/3489 1874/4214/3488 1879/4218/3490 +f 1875/4219/3491 1879/4218/3490 5805/4220/3492 +f 1874/4214/3488 5140/4216/1714 5139/4221/1724 +f 5139/4221/1724 7577/4222/1684 5805/4220/3492 +f 1875/4219/3491 1880/4223/3493 1876/4224/3494 +f 1880/4223/3493 5808/4225/3495 5809/4226/3496 +f 5806/4227/3497 5807/4228/3498 1880/4223/3493 +f 5807/4228/3498 7691/4229/3499 5808/4225/3495 +f 1873/4217/3489 1876/4224/3494 1881/4230/3500 +f 1877/4212/3486 1881/4230/3500 5795/4183/3466 +f 1876/4224/3494 5809/4226/3496 5810/4231/3501 +f 1881/4230/3500 5810/4231/3501 7693/4184/3467 +f 1886/4232/3502 1887/4233/3503 1883/4234/3504 +f 1887/4233/3503 5814/4235/3505 5815/4236/3506 +f 5812/4237/3507 5813/4238/3508 1887/4233/3503 +f 5813/4238/3508 7692/4239/3509 5814/4235/3505 +f 1882/4240/3510 1883/4234/3504 1888/4241/3511 +f 1884/4242/3512 1888/4241/3511 5150/4243/1740 +f 1883/4234/3504 5815/4236/3506 5816/4244/3513 +f 5816/4244/3513 7578/4245/1706 5150/4243/1740 +f 1884/4242/3512 1889/4246/3514 1885/4247/3515 +f 1889/4246/3514 5804/4199/3481 5803/4191/3474 +f 5149/4248/1739 5148/4249/1735 1889/4246/3514 +f 5148/4249/1735 7576/4200/1737 5804/4199/3481 +f 1882/4240/3510 1885/4247/3515 1890/4250/3516 +f 1886/4232/3502 1890/4250/3516 5811/4251/3517 +f 1885/4247/3515 5803/4191/3474 5802/4190/3473 +f 1890/4250/3516 5802/4190/3473 7694/4194/3477 +f 1891/4252/3518 1895/4253/3519 1896/4254/3520 +f 1892/4255/3521 1896/4254/3520 5129/4256/1683 +f 1895/4253/3519 5806/4227/3497 5805/4220/3492 +f 1896/4254/3520 5805/4220/3492 7577/4222/1684 +f 1892/4255/3521 1897/4257/3522 1893/4258/3523 +f 1897/4257/3522 5817/4259/3524 5818/4260/3525 +f 5128/4261/1682 5127/4262/1692 1897/4257/3522 +f 5127/4262/1692 7630/1939/1656 5817/4259/3524 +f 1891/4252/3518 1893/4258/3523 1898/4263/3526 +f 1894/4264/3527 1898/4263/3526 5820/4265/3528 +f 1893/4258/3523 5818/4260/3525 5819/4266/3529 +f 1898/4263/3526 5819/4266/3529 7687/4267/3530 +f 1894/4264/3527 1899/4268/3531 1895/4253/3519 +f 1899/4268/3531 5807/4228/3498 5806/4227/3497 +f 5821/4269/3532 5822/4270/3533 1899/4268/3531 +f 1899/4268/3531 5822/4270/3533 7691/4229/3499 +f 1900/4271/3534 1904/4272/3535 1905/4273/3536 +f 1901/4274/3537 1905/4273/3536 5826/4275/3538 +f 1904/4272/3535 5824/4276/3539 5825/4277/3540 +f 1905/4273/3536 5825/4277/3540 7688/4278/3541 +f 1901/4274/3537 1906/4279/3542 1902/4280/3543 +f 1906/4279/3542 5138/4281/1708 5137/4282/1703 +f 5827/4283/3544 5828/4284/3545 1906/4279/3542 +f 5828/4284/3545 7631/4285/1672 5138/4281/1708 +f 1900/4271/3534 1902/4280/3543 1907/4286/3546 +f 1903/4287/3547 1907/4286/3546 5816/4244/3513 +f 1902/4280/3543 5137/4282/1703 5136/4288/1702 +f 1907/4286/3546 5136/4288/1702 7578/4245/1706 +f 1903/4287/3547 1908/4289/3548 1904/4272/3535 +f 1908/4289/3548 5823/4290/3549 5824/4276/3539 +f 5815/4236/3506 5814/4235/3505 1908/4289/3548 +f 1908/4289/3548 5814/4235/3505 7692/4239/3509 +f 1909/4291/3550 1913/4292/3551 1914/4293/3552 +f 1910/4294/3553 1914/4293/3552 5117/1938/1655 +f 1913/4292/3551 5818/4260/3525 5817/4259/3524 +f 1914/4293/3552 5817/4259/3524 7630/1939/1656 +f 1910/4294/3553 1915/4295/3554 1911/4296/3555 +f 1915/4295/3554 5829/4297/3556 5830/4298/3557 +f 5116/1937/1654 5115/1932/1649 1915/4295/3554 +f 5115/1932/1649 7579/1892/1618 5829/4297/3556 +f 1909/4291/3550 1911/4296/3555 1916/4299/3558 +f 1912/4300/3559 1916/4299/3558 5832/4301/3560 +f 1911/4296/3555 5830/4298/3557 5831/4302/3561 +f 1916/4299/3558 5831/4302/3561 7689/4303/3562 +f 1912/4300/3559 1917/4304/3563 1913/4292/3551 +f 1917/4304/3563 5819/4266/3529 5818/4260/3525 +f 5833/4305/3564 5834/4306/3565 1917/4304/3563 +f 5834/4306/3565 7687/4267/3530 5819/4266/3529 +f 1918/4307/3566 1922/4308/3567 1923/4309/3568 +f 1919/4310/3569 1923/4309/3568 5838/4311/3570 +f 1922/4308/3567 5836/4312/3571 5837/4313/3572 +f 1923/4309/3568 5837/4313/3572 7690/4314/3573 +f 1919/4310/3569 1924/4315/3574 1920/4316/3575 +f 1924/4315/3574 5126/4317/1674 5125/4318/1669 +f 5839/4319/3576 5840/4320/3577 1924/4315/3574 +f 5840/4320/3577 7580/4321/1635 5126/4317/1674 +f 1918/4307/3566 1920/4316/3575 1925/4322/3578 +f 1921/4323/3579 1925/4322/3578 5828/4324/3545 +f 1920/4316/3575 5125/4318/1669 5124/4325/1668 +f 1925/4322/3578 5124/4325/1668 7631/4326/1672 +f 1921/4323/3579 1926/4327/3580 1922/4308/3567 +f 1926/4327/3580 5835/4328/3581 5836/4312/3571 +f 5827/4329/3544 5826/4330/3538 1926/4327/3580 +f 5826/4330/3538 7688/4331/3541 5835/4328/3581 +f 1927/4332/3582 1931/4333/3583 1932/4334/3584 +f 1932/4334/3584 5102/1891/1617 5101/1883/1609 +f 1931/4333/3583 5830/4298/3557 5829/4297/3556 +f 5829/4297/3556 7579/1892/1618 5102/1891/1617 +f 1928/4335/3585 1933/4336/3586 1929/4337/3587 +f 1933/4336/3586 5723/3951/3266 5722/3950/3265 +f 1928/4335/3585 5101/1883/1609 5100/1882/1608 +f 5100/1882/1608 7561/1886/1612 5723/3951/3266 +f 1927/4332/3582 1929/4337/3587 1934/4338/3588 +f 1930/4339/3589 1934/4338/3588 5841/4340/3590 +f 1929/4337/3587 5722/3950/3265 5721/3964/3275 +f 1934/4338/3588 5721/3964/3275 7707/3966/3277 +f 1930/4339/3589 1935/4341/3591 1931/4333/3583 +f 1935/4341/3591 5831/4302/3561 5830/4298/3557 +f 5842/4342/3592 5843/4343/3593 1935/4341/3591 +f 5843/4343/3593 7689/4303/3562 5831/4302/3561 +f 1936/4344/3594 1940/4345/3595 1941/4346/3596 +f 1937/4347/3597 1941/4346/3596 5732/3985/3292 +f 1940/4345/3595 5845/4348/3598 5846/4349/3599 +f 1941/4346/3596 5846/4349/3599 7708/3986/3293 +f 1937/4347/3597 1942/4350/3600 1938/4351/3601 +f 1938/4351/3601 1942/4350/3600 5111/4352/1641 +f 5731/3984/3291 5730/3979/3288 1942/4350/3600 +f 5730/3979/3288 7562/3981/1642 5111/4352/1641 +f 1936/4344/3594 1938/4351/3601 1943/4353/3602 +f 1939/4354/3603 1943/4353/3602 5840/4320/3577 +f 5110/4355/1640 5109/4356/1633 1943/4353/3602 +f 5109/4356/1633 7580/4321/1635 5840/4320/3577 +f 1939/4354/3603 1944/4357/3604 1940/4345/3595 +f 1944/4357/3604 5844/4358/3605 5845/4348/3598 +f 5839/4319/3576 5838/4311/3570 1944/4357/3604 +f 5838/4311/3570 7690/4314/3573 5844/4358/3605 +f 1945/4359/3606 1949/4360/3607 1950/4361/3608 +f 1946/4362/3609 1950/4361/3608 5843/4343/3593 +f 1949/4360/3607 5848/4363/3610 5849/4364/3611 +f 1950/4361/3608 5849/4364/3611 7689/4303/3562 +f 1946/4362/3609 1951/4365/3612 1947/4366/3613 +f 1951/4365/3612 5850/4367/3614 5851/4368/3615 +f 5842/4342/3592 5841/4340/3590 1951/4365/3612 +f 5841/4340/3590 7707/3966/3277 5850/4367/3614 +f 1945/4359/3606 1947/4366/3613 1952/4369/3616 +f 1952/4369/3616 5853/4370/3617 5854/4371/3618 +f 1947/4366/3613 5851/4368/3615 5852/4372/3619 +f 5852/4372/3619 7709/4373/3620 5853/4370/3617 +f 1945/4359/3606 1948/4374/3621 1953/4375/3622 +f 1953/4375/3622 5847/4376/3623 5848/4363/3610 +f 1948/4374/3621 5854/4371/3618 5855/4377/3624 +f 1953/4375/3622 5855/4377/3624 7727/4378/3625 +f 1954/4379/3626 1958/4380/3627 1959/4381/3628 +f 1955/4382/3629 1959/4381/3628 5859/4383/3630 +f 5857/4384/3631 5858/4385/3632 1959/4381/3628 +f 5858/4385/3632 7710/4386/3633 5859/4383/3630 +f 1955/4382/3629 1960/4387/3634 1956/4388/3635 +f 1960/4387/3634 5846/4389/3599 5845/4390/3598 +f 5860/4391/3636 5861/4392/3637 1960/4387/3634 +f 5861/4392/3637 7708/4393/3293 5846/4389/3599 +f 1954/4379/3626 1956/4388/3635 1961/4394/3638 +f 1957/4395/3639 1961/4394/3638 5862/4396/3640 +f 1956/4388/3635 5845/4390/3598 5844/4397/3605 +f 1961/4394/3638 5844/4397/3605 7690/4398/3573 +f 1954/4379/3626 1957/4395/3639 1962/4399/3641 +f 1958/4380/3627 1962/4399/3641 5856/4400/3642 +f 5863/4401/3643 5864/4402/3644 1962/4399/3641 +f 1962/4399/3641 5864/4402/3644 7728/4403/3645 +f 1963/4404/3646 1967/4405/3647 1968/4406/3648 +f 1964/4407/3649 1968/4406/3648 5834/4306/3565 +f 1967/4405/3647 5866/4408/3650 5867/4409/3651 +f 1968/4406/3648 5867/4409/3651 7687/4267/3530 +f 1964/4407/3649 1969/4410/3652 1965/4411/3653 +f 1969/4410/3652 5849/4364/3611 5848/4363/3610 +f 5833/4305/3564 5832/4301/3560 1969/4410/3652 +f 5832/4301/3560 7689/4303/3562 5849/4364/3611 +f 1963/4404/3646 1965/4411/3653 1970/4412/3654 +f 1966/4413/3655 1970/4412/3654 5868/4414/3656 +f 1965/4411/3653 5848/4363/3610 5847/4376/3623 +f 1970/4412/3654 5847/4376/3623 7727/4378/3625 +f 1966/4413/3655 1971/4415/3657 1967/4405/3647 +f 1971/4415/3657 5865/4416/3658 5866/4408/3650 +f 5869/4417/3659 5870/4418/3660 1971/4415/3657 +f 5870/4418/3660 7729/4419/3661 5865/4416/3658 +f 1972/4420/3662 1976/4421/3663 1977/4422/3664 +f 1973/4423/3665 1977/4422/3664 5864/4402/3644 +f 1976/4421/3663 5872/4424/3666 5873/4425/3667 +f 1977/4422/3664 5873/4425/3667 7728/4403/3645 +f 1973/4423/3665 1978/4426/3668 1974/4427/3669 +f 1978/4426/3668 5837/4428/3572 5836/4429/3571 +f 5863/4401/3643 5862/4396/3640 1978/4426/3668 +f 5862/4396/3640 7690/4398/3573 5837/4428/3572 +f 1972/4420/3662 1974/4427/3669 1979/4430/3670 +f 1975/4431/3671 1979/4430/3670 5874/4432/3672 +f 1974/4427/3669 5836/4429/3571 5835/4433/3581 +f 1979/4430/3670 5835/4433/3581 7688/4434/3541 +f 1975/4431/3671 1980/4435/3673 1976/4421/3663 +f 1980/4435/3673 5871/4436/3674 5872/4424/3666 +f 5875/4437/3675 5876/4438/3676 1980/4435/3673 +f 5876/4438/3676 7730/4439/3677 5871/4436/3674 +f 1981/4440/3678 1985/4441/3679 1986/4442/3680 +f 1982/4443/3681 1986/4442/3680 5822/4444/3533 +f 5878/4445/3682 5879/4446/3683 1986/4442/3680 +f 5879/4446/3683 7691/4447/3499 5822/4444/3533 +f 1982/4443/3681 1987/4448/3684 1983/4449/3685 +f 1987/4448/3684 5867/4450/3651 5866/4451/3650 +f 5821/4452/3532 5820/4453/3528 1987/4448/3684 +f 5820/4453/3528 7687/4454/3530 5867/4450/3651 +f 1981/4440/3678 1983/4449/3685 1988/4455/3686 +f 1984/4456/3687 1988/4455/3686 5880/4457/3688 +f 1983/4449/3685 5866/4451/3650 5865/4458/3658 +f 1988/4455/3686 5865/4458/3658 7729/4459/3661 +f 1984/4456/3687 1989/4460/3689 1985/4441/3679 +f 1985/4441/3679 1989/4460/3689 5877/4461/3690 +f 5881/4462/3691 5882/4463/3692 1989/4460/3689 +f 5882/4463/3692 7725/4464/3693 5877/4461/3690 +f 1990/4465/3694 1994/4466/3695 1995/4467/3696 +f 1991/4468/3697 1995/4467/3696 5876/4438/3676 +f 1994/4466/3695 5884/4469/3698 5885/4470/3699 +f 1995/4467/3696 5885/4470/3699 7730/4439/3677 +f 1991/4468/3697 1996/4471/3700 1992/4472/3701 +f 1996/4471/3700 5825/4473/3540 5824/4474/3539 +f 5875/4437/3675 5874/4432/3672 1996/4471/3700 +f 5874/4432/3672 7688/4434/3541 5825/4473/3540 +f 1990/4465/3694 1992/4472/3701 1997/4475/3702 +f 1997/4475/3702 5886/4476/3703 5887/4477/3704 +f 1992/4472/3701 5824/4474/3539 5823/4478/3549 +f 5823/4478/3549 7692/4479/3509 5886/4476/3703 +f 1993/4480/3705 1998/4481/3706 1994/4466/3695 +f 1998/4481/3706 5883/4482/3707 5884/4469/3698 +f 1993/4480/3705 5887/4477/3704 5888/4483/3708 +f 5888/4483/3708 7726/4484/3709 5883/4482/3707 +f 2003/4485/3710 2004/4486/3711 2000/4487/3712 +f 2004/4486/3711 5810/4488/3501 5809/4489/3496 +f 5890/4490/3713 5891/4491/3714 2004/4486/3711 +f 5891/4491/3714 7693/4492/3467 5810/4488/3501 +f 1999/4493/3715 2000/4487/3712 2005/4494/3716 +f 2001/4495/3717 2005/4494/3716 5879/4446/3683 +f 2000/4487/3712 5809/4489/3496 5808/4496/3495 +f 2005/4494/3716 5808/4496/3495 7691/4447/3499 +f 2001/4495/3717 2006/4497/3718 2002/4498/3719 +f 2006/4497/3718 5892/4499/3720 5893/4500/3721 +f 5878/4445/3682 5877/4461/3690 2006/4497/3718 +f 2006/4497/3718 5877/4461/3690 7725/4464/3693 +f 1999/4493/3715 2002/4498/3719 2007/4501/3722 +f 2003/4485/3710 2007/4501/3722 5889/4502/3723 +f 2002/4498/3719 5893/4500/3721 5894/4503/3724 +f 2007/4501/3722 5894/4503/3724 7723/4504/3725 +f 2012/4505/3726 2013/4506/3727 2009/4507/3728 +f 2013/4506/3727 5888/4483/3708 5887/4477/3704 +f 5896/4508/3729 5897/4509/3730 2013/4506/3727 +f 2013/4506/3727 5897/4509/3730 7726/4484/3709 +f 2008/4510/3731 2009/4507/3728 2014/4511/3732 +f 2010/4512/3733 2014/4511/3732 5813/4513/3508 +f 2009/4507/3728 5887/4477/3704 5886/4476/3703 +f 2014/4511/3732 5886/4476/3703 7692/4479/3509 +f 2010/4512/3733 2015/4514/3734 2011/4515/3735 +f 2015/4514/3734 5898/4516/3736 5899/4517/3737 +f 5812/4518/3507 5811/4519/3517 2015/4514/3734 +f 5811/4519/3517 7694/4520/3477 5898/4516/3736 +f 2008/4510/3731 2011/4515/3735 2016/4521/3738 +f 2012/4505/3726 2016/4521/3738 5895/4522/3739 +f 2011/4515/3735 5899/4517/3737 5900/4523/3740 +f 2016/4521/3738 5900/4523/3740 7724/4524/3741 +f 2021/4525/3742 2022/4526/3743 2018/4527/3744 +f 2022/4526/3743 5798/4528/3469 5797/4529/3464 +f 2021/4525/3742 5902/4530/3745 5903/4531/3746 +f 2022/4526/3743 5903/4531/3746 7695/4532/3434 +f 2017/4533/3747 2018/4527/3744 2023/4534/3748 +f 2019/4535/3749 2023/4534/3748 5891/4491/3714 +f 2018/4527/3744 5797/4529/3464 5796/4536/3463 +f 2023/4534/3748 5796/4536/3463 7693/4492/3467 +f 2019/4535/3749 2024/4537/3750 2020/4538/3751 +f 2024/4537/3750 5904/4539/3752 5905/4540/3753 +f 5890/4490/3713 5889/4502/3723 2024/4537/3750 +f 5889/4502/3723 7723/4504/3725 5904/4539/3752 +f 2017/4533/3747 2020/4538/3751 2025/4541/3754 +f 2025/4541/3754 5901/4542/3755 5902/4530/3745 +f 5905/4540/3753 5906/4543/3756 2025/4541/3754 +f 5906/4543/3756 7721/4544/3757 5901/4542/3755 +f 2030/4545/3758 2031/4546/3759 2027/4547/3760 +f 2031/4546/3759 5900/4548/3740 5899/4549/3737 +f 5908/4550/3761 5909/4551/3762 2031/4546/3759 +f 5909/4551/3762 7724/4552/3741 5900/4548/3740 +f 2026/4553/3763 2027/4547/3760 2032/4554/3764 +f 2028/4555/3765 2032/4554/3764 5801/4556/3476 +f 2027/4547/3760 5899/4549/3737 5898/4557/3736 +f 2032/4554/3764 5898/4557/3736 7694/4558/3477 +f 2028/4555/3765 2033/4559/3766 2029/4560/3767 +f 2029/4560/3767 2033/4559/3766 5910/4561/3768 +f 5800/4562/3475 5799/4563/3485 2033/4559/3766 +f 2033/4559/3766 5799/4563/3485 7696/4564/3445 +f 2026/4553/3763 2029/4560/3767 2034/4565/3769 +f 2034/4565/3769 5907/4566/3770 5908/4550/3761 +f 5911/4567/3771 5912/4568/3772 2034/4565/3769 +f 5912/4568/3772 7722/4569/3773 5907/4566/3770 +f 2035/4570/3774 2039/4571/3775 2040/4572/3776 +f 2036/4573/3777 2040/4572/3776 5786/4574/3437 +f 2039/4571/3775 5914/4575/3778 5915/4576/3779 +f 2040/4572/3776 5915/4576/3779 7697/4577/3402 +f 2036/4573/3777 2041/4578/3780 2037/4579/3781 +f 2041/4578/3780 5903/4531/3746 5902/4530/3745 +f 5785/4580/3436 5784/4581/3432 2041/4578/3780 +f 5784/4581/3432 7695/4532/3434 5903/4531/3746 +f 2035/4570/3774 2037/4579/3781 2042/4582/3782 +f 2038/4583/3783 2042/4582/3782 5916/4584/3784 +f 2037/4579/3781 5902/4530/3745 5901/4542/3755 +f 2042/4582/3782 5901/4542/3755 7721/4544/3757 +f 2038/4583/3783 2043/4585/3785 2039/4571/3775 +f 2043/4585/3785 5913/4586/3786 5914/4575/3778 +f 5917/4587/3787 5918/4588/3788 2043/4585/3785 +f 5918/4588/3788 7719/4589/3789 5913/4586/3786 +f 2044/4590/3790 2048/4591/3791 2049/4592/3792 +f 2045/4593/3793 2049/4592/3792 5912/4568/3772 +f 2048/4591/3791 5920/4594/3794 5921/4595/3795 +f 2049/4592/3792 5921/4595/3795 7722/4569/3773 +f 2045/4593/3793 2050/4596/3796 2046/4597/3797 +f 2050/4596/3796 5789/4598/3444 5788/4599/3443 +f 5911/4567/3771 5910/4561/3768 2050/4596/3796 +f 5910/4561/3768 7696/4564/3445 5789/4598/3444 +f 2044/4590/3790 2046/4597/3797 2051/4600/3798 +f 2047/4601/3799 2051/4600/3798 5922/4602/3800 +f 2046/4597/3797 5788/4599/3443 5787/4603/3453 +f 2051/4600/3798 5787/4603/3453 7698/4604/3413 +f 2047/4601/3799 2052/4605/3801 2048/4591/3791 +f 2052/4605/3801 5919/4606/3802 5920/4594/3794 +f 5923/4607/3803 5924/4608/3804 2052/4605/3801 +f 5924/4608/3804 7720/4609/3805 5919/4606/3802 +f 2053/4610/3806 2057/4611/3807 2058/4612/3808 +f 2054/4613/3809 2058/4612/3808 5774/4118/3405 +f 2057/4611/3807 5926/4614/3810 5927/4615/3811 +f 2058/4612/3808 5927/4615/3811 7699/4081/3370 +f 2054/4613/3809 2059/4616/3812 2055/4617/3813 +f 2059/4616/3812 5915/4618/3779 5914/4619/3778 +f 5773/4117/3404 5772/4113/3400 2059/4616/3812 +f 5772/4113/3400 7697/4115/3402 5915/4618/3779 +f 2053/4610/3806 2055/4617/3813 2060/4620/3814 +f 2056/4621/3815 2060/4620/3814 5928/4622/3816 +f 2055/4617/3813 5914/4619/3778 5913/4623/3786 +f 2060/4620/3814 5913/4623/3786 7719/4624/3789 +f 2056/4621/3815 2061/4625/3817 2057/4611/3807 +f 2061/4625/3817 5925/4626/3818 5926/4614/3810 +f 5929/4627/3819 5930/4628/3820 2061/4625/3817 +f 2061/4625/3817 5930/4628/3820 7717/4629/3821 +f 2062/4630/3822 2066/4631/3823 2067/4632/3824 +f 2063/4633/3825 2067/4632/3824 5924/4608/3804 +f 2066/4631/3823 5932/4634/3826 5933/4635/3827 +f 2067/4632/3824 5933/4635/3827 7720/4609/3805 +f 2063/4633/3825 2068/4636/3828 2064/4637/3829 +f 2068/4636/3828 5777/4638/3412 5776/4639/3411 +f 5923/4607/3803 5922/4602/3800 2068/4636/3828 +f 5922/4602/3800 7698/4604/3413 5777/4638/3412 +f 2062/4630/3822 2064/4637/3829 2069/4640/3830 +f 2065/4641/3831 2069/4640/3830 5934/4642/3832 +f 2064/4637/3829 5776/4639/3411 5775/4643/3421 +f 2069/4640/3830 5775/4643/3421 7700/4644/3381 +f 2065/4641/3831 2070/4645/3833 2066/4631/3823 +f 2070/4645/3833 5931/4646/3834 5932/4634/3826 +f 5935/4647/3835 5936/4648/3836 2070/4645/3833 +f 2070/4645/3833 5936/4648/3836 7718/4649/3837 +f 2075/4650/3838 2076/4651/3839 2072/4652/3840 +f 2072/4652/3840 2076/4651/3839 5762/4085/3373 +f 5938/4653/3841 5939/4654/3842 2076/4651/3839 +f 2076/4651/3839 5939/4654/3842 7701/4086/3338 +f 2071/4655/3843 2072/4652/3840 2077/4656/3844 +f 2073/4657/3845 2077/4656/3844 5927/4615/3811 +f 5761/4084/3372 5760/4079/3368 2077/4656/3844 +f 5760/4079/3368 7699/4081/3370 5927/4615/3811 +f 2073/4657/3845 2078/4658/3846 2074/4659/3847 +f 2078/4658/3846 5940/4660/3848 5941/4661/3849 +f 5926/4614/3810 5925/4626/3818 2078/4658/3846 +f 5925/4626/3818 7717/4629/3821 5940/4660/3848 +f 2071/4655/3843 2074/4659/3847 2079/4662/3850 +f 2075/4650/3838 2079/4662/3850 5937/4663/3851 +f 2074/4659/3847 5941/4661/3849 5942/4664/3852 +f 2079/4662/3850 5942/4664/3852 7715/4665/3853 +f 2084/4666/3854 2085/4667/3855 2081/4668/3856 +f 2085/4667/3855 5936/4648/3836 5935/4647/3835 +f 5944/4669/3857 5945/4670/3858 2085/4667/3855 +f 5945/4670/3858 7718/4649/3837 5936/4648/3836 +f 2080/4671/3859 2081/4668/3856 2086/4672/3860 +f 2086/4672/3860 5765/4673/3380 5764/4674/3379 +f 2081/4668/3856 5935/4647/3835 5934/4642/3832 +f 5934/4642/3832 7700/4644/3381 5765/4673/3380 +f 2082/4675/3861 2087/4676/3862 2083/4677/3863 +f 2087/4676/3862 5946/4678/3864 5947/4679/3865 +f 2082/4675/3861 5764/4674/3379 5763/4680/3389 +f 2087/4676/3862 5763/4680/3389 7702/4681/3349 +f 2080/4671/3859 2083/4677/3863 2088/4682/3866 +f 2084/4666/3854 2088/4682/3866 5943/4683/3867 +f 2083/4677/3863 5947/4679/3865 5948/4684/3868 +f 2088/4682/3866 5948/4684/3868 7716/4685/3869 +f 2093/4686/3870 2094/4687/3871 2090/4688/3872 +f 2094/4687/3871 5750/4689/3341 5749/4690/3340 +f 5950/4691/3873 5951/4692/3874 2094/4687/3871 +f 5951/4692/3874 7703/4693/3307 5750/4689/3341 +f 2089/4694/3875 2090/4688/3872 2095/4695/3876 +f 2091/4696/3877 2095/4695/3876 5939/4654/3842 +f 2090/4688/3872 5749/4690/3340 5748/4697/3336 +f 5748/4697/3336 7701/4086/3338 5939/4654/3842 +f 2091/4696/3877 2096/4698/3878 2092/4699/3879 +f 2096/4698/3878 5952/4700/3880 5953/4701/3881 +f 5938/4653/3841 5937/4663/3851 2096/4698/3878 +f 5937/4663/3851 7715/4665/3853 5952/4700/3880 +f 2089/4694/3875 2092/4699/3879 2097/4702/3882 +f 2093/4686/3870 2097/4702/3882 5949/4703/3883 +f 2092/4699/3879 5953/4701/3881 5954/4704/3884 +f 2097/4702/3882 5954/4704/3884 7713/4705/3885 +f 2102/4706/3886 2103/4707/3887 2099/4708/3888 +f 2103/4707/3887 5948/4709/3868 5947/4710/3865 +f 5956/4711/3889 5957/4712/3890 2103/4707/3887 +f 5957/4712/3890 7716/4713/3869 5948/4709/3868 +f 2098/4714/3891 2099/4708/3888 2104/4715/3892 +f 2100/4716/3893 2104/4715/3892 5753/4717/3348 +f 2099/4708/3888 5947/4710/3865 5946/4718/3864 +f 5946/4718/3864 7702/4719/3349 5753/4717/3348 +f 2100/4716/3893 2105/4720/3894 2101/4721/3895 +f 2105/4720/3894 5958/4722/3896 5959/4723/3897 +f 5752/4724/3347 5751/4725/3357 2105/4720/3894 +f 5751/4725/3357 7704/4726/3317 5958/4722/3896 +f 2098/4714/3891 2101/4721/3895 2106/4727/3898 +f 2102/4706/3886 2106/4727/3898 5955/4728/3899 +f 2101/4721/3895 5959/4723/3897 5960/4729/3900 +f 2106/4727/3898 5960/4729/3900 7714/4730/3901 +f 2107/4731/3902 2111/4732/3903 2112/4733/3904 +f 2112/4733/3904 5738/4734/3309 5737/4735/3304 +f 2111/4732/3903 5962/4736/3905 5963/4737/3906 +f 2112/4733/3904 5963/4737/3906 7685/4738/3204 +f 2107/4731/3902 2108/4739/3907 2113/4740/3908 +f 2109/4741/3909 2113/4740/3908 5951/4692/3874 +f 2108/4739/3907 5737/4735/3304 5736/4742/3303 +f 2113/4740/3908 5736/4742/3303 7703/4693/3307 +f 2109/4741/3909 2114/4743/3910 2110/4744/3911 +f 2114/4743/3910 5964/4745/3912 5965/4746/3913 +f 5950/4691/3873 5949/4703/3883 2114/4743/3910 +f 5949/4703/3883 7713/4705/3885 5964/4745/3912 +f 2110/4744/3911 2115/4747/3914 2111/4732/3903 +f 2115/4747/3914 5961/4748/3915 5962/4736/3905 +f 5965/4746/3913 5966/4749/3916 2115/4747/3914 +f 5966/4749/3916 7731/4750/3917 5961/4748/3915 +f 2120/4751/3918 2121/4752/3919 2117/4753/3920 +f 2121/4752/3919 5960/4729/3900 5959/4723/3897 +f 5968/4754/3921 5969/4755/3922 2121/4752/3919 +f 5969/4755/3922 7714/4730/3901 5960/4729/3900 +f 2116/4756/3923 2117/4753/3920 2122/4757/3924 +f 2118/4758/3925 2122/4757/3924 5741/4759/3316 +f 2117/4753/3920 5959/4723/3897 5958/4722/3896 +f 2122/4757/3924 5958/4722/3896 7704/4726/3317 +f 2116/4756/3923 2118/4758/3925 2123/4760/3926 +f 2119/4761/3927 2123/4760/3926 5970/4762/3928 +f 5740/4763/3315 5739/4764/3325 2123/4760/3926 +f 2123/4760/3926 5739/4764/3325 7686/4765/3217 +f 2119/4761/3927 2124/4766/3929 2120/4751/3918 +f 2124/4766/3929 5967/4767/3930 5968/4754/3921 +f 5971/4768/3931 5972/4769/3932 2124/4766/3929 +f 5972/4769/3932 7732/4770/3933 5967/4767/3930 +f 2125/4771/3934 2129/4772/3935 2130/4773/3936 +f 2126/4774/3937 2130/4773/3936 5726/3965/3276 +f 2129/4772/3935 5851/4368/3615 5850/4367/3614 +f 5850/4367/3614 7707/3966/3277 5726/3965/3276 +f 2126/4774/3937 2131/4775/3938 2127/4776/3939 +f 2131/4775/3938 5973/4777/3940 5974/4778/3941 +f 5725/3959/3273 5724/3958/3272 2131/4775/3938 +f 5724/3958/3272 7705/3962/3245 5973/4777/3940 +f 2125/4771/3934 2127/4776/3939 2132/4779/3942 +f 2128/4780/3943 2132/4779/3942 5976/4781/3944 +f 2127/4776/3939 5974/4778/3941 5975/4782/3945 +f 2132/4779/3942 5975/4782/3945 7711/4783/3946 +f 2128/4780/3943 2133/4784/3947 2129/4772/3935 +f 2133/4784/3947 5852/4372/3619 5851/4368/3615 +f 5977/4785/3948 5978/4786/3949 2133/4784/3947 +f 5978/4786/3949 7709/4373/3620 5852/4372/3619 +f 2134/4787/3950 2138/4788/3951 2139/4789/3952 +f 2135/4790/3953 2139/4789/3952 5982/4791/3954 +f 2138/4788/3951 5980/4792/3955 5981/4793/3956 +f 2139/4789/3952 5981/4793/3956 7712/4794/3957 +f 2135/4790/3953 2140/4795/3958 2136/4796/3959 +f 2140/4795/3958 5729/4797/3282 5728/4798/3281 +f 5983/4799/3960 5984/4800/3961 2140/4795/3958 +f 5984/4800/3961 7706/4801/3261 5729/4797/3282 +f 2134/4787/3950 2136/4796/3959 2141/4802/3962 +f 2137/4803/3963 2141/4802/3962 5861/4392/3637 +f 2136/4796/3959 5728/4798/3281 5727/4804/3290 +f 5727/4804/3290 7708/4393/3293 5861/4392/3637 +f 2137/4803/3963 2142/4805/3964 2138/4788/3951 +f 2142/4805/3964 5979/4806/3965 5980/4792/3955 +f 5860/4391/3636 5859/4383/3630 2142/4805/3964 +f 5859/4383/3630 7710/4386/3633 5979/4806/3965 +f 2143/4807/3966 2147/4808/3967 2148/4809/3968 +f 2144/4810/3969 2148/4809/3968 5714/4811/3244 +f 2147/4808/3967 5974/4778/3941 5973/4777/3940 +f 2148/4809/3968 5973/4777/3940 7705/3962/3245 +f 2144/4810/3969 2149/4812/3970 2145/4813/3971 +f 2149/4812/3970 5985/4814/3972 5986/4815/3973 +f 5713/4816/3243 5712/4817/3240 2149/4812/3970 +f 5712/4817/3240 7683/4818/3209 5985/4814/3972 +f 2143/4807/3966 2145/4813/3971 2150/4819/3974 +f 2146/4820/3975 2150/4819/3974 5988/4821/3976 +f 2145/4813/3971 5986/4815/3973 5987/4822/3977 +f 2150/4819/3974 5987/4822/3977 7733/4823/3978 +f 2146/4820/3975 2151/4824/3979 2147/4808/3967 +f 2151/4824/3979 5975/4782/3945 5974/4778/3941 +f 5989/4825/3980 5990/4826/3981 2151/4824/3979 +f 5990/4826/3981 7711/4783/3946 5975/4782/3945 +f 2152/4827/3982 2156/4828/3983 2157/4829/3984 +f 2153/4830/3985 2157/4829/3984 5994/4831/3986 +f 2156/4828/3983 5992/4832/3987 5993/4833/3988 +f 2157/4829/3984 5993/4833/3988 7734/4834/3989 +f 2153/4830/3985 2158/4835/3990 2154/4836/3991 +f 2158/4835/3990 5717/4837/3251 5716/4838/3250 +f 5995/4839/3992 5996/4840/3993 2158/4835/3990 +f 5996/4840/3993 7684/4841/3229 5717/4837/3251 +f 2152/4827/3982 2154/4836/3991 2159/4842/3994 +f 2155/4843/3995 2159/4842/3994 5984/4800/3961 +f 2154/4836/3991 5716/4838/3250 5715/4844/3259 +f 2159/4842/3994 5715/4844/3259 7706/4801/3261 +f 2155/4843/3995 2160/4845/3996 2156/4828/3983 +f 2160/4845/3996 5991/4846/3997 5992/4832/3987 +f 5983/4799/3960 5982/4791/3954 2160/4845/3996 +f 5982/4791/3954 7712/4794/3957 5991/4846/3997 +f 2161/4847/3998 2165/4848/3999 2166/4849/4000 +f 2162/4850/4001 2166/4849/4000 5699/4851/3208 +f 2165/4848/3999 5986/4815/3973 5985/4814/3972 +f 2166/4849/4000 5985/4814/3972 7683/4818/3209 +f 2162/4850/4001 2167/4852/4002 2163/4853/4003 +f 2167/4852/4002 5963/4737/3906 5962/4736/3905 +f 5698/4854/3207 5697/4855/3202 2167/4852/4002 +f 5697/4855/3202 7685/4738/3204 5963/4737/3906 +f 2161/4847/3998 2163/4853/4003 2168/4856/4004 +f 2164/4857/4005 2168/4856/4004 5997/4858/4006 +f 2163/4853/4003 5962/4736/3905 5961/4748/3915 +f 2168/4856/4004 5961/4748/3915 7731/4750/3917 +f 2164/4857/4005 2169/4859/4007 2165/4848/3999 +f 2169/4859/4007 5987/4822/3977 5986/4815/3973 +f 5998/4860/4008 5999/4861/4009 2169/4859/4007 +f 5999/4861/4009 7733/4823/3978 5987/4822/3977 +f 2170/4862/4010 2174/4863/4011 2175/4864/4012 +f 2171/4865/4013 2175/4864/4012 5972/4769/3932 +f 2174/4863/4011 6001/4866/4014 6002/4867/4015 +f 2175/4864/4012 6002/4867/4015 7732/4770/3933 +f 2171/4865/4013 2176/4868/4016 2172/4869/4017 +f 2176/4868/4016 5702/4870/3216 5701/4871/3215 +f 5971/4768/3931 5970/4762/3928 2176/4868/4016 +f 5970/4762/3928 7686/4765/3217 5702/4870/3216 +f 2170/4862/4010 2172/4869/4017 2177/4872/4018 +f 2173/4873/4019 2177/4872/4018 5996/4874/3993 +f 2172/4869/4017 5701/4871/3215 5700/4875/3226 +f 2177/4872/4018 5700/4875/3226 7684/4876/3229 +f 2173/4873/4019 2178/4877/4020 2174/4863/4011 +f 2178/4877/4020 6000/4878/4021 6001/4866/4014 +f 5995/4879/3992 5994/4880/3986 2178/4877/4020 +f 5994/4880/3986 7734/4881/3989 6000/4878/4021 +f 2183/4882/4022 2184/4883/4023 2180/4884/4024 +f 2184/4883/4023 5084/1848/1579 5083/1843/1574 +f 2183/4882/4022 6004/4885/4025 6005/4886/4026 +f 6005/4886/4026 7518/1849/1580 5084/1848/1579 +f 2179/4887/4027 2180/4884/4024 2185/4888/4028 +f 2181/4889/4029 2185/4888/4028 6006/4890/4030 +f 2180/4884/4024 5083/1843/1574 5082/1842/1573 +f 2185/4888/4028 5082/1842/1573 7559/1807/1540 +f 2181/4889/4029 2186/4891/4031 2182/4892/4032 +f 2186/4891/4031 6009/4893/4033 6010/4894/4034 +f 6007/4895/4035 6008/4896/4036 2186/4891/4031 +f 6008/4896/4036 7735/4897/4037 6009/4893/4033 +f 2179/4887/4027 2182/4892/4032 2187/4898/4038 +f 2183/4882/4022 2187/4898/4038 6003/4899/4039 +f 2182/4892/4032 6010/4894/4034 6011/4900/4040 +f 2187/4898/4038 6011/4900/4040 7524/4901/4041 +f 2192/4902/4042 2193/4903/4043 2189/4904/4044 +f 2193/4903/4043 6015/4905/4045 6016/4906/4046 +f 6013/4907/4047 6014/4908/4048 2193/4903/4043 +f 6014/4908/4048 7736/4909/4049 6015/4905/4045 +f 2188/4910/4050 2189/4904/4044 2194/4911/4051 +f 2190/4912/4052 2194/4911/4051 5096/4913/1602 +f 2189/4904/4044 6016/4906/4046 6017/4914/4053 +f 2194/4911/4051 6017/4914/4053 7560/4915/1563 +f 2190/4912/4052 2195/4916/4054 2191/4917/4055 +f 2191/4917/4055 2195/4916/4054 6005/4918/4026 +f 5095/4919/1601 5094/4920/1598 2195/4916/4054 +f 5094/4920/1598 7518/4921/1580 6005/4918/4026 +f 2188/4910/4050 2191/4917/4055 2196/4922/4056 +f 2192/4902/4042 2196/4922/4056 6012/4923/4057 +f 2191/4917/4055 6004/4924/4025 6003/4925/4039 +f 2196/4922/4056 6003/4925/4039 7524/4926/4041 +f 2201/4927/4058 2202/4928/4059 2198/4929/4060 +f 2202/4928/4059 5066/1806/1539 5065/1798/1534 +f 6007/4895/4035 6006/4890/4030 2202/4928/4059 +f 6006/4890/4030 7559/1807/1540 5066/1806/1539 +f 2197/4930/4061 2198/4929/4060 2203/4931/4062 +f 2199/4932/4063 2203/4931/4062 6018/4933/4064 +f 2198/4929/4060 5065/1798/1534 5064/1797/1533 +f 2203/4931/4062 5064/1797/1533 7557/1801/1500 +f 2199/4932/4063 2204/4934/4065 2200/4935/4066 +f 2204/4934/4065 6021/4936/4067 6022/4937/4068 +f 2199/4932/4063 6019/4938/4069 6020/4939/4070 +f 2204/4934/4065 6020/4939/4070 7737/4940/4071 +f 2197/4930/4061 2200/4935/4066 2205/4941/4072 +f 2201/4927/4058 2205/4941/4072 6008/4896/4036 +f 2200/4935/4066 6022/4937/4068 6023/4942/4073 +f 2205/4941/4072 6023/4942/4073 7735/4897/4037 +f 2210/4943/4074 2211/4944/4075 2207/4945/4076 +f 2207/4945/4076 2211/4944/4075 6027/4946/4077 +f 6025/4947/4078 6026/4948/4079 2211/4944/4075 +f 2211/4944/4075 6026/4948/4079 7738/4949/4080 +f 2206/4950/4081 2207/4945/4076 2212/4951/4082 +f 2208/4952/4083 2212/4951/4082 5081/4953/1567 +f 2207/4945/4076 6028/4954/4084 6029/4955/4085 +f 2212/4951/4082 6029/4955/4085 7558/4956/1523 +f 2208/4952/4083 2213/4957/4086 2209/4958/4087 +f 2213/4957/4086 6017/4914/4053 6016/4906/4046 +f 5080/4959/1566 5079/4960/1561 2213/4957/4086 +f 5079/4960/1561 7560/4915/1563 6017/4914/4053 +f 2206/4950/4081 2209/4958/4087 2214/4961/4088 +f 2210/4943/4074 2214/4961/4088 6024/4962/4089 +f 2209/4958/4087 6016/4906/4046 6015/4905/4045 +f 2214/4961/4088 6015/4905/4045 7736/4909/4049 +f 2219/4963/4090 2220/4964/4091 2216/4965/4092 +f 2220/4964/4091 5048/4966/1499 5047/4967/1494 +f 6019/4968/4069 6018/4969/4064 2220/4964/4091 +f 6018/4969/4064 7557/4970/1500 5048/4966/1499 +f 2215/4971/4093 2216/4965/4092 2221/4972/4094 +f 2217/4973/4095 2221/4972/4094 6030/4974/4096 +f 2216/4965/4092 5047/4967/1494 5046/4975/1493 +f 2221/4972/4094 5046/4975/1493 7555/4976/1460 +f 2217/4973/4095 2222/4977/4097 2218/4978/4098 +f 2222/4977/4097 6033/4979/4099 6034/4980/4100 +f 6031/4981/4101 6032/4982/4102 2222/4977/4097 +f 6032/4982/4102 7739/4983/4103 6033/4979/4099 +f 2215/4971/4093 2218/4978/4098 2223/4984/4104 +f 2219/4963/4090 2223/4984/4104 6020/4985/4070 +f 2218/4978/4098 6034/4980/4100 6035/4986/4105 +f 2223/4984/4104 6035/4986/4105 7737/4987/4071 +f 2228/4988/4106 2229/4989/4107 2225/4990/4108 +f 2229/4989/4107 6039/4991/4109 6040/4992/4110 +f 6037/4993/4111 6038/4994/4112 2229/4989/4107 +f 6038/4994/4112 7740/4995/4113 6039/4991/4109 +f 2224/4996/4114 2225/4990/4108 2230/4997/4115 +f 2226/4998/4116 2230/4997/4115 5063/4999/1527 +f 2225/4990/4108 6040/4992/4110 6041/5000/4117 +f 2230/4997/4115 6041/5000/4117 7556/5001/1483 +f 2226/4998/4116 2231/5002/4118 2227/5003/4119 +f 2231/5002/4118 6029/4955/4085 6028/4954/4084 +f 5062/5004/1526 5061/5005/1521 2231/5002/4118 +f 5061/5005/1521 7558/4956/1523 6029/4955/4085 +f 2224/4996/4114 2227/5003/4119 2232/5006/4120 +f 2228/4988/4106 2232/5006/4120 6036/5007/4121 +f 2227/5003/4119 6028/4954/4084 6027/4946/4077 +f 2232/5006/4120 6027/4946/4077 7738/4949/4080 +f 2237/5008/4122 2238/5009/4123 2234/5010/4124 +f 2238/5009/4123 5030/5011/1459 5029/5012/1454 +f 6031/4981/4101 6030/4974/4096 2238/5009/4123 +f 6030/4974/4096 7555/4976/1460 5030/5011/1459 +f 2233/5013/4125 2234/5010/4124 2239/5014/4126 +f 2235/5015/4127 2239/5014/4126 6042/5016/4128 +f 2234/5010/4124 5029/5012/1454 5028/5017/1453 +f 2239/5014/4126 5028/5017/1453 7553/5018/1420 +f 2235/5015/4127 2240/5019/4129 2236/5020/4130 +f 2236/5020/4130 2240/5019/4129 6045/5021/4131 +f 6043/5022/4132 6044/5023/4133 2240/5019/4129 +f 2240/5019/4129 6044/5023/4133 7741/5024/4134 +f 2233/5013/4125 2236/5020/4130 2241/5025/4135 +f 2237/5008/4122 2241/5025/4135 6032/4982/4102 +f 2236/5020/4130 6046/5026/4136 6047/5027/4137 +f 2241/5025/4135 6047/5027/4137 7739/4983/4103 +f 2246/5028/4138 2247/5029/4139 2243/5030/4140 +f 2247/5029/4139 6051/5031/4141 6052/5032/4142 +f 2246/5028/4138 6049/5033/4143 6050/5034/4144 +f 2247/5029/4139 6050/5034/4144 7742/5035/4145 +f 2242/5036/4146 2243/5030/4140 2248/5037/4147 +f 2244/5038/4148 2248/5037/4147 5045/1746/1487 +f 2243/5030/4140 6052/5032/4142 6053/5039/4149 +f 2248/5037/4147 6053/5039/4149 7554/1702/1443 +f 2244/5038/4148 2249/5040/4150 2245/5041/4151 +f 2249/5040/4150 6041/5042/4117 6040/5043/4110 +f 5044/1745/1486 5043/1739/1480 2249/5040/4150 +f 5043/1739/1480 7556/1742/1483 6041/5042/4117 +f 2242/5036/4146 2245/5041/4151 2250/5044/4152 +f 2246/5028/4138 2250/5044/4152 6048/5045/4153 +f 2245/5041/4151 6040/5043/4110 6039/5046/4109 +f 2250/5044/4152 6039/5046/4109 7740/5047/4113 +f 2255/5048/4154 2256/5049/4155 2252/5050/4156 +f 2256/5049/4155 5012/5051/1419 5011/5052/1414 +f 6043/5022/4132 6042/5016/4128 2256/5049/4155 +f 6042/5016/4128 7553/5018/1420 5012/5051/1419 +f 2251/5053/4157 2252/5050/4156 2257/5054/4158 +f 2253/5055/4159 2257/5054/4158 6054/5056/4160 +f 2252/5050/4156 5011/5052/1414 5010/5057/1413 +f 2257/5054/4158 5010/5057/1413 7551/5058/1380 +f 2253/5055/4159 2258/5059/4161 2254/5060/4162 +f 2258/5059/4161 6057/5061/4163 6058/5062/4164 +f 6055/5063/4165 6056/5064/4166 2258/5059/4161 +f 6056/5064/4166 7743/5065/4167 6057/5061/4163 +f 2251/5053/4157 2254/5060/4162 2259/5066/4168 +f 2255/5048/4154 2259/5066/4168 6044/5023/4133 +f 2254/5060/4162 6058/5062/4164 6059/5067/4169 +f 6059/5067/4169 7741/5024/4134 6044/5023/4133 +f 2264/5068/4170 2265/5069/4171 2261/5070/4172 +f 2265/5069/4171 6063/5071/4173 6064/5072/4174 +f 6061/5073/4175 6062/5074/4176 2265/5069/4171 +f 6062/5074/4176 7744/5075/4177 6063/5071/4173 +f 2260/5076/4178 2261/5070/4172 2266/5077/4179 +f 2262/5078/4180 2266/5077/4179 5027/1706/1447 +f 2261/5070/4172 6064/5072/4174 6065/5079/4181 +f 2266/5077/4179 6065/5079/4181 7552/1657/1403 +f 2262/5078/4180 2267/5080/4182 2263/5081/4183 +f 2267/5080/4182 6053/5039/4149 6052/5032/4142 +f 5026/1705/1446 5025/1700/1441 2267/5080/4182 +f 5025/1700/1441 7554/1702/1443 6053/5039/4149 +f 2260/5076/4178 2263/5081/4183 2268/5082/4184 +f 2264/5068/4170 2268/5082/4184 6060/5083/4185 +f 2263/5081/4183 6052/5032/4142 6051/5031/4141 +f 6051/5031/4141 7742/5035/4145 6060/5083/4185 +f 2273/5084/4186 2274/5085/4187 2270/5086/4188 +f 2274/5085/4187 4994/5087/1379 4993/5088/1374 +f 6055/5063/4165 6054/5056/4160 2274/5085/4187 +f 6054/5056/4160 7551/5058/1380 4994/5087/1379 +f 2269/5089/4189 2270/5086/4188 2275/5090/4190 +f 2271/5091/4191 2275/5090/4190 6066/5092/4192 +f 2270/5086/4188 4993/5088/1374 4992/5093/1373 +f 2275/5090/4190 4992/5093/1373 7549/5094/1340 +f 2271/5091/4191 2276/5095/4193 2272/5096/4194 +f 2276/5095/4193 6069/5097/4195 6070/5098/4196 +f 6067/5099/4197 6068/5100/4198 2276/5095/4193 +f 6068/5100/4198 7745/5101/4199 6069/5097/4195 +f 2269/5089/4189 2272/5096/4194 2277/5102/4200 +f 2273/5084/4186 2277/5102/4200 6056/5064/4166 +f 2272/5096/4194 6070/5098/4196 6071/5103/4201 +f 2277/5102/4200 6071/5103/4201 7743/5065/4167 +f 2282/5104/4202 2283/5105/4203 2279/5106/4204 +f 2283/5105/4203 6075/5107/4205 6076/5108/4206 +f 6073/5109/4207 6074/5110/4208 2283/5105/4203 +f 6074/5110/4208 7746/5111/4209 6075/5107/4205 +f 2278/5112/4210 2279/5106/4204 2284/5113/4211 +f 2280/5114/4212 2284/5113/4211 5009/1663/1407 +f 2279/5106/4204 6076/5108/4206 6077/5115/4213 +f 2284/5113/4211 6077/5115/4213 7550/1664/1363 +f 2280/5114/4212 2285/5116/4214 2281/5117/4215 +f 2285/5116/4214 6065/5079/4181 6064/5072/4174 +f 5008/1662/1406 5007/1655/1401 2285/5116/4214 +f 5007/1655/1401 7552/1657/1403 6065/5079/4181 +f 2278/5112/4210 2281/5117/4215 2286/5118/4216 +f 2282/5104/4202 2286/5118/4216 6072/5119/4217 +f 2281/5117/4215 6064/5072/4174 6063/5071/4173 +f 2286/5118/4216 6063/5071/4173 7744/5075/4177 +f 2291/5120/4218 2292/5121/4219 2288/5122/4220 +f 2292/5121/4219 4976/5123/1339 4975/5124/1334 +f 6067/5099/4197 6066/5092/4192 2292/5121/4219 +f 6066/5092/4192 7549/5094/1340 4976/5123/1339 +f 2287/5125/4221 2288/5122/4220 2293/5126/4222 +f 2289/5127/4223 2293/5126/4222 6078/5128/4224 +f 2288/5122/4220 4975/5124/1334 4974/5129/1333 +f 2293/5126/4222 4974/5129/1333 7547/5130/1300 +f 2289/5127/4223 2294/5131/4225 2290/5132/4226 +f 2294/5131/4225 6081/5133/4227 6082/5134/4228 +f 6079/5135/4229 6080/5136/4230 2294/5131/4225 +f 6080/5136/4230 7747/5137/4231 6081/5133/4227 +f 2287/5125/4221 2290/5132/4226 2295/5138/4232 +f 2291/5120/4218 2295/5138/4232 6068/5100/4198 +f 2290/5132/4226 6082/5134/4228 6083/5139/4233 +f 2295/5138/4232 6083/5139/4233 7745/5101/4199 +f 2300/5140/4234 2301/5141/4235 2297/5142/4236 +f 2301/5141/4235 6087/5143/4237 6088/5144/4238 +f 6085/5145/4239 6086/5146/4240 2301/5141/4235 +f 6086/5146/4240 7748/5147/4241 6087/5143/4237 +f 2296/5148/4242 2297/5142/4236 2302/5149/4243 +f 2298/5150/4244 2302/5149/4243 4991/5151/1367 +f 2297/5142/4236 6088/5144/4238 6089/5152/4245 +f 2302/5149/4243 6089/5152/4245 7548/5153/1324 +f 2298/5150/4244 2303/5154/4246 2299/5155/4247 +f 2303/5154/4246 6077/5156/4213 6076/5157/4206 +f 4990/5158/1366 4989/5159/1361 2303/5154/4246 +f 4989/5159/1361 7550/5160/1363 6077/5156/4213 +f 2296/5148/4242 2299/5155/4247 2304/5161/4248 +f 2300/5140/4234 2304/5161/4248 6084/5162/4249 +f 2299/5155/4247 6076/5157/4206 6075/5163/4205 +f 2304/5161/4248 6075/5163/4205 7746/5164/4209 +f 2305/5165/4250 2309/5166/4251 2310/5167/4252 +f 2306/5168/4253 2310/5167/4252 4958/5169/1299 +f 6079/5170/4229 6078/5171/4224 2310/5167/4252 +f 6078/5171/4224 7547/5172/1300 4958/5169/1299 +f 2306/5168/4253 2311/5173/4254 2307/5174/4255 +f 2311/5173/4254 6090/5175/4256 6091/5176/4257 +f 4957/5177/1298 4956/5178/1294 2311/5173/4254 +f 4956/5178/1294 7545/5179/1261 6090/5175/4256 +f 2305/5165/4250 2307/5174/4255 2312/5180/4258 +f 2308/5181/4259 2312/5180/4258 6093/5182/4260 +f 2307/5174/4255 6091/5176/4257 6092/5183/4261 +f 2312/5180/4258 6092/5183/4261 7749/5184/4262 +f 2308/5181/4259 2313/5185/4263 2309/5166/4251 +f 2313/5185/4263 6080/5186/4230 6079/5170/4229 +f 6094/5187/4264 6095/5188/4265 2313/5185/4263 +f 6095/5188/4265 7747/5189/4231 6080/5186/4230 +f 2314/5190/4266 2318/5191/4267 2319/5192/4268 +f 2315/5193/4269 2319/5192/4268 6099/5194/4270 +f 2318/5191/4267 6097/5195/4271 6098/5196/4272 +f 2319/5192/4268 6098/5196/4272 7750/5197/4273 +f 2315/5193/4269 2320/5198/4274 2316/5199/4275 +f 2320/5198/4274 4973/5200/1327 4972/5201/1321 +f 6100/5202/4276 6101/5203/4277 2320/5198/4274 +f 6101/5203/4277 7546/5204/1284 4973/5200/1327 +f 2314/5190/4266 2316/5199/4275 2321/5205/4278 +f 2321/5205/4278 6089/5152/4245 6088/5144/4238 +f 2316/5199/4275 4972/5201/1321 4971/5206/1320 +f 4971/5206/1320 7548/5153/1324 6089/5152/4245 +f 2317/5207/4279 2322/5208/4280 2318/5191/4267 +f 2322/5208/4280 6096/5209/4281 6097/5195/4271 +f 6088/5144/4238 6087/5143/4237 2322/5208/4280 +f 6087/5143/4237 7748/5147/4241 6096/5209/4281 +f 2323/5210/4282 2327/5211/4283 2328/5212/4284 +f 2324/5213/4285 2328/5212/4284 4940/5214/1260 +f 2327/5211/4283 6091/5176/4257 6090/5175/4256 +f 2328/5212/4284 6090/5175/4256 7545/5179/1261 +f 2324/5213/4285 2329/5215/4286 2325/5216/4287 +f 2329/5215/4286 6102/5217/4288 6103/5218/4289 +f 4939/5219/1259 4938/5220/1254 2329/5215/4286 +f 4938/5220/1254 7543/1458/1214 6102/5217/4288 +f 2323/5210/4282 2325/5216/4287 2330/5221/4290 +f 2326/5222/4291 2330/5221/4290 6105/5223/4292 +f 2325/5216/4287 6103/5218/4289 6104/5224/4293 +f 2330/5221/4290 6104/5224/4293 7751/5225/4294 +f 2326/5222/4291 2331/5226/4295 2327/5211/4283 +f 2331/5226/4295 6092/5183/4261 6091/5176/4257 +f 6106/5227/4296 6107/5228/4297 2331/5226/4295 +f 6107/5228/4297 7749/5184/4262 6092/5183/4261 +f 2332/5229/4298 2336/5230/4299 2337/5231/4300 +f 2333/5232/4301 2337/5231/4300 6111/5233/4302 +f 2336/5230/4299 6109/5234/4303 6110/5235/4304 +f 2337/5231/4300 6110/5235/4304 7752/5236/4305 +f 2333/5232/4301 2338/5237/4306 2334/5238/4307 +f 2338/5237/4306 4955/5239/1287 4954/5240/1281 +f 6112/5241/4308 6113/5242/4309 2338/5237/4306 +f 6113/5242/4309 7544/5243/1239 4955/5239/1287 +f 2332/5229/4298 2334/5238/4307 2339/5244/4310 +f 2335/5245/4311 2339/5244/4310 6101/5203/4277 +f 2334/5238/4307 4954/5240/1281 4953/5246/1280 +f 2339/5244/4310 4953/5246/1280 7546/5204/1284 +f 2335/5245/4311 2340/5247/4312 2336/5230/4299 +f 2340/5247/4312 6108/5248/4313 6109/5234/4303 +f 6100/5202/4276 6099/5194/4270 2340/5247/4312 +f 6099/5194/4270 7750/5197/4273 6108/5248/4313 +f 2345/5249/4314 2346/5250/4315 2342/5251/4316 +f 2346/5250/4315 6117/5252/4317 6118/5253/4318 +f 6115/5254/4319 6116/5255/4320 2346/5250/4315 +f 6116/5255/4320 7759/5256/4321 6117/5252/4317 +f 2341/5257/4322 2342/5251/4316 2347/5258/4323 +f 2343/5259/4324 2347/5258/4323 6120/5260/4325 +f 2342/5251/4316 6118/5253/4318 6119/5261/4326 +f 2347/5258/4323 6119/5261/4326 7760/5262/4327 +f 2343/5259/4324 2348/5263/4328 2344/5264/4329 +f 2348/5263/4328 6123/5265/4330 6124/5266/4331 +f 6121/5267/4332 6122/5268/4333 2348/5263/4328 +f 6122/5268/4333 7779/5269/4334 6123/5265/4330 +f 2341/5257/4322 2344/5264/4329 2349/5270/4335 +f 2345/5249/4314 2349/5270/4335 6114/5271/4336 +f 2344/5264/4329 6124/5266/4331 6125/5272/4337 +f 2349/5270/4335 6125/5272/4337 7789/5273/4338 +f 2354/5274/4339 2355/5275/4340 2351/5276/4341 +f 2355/5275/4340 6129/5277/4342 6130/5278/4343 +f 6127/5279/4344 6128/5280/4345 2355/5275/4340 +f 6128/5280/4345 7780/5281/4346 6129/5277/4342 +f 2350/5282/4347 2351/5276/4341 2356/5283/4348 +f 2352/5284/4349 2356/5283/4348 6119/5285/4326 +f 2351/5276/4341 6130/5278/4343 6131/5286/4350 +f 2356/5283/4348 6131/5286/4350 7760/5287/4327 +f 2352/5284/4349 2357/5288/4351 2353/5289/4352 +f 2357/5288/4351 6132/5290/4353 6133/5291/4354 +f 6118/5292/4318 6117/5293/4317 2357/5288/4351 +f 6117/5293/4317 7759/5294/4321 6132/5290/4353 +f 2350/5282/4347 2353/5289/4352 2358/5295/4355 +f 2354/5274/4339 2358/5295/4355 6126/5296/4356 +f 2353/5289/4352 6133/5291/4354 6134/5297/4357 +f 2358/5295/4355 6134/5297/4357 7790/5298/4358 +f 2363/5299/4359 2364/5300/4360 2360/5301/4361 +f 2364/5300/4360 6138/5302/4362 6139/5303/4363 +f 6136/5304/4364 6137/5305/4365 2364/5300/4360 +f 6137/5305/4365 7758/5306/4366 6138/5302/4362 +f 2359/5307/4367 2360/5301/4361 2365/5308/4368 +f 2361/5309/4369 2365/5308/4368 6116/5310/4320 +f 2360/5301/4361 6139/5303/4363 6140/5311/4370 +f 2365/5308/4368 6140/5311/4370 7759/5294/4321 +f 2361/5309/4369 2366/5312/4371 2362/5313/4372 +f 2366/5312/4371 6141/5314/4373 6142/5315/4374 +f 6115/5316/4319 6114/5317/4336 2366/5312/4371 +f 6114/5317/4336 7789/5318/4338 6141/5314/4373 +f 2359/5307/4367 2362/5313/4372 2367/5319/4375 +f 2363/5299/4359 2367/5319/4375 6135/5320/4376 +f 2362/5313/4372 6142/5315/4374 6143/5321/4377 +f 2367/5319/4375 6143/5321/4377 7787/5322/4378 +f 2372/5323/4379 2373/5324/4380 2369/5325/4381 +f 2373/5324/4380 6134/5297/4357 6133/5291/4354 +f 6145/5326/4382 6146/5327/4383 2373/5324/4380 +f 6146/5327/4383 7790/5298/4358 6134/5297/4357 +f 2368/5328/4384 2369/5325/4381 2374/5329/4385 +f 2370/5330/4386 2374/5329/4385 6140/5311/4370 +f 2369/5325/4381 6133/5291/4354 6132/5290/4353 +f 2374/5329/4385 6132/5290/4353 7759/5294/4321 +f 2370/5330/4386 2375/5331/4387 2371/5332/4388 +f 2375/5331/4387 6147/5333/4389 6148/5334/4390 +f 6139/5303/4363 6138/5302/4362 2375/5331/4387 +f 6138/5302/4362 7758/5306/4366 6147/5333/4389 +f 2368/5328/4384 2371/5332/4388 2376/5335/4391 +f 2372/5323/4379 2376/5335/4391 6144/5336/4392 +f 2371/5332/4388 6148/5334/4390 6149/5337/4393 +f 2376/5335/4391 6149/5337/4393 7788/5338/4394 +f 2381/5339/4395 2382/5340/4396 2378/5341/4397 +f 2382/5340/4396 6153/5342/4398 6154/5343/4399 +f 6151/5344/4400 6152/5345/4401 2382/5340/4396 +f 6152/5345/4401 7757/5346/4402 6153/5342/4398 +f 2377/5347/4403 2378/5341/4397 2383/5348/4404 +f 2379/5349/4405 2383/5348/4404 6137/5305/4365 +f 2378/5341/4397 6154/5343/4399 6155/5350/4406 +f 2383/5348/4404 6155/5350/4406 7758/5306/4366 +f 2379/5349/4405 2384/5351/4407 2380/5352/4408 +f 2384/5351/4407 6156/5353/4409 6157/5354/4410 +f 6136/5304/4364 6135/5320/4376 2384/5351/4407 +f 6135/5320/4376 7787/5322/4378 6156/5353/4409 +f 2377/5347/4403 2380/5352/4408 2385/5355/4411 +f 2381/5339/4395 2385/5355/4411 6150/5356/4412 +f 2380/5352/4408 6157/5354/4410 6158/5357/4413 +f 2385/5355/4411 6158/5357/4413 7791/5358/4414 +f 2390/5359/4415 2391/5360/4416 2387/5361/4417 +f 2391/5360/4416 6149/5337/4393 6148/5334/4390 +f 6160/5362/4418 6161/5363/4419 2391/5360/4416 +f 6161/5363/4419 7788/5338/4394 6149/5337/4393 +f 2386/5364/4420 2387/5361/4417 2392/5365/4421 +f 2388/5366/4422 2392/5365/4421 6155/5350/4406 +f 2387/5361/4417 6148/5334/4390 6147/5333/4389 +f 2392/5365/4421 6147/5333/4389 7758/5306/4366 +f 2388/5366/4422 2393/5367/4423 2389/5368/4424 +f 2393/5367/4423 6162/5369/4425 6163/5370/4426 +f 6154/5343/4399 6153/5342/4398 2393/5367/4423 +f 6153/5342/4398 7757/5346/4402 6162/5369/4425 +f 2386/5364/4420 2389/5368/4424 2394/5371/4427 +f 2390/5359/4415 2394/5371/4427 6159/5372/4428 +f 2389/5368/4424 6163/5370/4426 6164/5373/4429 +f 2394/5371/4427 6164/5373/4429 7792/5374/4430 +f 2395/5375/4431 2399/5376/4432 2400/5377/4433 +f 2396/5378/4434 2400/5377/4433 4823/5379/1001 +f 2399/5376/4432 6166/5380/4435 6167/5381/4436 +f 2400/5377/4433 6167/5381/4436 7540/5382/1002 +f 2396/5378/4434 2401/5383/4437 2397/5384/4438 +f 2401/5383/4437 6168/5385/4439 6169/5386/4440 +f 4822/5387/1000 4821/5388/1017 2401/5383/4437 +f 4821/5388/1017 7542/5389/1019 6168/5385/4439 +f 2395/5375/4431 2397/5384/4438 2402/5390/4441 +f 2398/5391/4442 2402/5390/4441 6152/5392/4401 +f 2397/5384/4438 6169/5386/4440 6170/5393/4443 +f 2402/5390/4441 6170/5393/4443 7757/5394/4402 +f 2398/5391/4442 2403/5395/4444 2399/5376/4432 +f 2403/5395/4444 6165/5396/4445 6166/5380/4435 +f 6151/5397/4400 6150/5398/4412 2403/5395/4444 +f 6150/5398/4412 7791/5399/4414 6165/5396/4445 +f 2404/5400/4446 2408/5401/4447 2409/5402/4448 +f 2405/5403/4449 2409/5402/4448 6170/5404/4443 +f 2408/5401/4447 6163/5405/4426 6162/5406/4425 +f 2409/5402/4448 6162/5406/4425 7757/5407/4402 +f 2405/5403/4449 2410/5408/4450 2406/5409/4451 +f 2410/5408/4450 4841/1273/1039 4840/1268/1034 +f 6169/5410/4440 6168/5411/4439 2410/5408/4450 +f 6168/5411/4439 7542/1253/1019 4841/1273/1039 +f 2404/5400/4446 2406/5409/4451 2411/5412/4452 +f 2407/5413/4453 2411/5412/4452 6171/5414/4454 +f 2406/5409/4451 4840/1268/1034 4839/1267/1033 +f 2411/5412/4452 4839/1267/1033 7541/1271/1037 +f 2407/5413/4453 2412/5415/4455 2408/5401/4447 +f 2412/5415/4455 6164/5416/4429 6163/5405/4426 +f 6172/5417/4456 6173/5418/4457 2412/5415/4455 +f 6173/5418/4457 7792/5419/4430 6164/5416/4429 +f 2413/5420/4458 2417/5421/4459 2418/5422/4460 +f 2414/5423/4461 2418/5422/4460 4844/5424/1046 +f 2417/5421/4459 6175/5425/4462 6176/5426/4463 +f 2418/5422/4460 6176/5426/4463 7538/5427/1047 +f 2414/5423/4461 2419/5428/4464 2415/5429/4465 +f 2419/5428/4464 6167/5381/4436 6166/5380/4435 +f 4843/5430/1045 4842/5431/1059 2419/5428/4464 +f 4842/5431/1059 7540/5382/1002 6167/5381/4436 +f 2413/5420/4458 2415/5429/4465 2420/5432/4466 +f 2416/5433/4467 2420/5432/4466 6177/5434/4468 +f 2415/5429/4465 6166/5380/4435 6165/5396/4445 +f 2420/5432/4466 6165/5396/4445 7791/5399/4414 +f 2416/5433/4467 2421/5435/4469 2417/5421/4459 +f 2421/5435/4469 6174/5436/4470 6175/5425/4462 +f 6178/5437/4471 6179/5438/4472 2421/5435/4469 +f 6179/5438/4472 7785/5439/4473 6174/5436/4470 +f 2422/5440/4474 2426/5441/4475 2427/5442/4476 +f 2423/5443/4477 2427/5442/4476 6173/5444/4457 +f 2426/5441/4475 6181/5445/4478 6182/5446/4479 +f 2427/5442/4476 6182/5446/4479 7792/5447/4430 +f 2423/5443/4477 2428/5448/4480 2424/5449/4481 +f 2428/5448/4480 4859/5450/1079 4858/5451/1074 +f 6172/5452/4456 6171/5453/4454 2428/5448/4480 +f 6171/5453/4454 7541/5454/1037 4859/5450/1079 +f 2422/5440/4474 2424/5449/4481 2429/5455/4482 +f 2425/5456/4483 2429/5455/4482 6183/5457/4484 +f 2424/5449/4481 4858/5451/1074 4857/5458/1073 +f 2429/5455/4482 4857/5458/1073 7539/5459/1077 +f 2425/5456/4483 2430/5460/4485 2426/5441/4475 +f 2430/5460/4485 6180/5461/4486 6181/5445/4478 +f 6184/5462/4487 6185/5463/4488 2430/5460/4485 +f 6185/5463/4488 7786/5464/4489 6180/5461/4486 +f 2431/5465/4490 2435/5466/4491 2436/5467/4492 +f 2432/5468/4493 2436/5467/4492 4862/5469/1086 +f 2435/5466/4491 6187/5470/4494 6188/5471/4495 +f 2436/5467/4492 6188/5471/4495 7536/5472/1087 +f 2432/5468/4493 2437/5473/4496 2433/5474/4497 +f 2437/5473/4496 6176/5426/4463 6175/5425/4462 +f 4861/5475/1085 4860/5476/1099 2437/5473/4496 +f 4860/5476/1099 7538/5427/1047 6176/5426/4463 +f 2431/5465/4490 2433/5474/4497 2438/5477/4498 +f 2434/5478/4499 2438/5477/4498 6189/5479/4500 +f 2433/5474/4497 6175/5425/4462 6174/5436/4470 +f 2438/5477/4498 6174/5436/4470 7785/5439/4473 +f 2434/5478/4499 2439/5480/4501 2435/5466/4491 +f 2439/5480/4501 6186/5481/4502 6187/5470/4494 +f 6190/5482/4503 6191/5483/4504 2439/5480/4501 +f 6191/5483/4504 7781/5484/4505 6186/5481/4502 +f 2440/5485/4506 2444/5486/4507 2445/5487/4508 +f 2441/5488/4509 2445/5487/4508 6185/5489/4488 +f 2444/5486/4507 6193/5490/4510 6194/5491/4511 +f 2445/5487/4508 6194/5491/4511 7786/5492/4489 +f 2441/5488/4509 2446/5493/4512 2442/5494/4513 +f 2446/5493/4512 4877/5495/1119 4876/5496/1114 +f 6184/5497/4487 6183/5498/4484 2446/5493/4512 +f 6183/5498/4484 7539/5499/1077 4877/5495/1119 +f 2440/5485/4506 2442/5494/4513 2447/5500/4514 +f 2443/5501/4515 2447/5500/4514 6195/5502/4516 +f 2442/5494/4513 4876/5496/1114 4875/5503/1113 +f 2447/5500/4514 4875/5503/1113 7537/5504/1116 +f 2443/5501/4515 2448/5505/4517 2444/5486/4507 +f 2448/5505/4517 6192/5506/4518 6193/5490/4510 +f 6196/5507/4519 6197/5508/4520 2448/5505/4517 +f 6197/5508/4520 7782/5509/4521 6192/5506/4518 +f 2449/5510/4522 2453/5511/4523 2454/5512/4524 +f 2450/5513/4525 2454/5512/4524 4880/5514/1126 +f 2453/5511/4523 6199/5515/4526 6200/5516/4527 +f 2454/5512/4524 6200/5516/4527 7534/5517/1127 +f 2450/5513/4525 2455/5518/4528 2451/5519/4529 +f 2455/5518/4528 6188/5520/4495 6187/5521/4494 +f 4879/5522/1125 4878/5523/1139 2455/5518/4528 +f 4878/5523/1139 7536/5524/1087 6188/5520/4495 +f 2449/5510/4522 2451/5519/4529 2456/5525/4530 +f 2452/5526/4531 2456/5525/4530 6201/5527/4532 +f 2451/5519/4529 6187/5521/4494 6186/5528/4502 +f 2456/5525/4530 6186/5528/4502 7781/5529/4505 +f 2452/5526/4531 2457/5530/4533 2453/5511/4523 +f 2457/5530/4533 6198/5531/4534 6199/5515/4526 +f 6202/5532/4535 6203/5533/4536 2457/5530/4533 +f 6203/5533/4536 7783/5534/4537 6198/5531/4534 +f 2458/5535/4538 2462/5536/4539 2463/5537/4540 +f 2459/5538/4541 2463/5537/4540 6197/5539/4520 +f 2462/5536/4539 6205/5540/4542 6206/5541/4543 +f 2463/5537/4540 6206/5541/4543 7782/5542/4521 +f 2459/5538/4541 2464/5543/4544 2460/5544/4545 +f 2464/5543/4544 4895/5545/1159 4894/5546/1154 +f 6196/5547/4519 6195/5548/4516 2464/5543/4544 +f 6195/5548/4516 7537/5549/1116 4895/5545/1159 +f 2458/5535/4538 2460/5544/4545 2465/5550/4546 +f 2461/5551/4547 2465/5550/4546 6207/5552/4548 +f 2460/5544/4545 4894/5546/1154 4893/5553/1153 +f 2465/5550/4546 4893/5553/1153 7535/1443/1156 +f 2461/5551/4547 2466/5554/4549 2462/5536/4539 +f 2466/5554/4549 6204/5555/4550 6205/5540/4542 +f 6208/5556/4551 6209/5557/4552 2466/5554/4549 +f 6209/5557/4552 7784/5558/4553 6204/5555/4550 +f 2471/5559/4554 2472/5560/4555 2468/5561/4556 +f 2472/5560/4555 6210/5562/4557 6211/5563/4558 +f 6202/5564/4535 6201/5565/4532 2472/5560/4555 +f 6201/5565/4532 7781/5566/4505 6210/5562/4557 +f 2468/5561/4556 2473/5567/4559 2469/5568/4560 +f 2473/5567/4559 6143/5321/4377 6142/5315/4374 +f 6211/5563/4558 6212/5569/4561 2473/5567/4559 +f 6212/5569/4561 7787/5322/4378 6143/5321/4377 +f 2467/5570/4562 2469/5568/4560 2474/5571/4563 +f 2470/5572/4564 2474/5571/4563 6213/5573/4565 +f 2469/5568/4560 6142/5315/4374 6141/5314/4373 +f 2474/5571/4563 6141/5314/4373 7789/5318/4338 +f 2467/5570/4562 2470/5572/4564 2475/5574/4566 +f 2471/5559/4554 2475/5574/4566 6203/5575/4536 +f 2470/5572/4564 6214/5576/4567 6215/5577/4568 +f 2475/5574/4566 6215/5577/4568 7783/5578/4537 +f 2476/5579/4569 2480/5580/4570 2481/5581/4571 +f 2477/5582/4572 2481/5581/4571 6146/5327/4383 +f 2480/5580/4570 6217/5583/4573 6218/5584/4574 +f 2481/5581/4571 6218/5584/4574 7790/5298/4358 +f 2477/5582/4572 2482/5585/4575 2478/5586/4576 +f 2482/5585/4575 6219/5587/4577 6220/5588/4578 +f 6145/5326/4382 6144/5336/4392 2482/5585/4575 +f 6144/5336/4392 7788/5338/4394 6219/5587/4577 +f 2478/5586/4576 2483/5589/4579 2479/5590/4580 +f 2483/5589/4579 6206/5591/4543 6205/5592/4542 +f 6220/5588/4578 6221/5593/4581 2483/5589/4579 +f 6221/5593/4581 7782/5509/4521 6206/5591/4543 +f 2476/5579/4569 2479/5590/4580 2484/5594/4582 +f 2480/5580/4570 2484/5594/4582 6216/5595/4583 +f 2479/5590/4580 6205/5592/4542 6204/5596/4550 +f 2484/5594/4582 6204/5596/4550 7784/5597/4553 +f 2485/5598/4584 2489/5599/4585 2490/5600/4586 +f 2486/5601/4587 2490/5600/4586 6191/5602/4504 +f 2489/5599/4585 6211/5563/4558 6210/5562/4557 +f 2490/5600/4586 6210/5562/4557 7781/5566/4505 +f 2485/5598/4584 2486/5601/4587 2491/5603/4588 +f 2487/5604/4589 2491/5603/4588 6179/5605/4472 +f 6190/5606/4503 6189/5607/4500 2491/5603/4588 +f 2491/5603/4588 6189/5607/4500 7785/5608/4473 +f 2487/5604/4589 2492/5609/4590 2488/5610/4591 +f 2492/5609/4590 6158/5357/4413 6157/5354/4410 +f 6178/5611/4471 6177/5612/4468 2492/5609/4590 +f 6177/5612/4468 7791/5358/4414 6158/5357/4413 +f 2485/5598/4584 2488/5610/4591 2493/5613/4592 +f 2489/5599/4585 2493/5613/4592 6212/5569/4561 +f 2488/5610/4591 6157/5354/4410 6156/5353/4409 +f 2493/5613/4592 6156/5353/4409 7787/5322/4378 +f 2498/5614/4593 2499/5615/4594 2495/5616/4595 +f 2499/5615/4594 6182/5617/4479 6181/5618/4478 +f 6160/5362/4418 6159/5372/4428 2499/5615/4594 +f 6159/5372/4428 7792/5374/4430 6182/5617/4479 +f 2494/5619/4596 2495/5616/4595 2500/5620/4597 +f 2500/5620/4597 6194/5491/4511 6193/5490/4510 +f 2495/5616/4595 6181/5618/4478 6180/5621/4486 +f 2500/5620/4597 6180/5621/4486 7786/5492/4489 +f 2494/5619/4596 2496/5622/4598 2501/5623/4599 +f 2497/5624/4600 2501/5623/4599 6221/5593/4581 +f 2496/5622/4598 6193/5490/4510 6192/5506/4518 +f 2501/5623/4599 6192/5506/4518 7782/5509/4521 +f 2494/5619/4596 2497/5624/4600 2502/5625/4601 +f 2498/5614/4593 2502/5625/4601 6161/5363/4419 +f 2497/5624/4600 6220/5588/4578 6219/5587/4577 +f 2502/5625/4601 6219/5587/4577 7788/5338/4394 +f 2507/5626/4602 2508/5627/4603 2504/5628/4604 +f 2504/5628/4604 2508/5627/4603 6225/5629/4605 +f 6223/5630/4606 6224/5631/4607 2508/5627/4603 +f 2508/5627/4603 6224/5631/4607 7777/5632/4608 +f 2504/5628/4604 2509/5633/4609 2505/5634/4610 +f 2509/5633/4609 6215/5635/4568 6214/5636/4567 +f 6226/5637/4611 6227/5638/4612 2509/5633/4609 +f 6227/5638/4612 7783/5639/4537 6215/5635/4568 +f 2505/5634/4610 2510/5640/4613 2506/5641/4614 +f 2510/5640/4613 6125/5272/4337 6124/5266/4331 +f 6214/5636/4567 6213/5642/4565 2510/5640/4613 +f 6213/5642/4565 7789/5273/4338 6125/5272/4337 +f 2503/5643/4615 2506/5641/4614 2511/5644/4616 +f 2507/5626/4602 2511/5644/4616 6222/5645/4617 +f 2506/5641/4614 6124/5266/4331 6123/5265/4330 +f 2511/5644/4616 6123/5265/4330 7779/5269/4334 +f 2516/5646/4618 2517/5647/4619 2513/5648/4620 +f 2517/5647/4619 6218/5584/4574 6217/5583/4573 +f 6127/5279/4344 6126/5296/4356 2517/5647/4619 +f 6126/5296/4356 7790/5298/4358 6218/5584/4574 +f 2513/5648/4620 2518/5649/4621 2514/5650/4622 +f 2518/5649/4621 6228/5651/4623 6229/5652/4624 +f 6217/5583/4573 6216/5595/4583 2518/5649/4621 +f 6216/5595/4583 7784/5597/4553 6228/5651/4623 +f 2514/5650/4622 2519/5653/4625 2515/5654/4626 +f 2519/5653/4625 6231/5655/4627 6232/5656/4628 +f 2514/5650/4622 6229/5652/4624 6230/5657/4629 +f 2519/5653/4625 6230/5657/4629 7778/5658/4630 +f 2512/5659/4631 2515/5654/4626 2520/5660/4632 +f 2516/5646/4618 2520/5660/4632 6128/5280/4345 +f 2515/5654/4626 6232/5656/4628 6233/5661/4633 +f 2520/5660/4632 6233/5661/4633 7780/5281/4346 +f 2521/5662/4634 2525/5663/4635 2526/5664/4636 +f 2522/5665/4637 2526/5664/4636 4898/5666/1166 +f 2525/5663/4635 6235/5667/4638 6236/5668/4639 +f 2526/5664/4636 6236/5668/4639 7532/5669/1167 +f 2522/5665/4637 2527/5670/4640 2523/5671/4641 +f 2527/5670/4640 6200/5516/4527 6199/5515/4526 +f 4897/5672/1165 4896/5673/1179 2527/5670/4640 +f 4896/5673/1179 7534/5517/1127 6200/5516/4527 +f 2521/5662/4634 2523/5671/4641 2528/5674/4642 +f 2524/5675/4643 2528/5674/4642 6227/5676/4612 +f 2523/5671/4641 6199/5515/4526 6198/5531/4534 +f 2528/5674/4642 6198/5531/4534 7783/5534/4537 +f 2524/5675/4643 2529/5677/4644 2525/5663/4635 +f 2529/5677/4644 6234/5678/4645 6235/5667/4638 +f 6226/5679/4611 6225/5680/4605 2529/5677/4644 +f 6225/5680/4605 7777/5681/4608 6234/5678/4645 +f 2530/5682/4646 2534/5683/4647 2535/5684/4648 +f 2531/5685/4649 2535/5684/4648 6209/5557/4552 +f 2534/5683/4647 6229/5686/4624 6228/5687/4623 +f 2535/5684/4648 6228/5687/4623 7784/5558/4553 +f 2531/5685/4649 2536/5688/4650 2532/5689/4651 +f 2536/5688/4650 4913/1442/1199 4912/1437/1195 +f 6208/5556/4551 6207/5552/4548 2536/5688/4650 +f 6207/5552/4548 7535/1443/1156 4913/1442/1199 +f 2530/5682/4646 2532/5689/4651 2537/5690/4652 +f 2533/5691/4653 2537/5690/4652 6237/5692/4654 +f 2532/5689/4651 4912/1437/1195 4911/1436/1194 +f 2537/5690/4652 4911/1436/1194 7533/1439/1197 +f 2533/5691/4653 2538/5693/4655 2534/5683/4647 +f 2538/5693/4655 6230/5694/4629 6229/5686/4624 +f 6238/5695/4656 6239/5696/4657 2538/5693/4655 +f 6239/5696/4657 7778/5697/4630 6230/5694/4629 +f 2539/5698/4658 2543/5699/4659 2544/5700/4660 +f 2544/5700/4660 6240/5701/4661 6241/5702/4662 +f 5569/5703/2841 5568/5704/2840 2544/5700/4660 +f 5568/5704/2840 7660/5705/2815 6240/5701/4661 +f 2540/5706/4663 2545/5707/4664 2541/5708/4665 +f 2545/5707/4664 6243/5709/4666 6244/5710/4667 +f 6241/5702/4662 6242/5711/4668 2545/5707/4664 +f 6242/5711/4668 7793/5712/4669 6243/5709/4666 +f 2539/5698/4658 2541/5708/4665 2546/5713/4670 +f 2542/5714/4671 2546/5713/4670 6246/5715/4672 +f 2541/5708/4665 6244/5710/4667 6245/5716/4673 +f 2546/5713/4670 6245/5716/4673 7795/5717/4674 +f 2542/5714/4671 2547/5718/4675 2543/5699/4659 +f 2547/5718/4675 5570/5719/2846 5569/5703/2841 +f 6247/5720/4676 6248/5721/4677 2547/5718/4675 +f 6248/5721/4677 7666/5722/2847 5570/5719/2846 +f 2548/5723/4678 2552/5724/4679 2553/5725/4680 +f 2549/5726/4681 2553/5725/4680 6252/5727/4682 +f 2552/5724/4679 6250/5728/4683 6251/5729/4684 +f 2553/5725/4680 6251/5729/4684 7796/5730/4685 +f 2549/5726/4681 2554/5731/4686 2550/5732/4687 +f 2554/5731/4686 6255/5733/4688 6256/5734/4689 +f 6253/5735/4690 6254/5736/4691 2554/5731/4686 +f 6254/5736/4691 7794/5737/4692 6255/5733/4688 +f 2548/5723/4678 2550/5732/4687 2555/5738/4693 +f 2555/5738/4693 5579/3397/2867 5578/3396/2866 +f 6256/5734/4689 6257/5739/4694 2555/5738/4693 +f 6257/5739/4694 7661/3357/2831 5579/3397/2867 +f 2551/5740/4695 2556/5741/4696 2552/5724/4679 +f 2556/5741/4696 6249/5742/4697 6250/5728/4683 +f 5578/3396/2866 5577/3391/2861 2556/5741/4696 +f 5577/3391/2861 7667/3393/2863 6249/5742/4697 +f 2561/5743/4698 2562/5744/4699 2558/5745/4700 +f 2562/5744/4699 6236/5668/4639 6235/5667/4638 +f 5557/5746/2813 5556/5747/2809 2562/5744/4699 +f 5556/5747/2809 7532/5669/1167 6236/5668/4639 +f 2557/5748/4701 2558/5745/4700 2563/5749/4702 +f 2559/5750/4703 2563/5749/4702 6258/5751/4704 +f 2558/5745/4700 6235/5667/4638 6234/5678/4645 +f 2563/5749/4702 6234/5678/4645 7777/5681/4608 +f 2559/5750/4703 2564/5752/4705 2560/5753/4706 +f 2564/5752/4705 6242/5711/4668 6241/5702/4662 +f 6259/5754/4707 6260/5755/4708 2564/5752/4705 +f 6260/5755/4708 7793/5712/4669 6242/5711/4668 +f 2557/5748/4701 2560/5753/4706 2565/5756/4709 +f 2561/5743/4698 2565/5756/4709 5558/5757/2814 +f 2560/5753/4706 6241/5702/4662 6240/5701/4661 +f 2565/5756/4709 6240/5701/4661 7660/5705/2815 +f 2570/5758/4710 2571/5759/4711 2567/5760/4712 +f 2571/5759/4711 6261/5761/4713 6262/5762/4714 +f 6256/5734/4689 6255/5733/4688 2571/5759/4711 +f 6255/5733/4688 7794/5737/4692 6261/5761/4713 +f 2566/5763/4715 2567/5760/4712 2572/5764/4716 +f 2568/5765/4717 2572/5764/4716 6239/5696/4657 +f 2567/5760/4712 6262/5762/4714 6263/5766/4718 +f 2572/5764/4716 6263/5766/4718 7778/5697/4630 +f 2568/5765/4717 2573/5767/4719 2569/5768/4720 +f 2573/5767/4719 5567/3361/2835 5566/3355/2829 +f 6238/5695/4656 6237/5692/4654 2573/5767/4719 +f 6237/5692/4654 7533/1439/1197 5567/3361/2835 +f 2566/5763/4715 2569/5768/4720 2574/5769/4721 +f 2570/5758/4710 2574/5769/4721 6257/5739/4694 +f 2569/5768/4720 5566/3355/2829 5565/3354/2828 +f 2574/5769/4721 5565/3354/2828 7661/3357/2831 +f 2579/5770/4722 2580/5771/4723 2576/5772/4724 +f 2580/5771/4723 5582/5773/2874 5581/5774/2873 +f 2579/5770/4722 6265/5775/4725 6266/5776/4726 +f 2580/5771/4723 6266/5776/4726 7530/5777/1207 +f 2575/5778/4727 2576/5772/4724 2581/5779/4728 +f 2577/5780/4729 2581/5779/4728 6248/5721/4677 +f 2576/5772/4724 5581/5774/2873 5580/5781/2880 +f 2581/5779/4728 5580/5781/2880 7666/5722/2847 +f 2577/5780/4729 2582/5782/4730 2578/5783/4731 +f 2578/5783/4731 2582/5782/4730 6267/5784/4732 +f 6247/5720/4676 6246/5715/4672 2582/5782/4730 +f 6246/5715/4672 7795/5717/4674 6267/5784/4732 +f 2578/5783/4731 2583/5785/4733 2579/5770/4722 +f 2583/5785/4733 6264/5786/4734 6265/5775/4725 +f 6268/5787/4735 6269/5788/4736 2583/5785/4733 +f 6269/5788/4736 7797/5789/4737 6264/5786/4734 +f 2588/5790/4738 2589/5791/4739 2585/5792/4740 +f 2589/5791/4739 6251/5729/4684 6250/5728/4683 +f 2588/5790/4738 6271/5793/4741 6272/5794/4742 +f 6272/5794/4742 7796/5730/4685 6251/5729/4684 +f 2584/5795/4743 2585/5792/4740 2590/5796/4744 +f 2586/5797/4745 2590/5796/4744 5585/3439/2892 +f 2585/5792/4740 6250/5728/4683 6249/5742/4697 +f 2590/5796/4744 6249/5742/4697 7667/3393/2863 +f 2586/5797/4745 2591/5798/4746 2587/5799/4747 +f 2587/5799/4747 2591/5798/4746 6273/5800/4748 +f 5584/3438/2891 5583/3434/2889 2591/5798/4746 +f 2591/5798/4746 5583/3434/2889 7531/3436/1244 +f 2587/5799/4747 2592/5801/4749 2588/5790/4738 +f 2592/5801/4749 6270/5802/4750 6271/5793/4741 +f 6274/5803/4751 6275/5804/4752 2592/5801/4749 +f 6275/5804/4752 7798/5805/4753 6270/5802/4750 +f 2597/5806/4754 2598/5807/4755 2594/5808/4756 +f 2598/5807/4755 6266/5809/4726 6265/5810/4725 +f 4918/1456/1212 4917/1448/1204 2598/5807/4755 +f 4917/1448/1204 7530/1451/1207 6266/5809/4726 +f 2593/5811/4757 2594/5808/4756 2599/5812/4758 +f 2599/5812/4758 6276/5813/4759 6277/5814/4760 +f 2594/5808/4756 6265/5810/4725 6264/5815/4734 +f 2599/5812/4758 6264/5815/4734 7797/5816/4737 +f 2595/5817/4761 2600/5818/4762 2596/5819/4763 +f 2600/5818/4762 6104/5224/4293 6103/5218/4289 +f 2595/5817/4761 6277/5814/4760 6278/5820/4764 +f 2600/5818/4762 6278/5820/4764 7751/5225/4294 +f 2593/5811/4757 2596/5819/4763 2601/5821/4765 +f 2597/5806/4754 2601/5821/4765 4919/1457/1213 +f 2596/5819/4763 6103/5218/4289 6102/5217/4288 +f 2601/5821/4765 6102/5217/4288 7543/1458/1214 +f 2606/5822/4766 2607/5823/4767 2603/5824/4768 +f 2603/5824/4768 2607/5823/4767 6279/5825/4769 +f 6112/5241/4308 6111/5233/4302 2607/5823/4767 +f 2607/5823/4767 6111/5233/4302 7752/5236/4305 +f 2602/5826/4770 2603/5824/4768 2608/5827/4771 +f 2604/5828/4772 2608/5827/4771 6275/5829/4752 +f 6280/5830/4773 6281/5831/4774 2608/5827/4771 +f 2608/5827/4771 6281/5831/4774 7798/5832/4753 +f 2604/5828/4772 2609/5833/4775 2605/5834/4776 +f 2609/5833/4775 4934/5835/1243 4933/5836/1236 +f 6274/5837/4751 6273/5838/4748 2609/5833/4775 +f 6273/5838/4748 7531/5839/1244 4934/5835/1243 +f 2602/5826/4770 2605/5834/4776 2610/5840/4777 +f 2606/5822/4766 2610/5840/4777 6113/5242/4309 +f 2605/5834/4776 4933/5836/1236 4932/5841/1235 +f 2610/5840/4777 4932/5841/1235 7544/5243/1239 +f 2615/5842/4778 2616/5843/4779 2612/5844/4780 +f 2616/5843/4779 6285/5845/4781 6286/5846/4782 +f 6283/5847/4783 6284/5848/4784 2616/5843/4779 +f 6284/5848/4784 7528/5849/4785 6285/5845/4781 +f 2611/5850/4786 2612/5844/4780 2617/5851/4787 +f 2613/5852/4788 2617/5851/4787 6288/5853/4789 +f 2612/5844/4780 6286/5846/4782 6287/5854/4790 +f 2617/5851/4787 6287/5854/4790 7775/5855/4791 +f 2613/5852/4788 2618/5856/4792 2614/5857/4793 +f 2618/5856/4792 6291/5858/4794 6292/5859/4795 +f 6289/5860/4796 6290/5861/4797 2618/5856/4792 +f 6290/5861/4797 7803/5862/4798 6291/5858/4794 +f 2611/5850/4786 2614/5857/4793 2619/5863/4799 +f 2615/5842/4778 2619/5863/4799 6282/5864/4800 +f 2614/5857/4793 6292/5859/4795 6293/5865/4801 +f 2619/5863/4799 6293/5865/4801 7755/5866/4802 +f 2624/5867/4803 2625/5868/4804 2621/5869/4805 +f 2625/5868/4804 6297/5870/4806 6298/5871/4807 +f 6295/5872/4808 6296/5873/4809 2625/5868/4804 +f 6296/5873/4809 7804/5874/4810 6297/5870/4806 +f 2620/5875/4811 2621/5869/4805 2626/5876/4812 +f 2622/5877/4813 2626/5876/4812 6300/5878/4814 +f 2621/5869/4805 6298/5871/4807 6299/5879/4815 +f 2626/5876/4812 6299/5879/4815 7776/5880/4816 +f 2622/5877/4813 2627/5881/4817 2623/5882/4818 +f 2627/5881/4817 6284/5883/4784 6283/5884/4783 +f 6301/5885/4819 6302/5886/4820 2627/5881/4817 +f 6302/5886/4820 7528/5887/4785 6284/5883/4784 +f 2620/5875/4811 2623/5882/4818 2628/5888/4821 +f 2624/5867/4803 2628/5888/4821 6294/5889/4822 +f 2623/5882/4818 6283/5884/4783 6282/5890/4800 +f 2628/5888/4821 6282/5890/4800 7755/5891/4802 +f 2633/5892/4823 2634/5893/4824 2630/5894/4825 +f 2634/5893/4824 6293/5865/4801 6292/5859/4795 +f 6304/5895/4826 6305/5896/4827 2634/5893/4824 +f 6305/5896/4827 7755/5866/4802 6293/5865/4801 +f 2629/5897/4828 2630/5894/4825 2635/5898/4829 +f 2635/5898/4829 6306/5899/4830 6307/5900/4831 +f 2630/5894/4825 6292/5859/4795 6291/5858/4794 +f 2635/5898/4829 6291/5858/4794 7803/5862/4798 +f 2631/5901/4832 2636/5902/4833 2632/5903/4834 +f 2636/5902/4833 6309/5904/4835 6310/5905/4836 +f 2631/5901/4832 6307/5900/4831 6308/5906/4837 +f 2636/5902/4833 6308/5906/4837 7801/5907/4838 +f 2629/5897/4828 2632/5903/4834 2637/5908/4839 +f 2633/5892/4823 2637/5908/4839 6303/5909/4840 +f 2632/5903/4834 6310/5905/4836 6311/5910/4841 +f 2637/5908/4839 6311/5910/4841 7529/5911/4842 +f 2642/5912/4843 2643/5913/4844 2639/5914/4845 +f 2639/5914/4845 2643/5913/4844 6315/5915/4846 +f 6313/5916/4847 6314/5917/4848 2643/5913/4844 +f 2643/5913/4844 6314/5917/4848 7802/5918/4849 +f 2638/5919/4850 2639/5914/4845 2644/5920/4851 +f 2640/5921/4852 2644/5920/4851 6296/5873/4809 +f 6316/5922/4853 6317/5923/4854 2644/5920/4851 +f 2644/5920/4851 6317/5923/4854 7804/5874/4810 +f 2640/5921/4852 2645/5924/4855 2641/5925/4856 +f 2645/5924/4855 6305/5926/4827 6304/5927/4826 +f 6295/5872/4808 6294/5889/4822 2645/5924/4855 +f 6294/5889/4822 7755/5891/4802 6305/5926/4827 +f 2638/5919/4850 2641/5925/4856 2646/5928/4857 +f 2642/5912/4843 2646/5928/4857 6312/5929/4858 +f 2641/5925/4856 6304/5927/4826 6303/5930/4840 +f 2646/5928/4857 6303/5930/4840 7529/5931/4842 +f 2647/5932/4859 2651/5933/4860 2652/5934/4861 +f 2652/5934/4861 6311/5910/4841 6310/5905/4836 +f 6319/5935/4862 6320/5936/4863 2652/5934/4861 +f 6320/5936/4863 7529/5911/4842 6311/5910/4841 +f 2648/5937/4864 2653/5938/4865 2649/5939/4866 +f 2653/5938/4865 6321/5940/4867 6322/5941/4868 +f 2648/5937/4864 6310/5905/4836 6309/5904/4835 +f 6309/5904/4835 7801/5907/4838 6321/5940/4867 +f 2647/5932/4859 2649/5939/4866 2654/5942/4869 +f 2650/5943/4870 2654/5942/4869 6324/5944/4871 +f 2649/5939/4866 6322/5941/4868 6323/5945/4872 +f 6323/5945/4872 7799/5946/4873 6324/5944/4871 +f 2650/5943/4870 2655/5947/4874 2651/5933/4860 +f 2655/5947/4874 6318/5948/4875 6319/5935/4862 +f 6325/5949/4876 6326/5950/4877 2655/5947/4874 +f 6326/5950/4877 7756/5951/4878 6318/5948/4875 +f 2656/5952/4879 2660/5953/4880 2661/5954/4881 +f 2657/5955/4882 2661/5954/4881 6330/5956/4883 +f 2660/5953/4880 6328/5957/4884 6329/5958/4885 +f 6329/5958/4885 7800/5959/4886 6330/5956/4883 +f 2657/5955/4882 2662/5960/4887 2658/5961/4888 +f 2658/5961/4888 2662/5960/4887 6314/5962/4848 +f 6331/5963/4889 6332/5964/4890 2662/5960/4887 +f 6332/5964/4890 7802/5965/4849 6314/5962/4848 +f 2656/5952/4879 2658/5961/4888 2663/5966/4891 +f 2663/5966/4891 6320/5967/4863 6319/5968/4862 +f 6313/5969/4847 6312/5970/4858 2663/5966/4891 +f 6312/5970/4858 7529/5971/4842 6320/5967/4863 +f 2659/5972/4892 2664/5973/4893 2660/5953/4880 +f 2664/5973/4893 6327/5974/4894 6328/5957/4884 +f 6319/5968/4862 6318/5975/4875 2664/5973/4893 +f 6318/5975/4875 7756/5976/4878 6327/5974/4894 +f 2665/5977/4895 2669/5978/4896 2670/5979/4897 +f 2666/5980/4898 2670/5979/4897 6326/5950/4877 +f 2669/5978/4896 6334/5981/4899 6335/5982/4900 +f 2670/5979/4897 6335/5982/4900 7756/5951/4878 +f 2666/5980/4898 2671/5983/4901 2667/5984/4902 +f 2671/5983/4901 6336/5985/4903 6337/5986/4904 +f 6325/5949/4876 6324/5944/4871 2671/5983/4901 +f 2671/5983/4901 6324/5944/4871 7799/5946/4873 +f 2665/5977/4895 2667/5984/4902 2672/5987/4905 +f 2668/5988/4906 2672/5987/4905 6122/5268/4333 +f 2667/5984/4902 6337/5986/4904 6338/5989/4907 +f 2672/5987/4905 6338/5989/4907 7779/5269/4334 +f 2668/5988/4906 2673/5990/4908 2669/5978/4896 +f 2673/5990/4908 6333/5991/4909 6334/5981/4899 +f 6121/5267/4332 6120/5260/4325 2673/5990/4908 +f 6120/5260/4325 7760/5262/4327 6333/5991/4909 +f 2674/5992/4910 2678/5993/4911 2679/5994/4912 +f 2675/5995/4913 2679/5994/4912 6339/5996/4914 +f 2678/5993/4911 6130/5997/4343 6129/5998/4342 +f 2679/5994/4912 6129/5998/4342 7780/5999/4346 +f 2675/5995/4913 2680/6000/4915 2676/6001/4916 +f 2680/6000/4915 6329/5958/4885 6328/5957/4884 +f 6340/6002/4917 6341/6003/4918 2680/6000/4915 +f 2680/6000/4915 6341/6003/4918 7800/5959/4886 +f 2674/5992/4910 2676/6001/4916 2681/6004/4919 +f 2677/6005/4920 2681/6004/4919 6335/6006/4900 +f 2676/6001/4916 6328/5957/4884 6327/5974/4894 +f 2681/6004/4919 6327/5974/4894 7756/5976/4878 +f 2677/6005/4920 2682/6007/4921 2678/5993/4911 +f 2682/6007/4921 6131/6008/4350 6130/5997/4343 +f 6334/6009/4899 6333/6010/4909 2682/6007/4921 +f 6333/6010/4909 7760/6011/4327 6131/6008/4350 +f 2687/6012/4922 2688/6013/4923 2684/6014/4924 +f 2688/6013/4923 6224/5631/4607 6223/5630/4606 +f 6259/6015/4707 6258/6016/4704 2688/6013/4923 +f 6258/6016/4704 7777/5632/4608 6224/5631/4607 +f 2683/6017/4925 2684/6014/4924 2689/6018/4926 +f 2685/6019/4927 2689/6018/4926 6338/5989/4907 +f 2684/6014/4924 6223/5630/4606 6222/5645/4617 +f 6222/5645/4617 7779/5269/4334 6338/5989/4907 +f 2685/6019/4927 2690/6020/4928 2686/6021/4929 +f 2690/6020/4928 6342/6022/4930 6343/6023/4931 +f 6337/5986/4904 6336/5985/4903 2690/6020/4928 +f 6336/5985/4903 7799/5946/4873 6342/6022/4930 +f 2683/6017/4925 2686/6021/4929 2691/6024/4932 +f 2687/6012/4922 2691/6024/4932 6260/6025/4708 +f 2686/6021/4929 6343/6023/4931 6344/6026/4933 +f 2691/6024/4932 6344/6026/4933 7793/6027/4669 +f 2696/6028/4934 2697/6029/4935 2693/6030/4936 +f 2697/6029/4935 6341/6031/4918 6340/6032/4917 +f 6346/6033/4937 6347/6034/4938 2697/6029/4935 +f 6347/6034/4938 7800/6035/4886 6341/6031/4918 +f 2692/6036/4939 2693/6030/4936 2698/6037/4940 +f 2694/6038/4941 2698/6037/4940 6233/6039/4633 +f 2693/6030/4936 6340/6032/4917 6339/6040/4914 +f 6339/6040/4914 7780/6041/4346 6233/6039/4633 +f 2694/6038/4941 2699/6042/4942 2695/6043/4943 +f 2699/6042/4942 6263/5766/4718 6262/5762/4714 +f 6232/6044/4628 6231/6045/4627 2699/6042/4942 +f 6231/6045/4627 7778/5697/4630 6263/5766/4718 +f 2692/6036/4939 2695/6043/4943 2700/6046/4944 +f 2696/6028/4934 2700/6046/4944 6345/6047/4945 +f 2695/6043/4943 6262/5762/4714 6261/5761/4713 +f 2700/6046/4944 6261/5761/4713 7794/5737/4692 +f 2701/6048/4946 2705/6049/4947 2706/6050/4948 +f 2702/6051/4949 2706/6050/4948 6095/5188/4265 +f 2705/6049/4947 6349/6052/4950 6350/6053/4951 +f 2706/6050/4948 6350/6053/4951 7747/5189/4231 +f 2702/6051/4949 2707/6054/4952 2703/6055/4953 +f 2707/6054/4952 6351/6056/4954 6352/6057/4955 +f 6094/5187/4264 6093/5182/4260 2707/6054/4952 +f 6093/5182/4260 7749/5184/4262 6351/6056/4954 +f 2701/6048/4946 2703/6055/4953 2708/6058/4956 +f 2704/6059/4957 2708/6058/4956 6354/6060/4958 +f 2703/6055/4953 6352/6057/4955 6353/6061/4959 +f 2708/6058/4956 6353/6061/4959 7769/6062/4960 +f 2704/6059/4957 2709/6063/4961 2705/6049/4947 +f 2705/6049/4947 2709/6063/4961 6348/6064/4962 +f 6355/6065/4963 6356/6066/4964 2709/6063/4961 +f 2709/6063/4961 6356/6066/4964 7761/6067/4965 +f 2710/6068/4966 2714/6069/4967 2715/6070/4968 +f 2711/6071/4969 2715/6070/4968 6360/6072/4970 +f 2714/6069/4967 6358/6073/4971 6359/6074/4972 +f 2715/6070/4968 6359/6074/4972 7770/6075/4973 +f 2711/6071/4969 2716/6076/4974 2712/6077/4975 +f 2716/6076/4974 6098/6078/4272 6097/6079/4271 +f 6361/6080/4976 6362/6081/4977 2716/6076/4974 +f 6362/6081/4977 7750/6082/4273 6098/6078/4272 +f 2710/6068/4966 2712/6077/4975 2717/6083/4978 +f 2713/6084/4979 2717/6083/4978 6363/6085/4980 +f 2712/6077/4975 6097/6079/4271 6096/6086/4281 +f 2717/6083/4978 6096/6086/4281 7748/6087/4241 +f 2713/6084/4979 2718/6088/4981 2714/6069/4967 +f 2718/6088/4981 6357/6089/4982 6358/6073/4971 +f 2713/6084/4979 6364/6090/4983 6365/6091/4984 +f 2718/6088/4981 6365/6091/4984 7762/6092/4985 +f 2723/6093/4986 2724/6094/4987 2720/6095/4988 +f 2724/6094/4987 6369/6096/4989 6370/6097/4990 +f 6367/6098/4991 6368/6099/4992 2724/6094/4987 +f 6368/6099/4992 7527/6100/4993 6369/6096/4989 +f 2719/6101/4994 2720/6095/4988 2725/6102/4995 +f 2721/6103/4996 2725/6102/4995 6372/6104/4997 +f 2720/6095/4988 6370/6097/4990 6371/6105/4998 +f 2725/6102/4995 6371/6105/4998 7767/6106/4999 +f 2721/6103/4996 2726/6107/5000 2722/6108/5001 +f 2726/6107/5000 6287/6109/4790 6286/6110/4782 +f 6373/6111/5002 6374/6112/5003 2726/6107/5000 +f 6374/6112/5003 7775/6113/4791 6287/6109/4790 +f 2719/6101/4994 2722/6108/5001 2727/6114/5004 +f 2723/6093/4986 2727/6114/5004 6366/6115/5005 +f 2722/6108/5001 6286/6110/4782 6285/6116/4781 +f 2727/6114/5004 6285/6116/4781 7528/5887/4785 +f 2732/6117/5006 2733/6118/5007 2729/6119/5008 +f 2733/6118/5007 6375/6120/5009 6376/6121/5010 +f 6301/5885/4819 6300/5878/4814 2733/6118/5007 +f 6300/5878/4814 7776/5880/4816 6375/6120/5009 +f 2728/6122/5011 2729/6119/5008 2734/6123/5012 +f 2730/6124/5013 2734/6123/5012 6378/6125/5014 +f 2729/6119/5008 6376/6121/5010 6377/6126/5015 +f 2734/6123/5012 6377/6126/5015 7768/6127/5016 +f 2730/6124/5013 2735/6128/5017 2731/6129/5018 +f 2735/6128/5017 6368/6099/4992 6367/6098/4991 +f 6379/6130/5019 6380/6131/5020 2735/6128/5017 +f 6380/6131/5020 7527/6100/4993 6368/6099/4992 +f 2728/6122/5011 2731/6129/5018 2736/6132/5021 +f 2732/6117/5006 2736/6132/5021 6302/5886/4820 +f 2731/6129/5018 6367/6098/4991 6366/6115/5005 +f 2736/6132/5021 6366/6115/5005 7528/5887/4785 +f 2741/6133/5022 2742/6134/5023 2738/6135/5024 +f 2742/6134/5023 6384/6136/5025 6385/6137/5026 +f 6382/6138/5027 6383/6139/5028 2742/6134/5023 +f 6383/6139/5028 7753/6140/5029 6384/6136/5025 +f 2737/6141/5030 2738/6135/5024 2743/6142/5031 +f 2739/6143/5032 2743/6142/5031 6387/6144/5033 +f 2738/6135/5024 6385/6137/5026 6386/6145/5034 +f 2743/6142/5031 6386/6145/5034 7809/6146/5035 +f 2739/6143/5032 2744/6147/5036 2740/6148/5037 +f 2744/6147/5036 6390/6149/5038 6391/6150/5039 +f 6388/6151/5040 6389/6152/5041 2744/6147/5036 +f 6389/6152/5041 7807/6153/5042 6390/6149/5038 +f 2737/6141/5030 2740/6148/5037 2745/6154/5043 +f 2741/6133/5022 2745/6154/5043 6381/6155/5044 +f 6391/6150/5039 6392/6156/5045 2745/6154/5043 +f 6392/6156/5045 7754/6157/5046 6381/6155/5044 +f 2750/6158/5047 2751/6159/5048 2747/6160/5049 +f 2751/6159/5048 6396/6161/5050 6397/6162/5051 +f 6394/6163/5052 6395/6164/5053 2751/6159/5048 +f 6395/6164/5053 7808/6165/5054 6396/6161/5050 +f 2746/6166/5055 2747/6160/5049 2752/6167/5056 +f 2748/6168/5057 2752/6167/5056 6399/6169/5058 +f 2747/6160/5049 6397/6162/5051 6398/6170/5059 +f 2752/6167/5056 6398/6170/5059 7810/6171/5060 +f 2748/6168/5057 2753/6172/5061 2749/6173/5062 +f 2753/6172/5061 6383/6174/5028 6382/6175/5027 +f 6400/6176/5063 6401/6177/5064 2753/6172/5061 +f 6401/6177/5064 7753/6178/5029 6383/6174/5028 +f 2746/6166/5055 2749/6173/5062 2754/6179/5065 +f 2754/6179/5065 6393/6180/5066 6394/6163/5052 +f 2749/6173/5062 6382/6175/5027 6381/6181/5044 +f 6381/6181/5044 7754/6182/5046 6393/6180/5066 +f 2755/6183/5067 2759/6184/5068 2760/6185/5069 +f 2756/6186/5070 2760/6185/5069 6392/6156/5045 +f 2759/6184/5068 6403/6187/5071 6404/6188/5072 +f 2760/6185/5069 6404/6188/5072 7754/6157/5046 +f 2756/6186/5070 2761/6189/5073 2757/6190/5074 +f 2757/6190/5074 2761/6189/5073 6405/6191/5075 +f 6391/6150/5039 6390/6149/5038 2761/6189/5073 +f 2761/6189/5073 6390/6149/5038 7807/6153/5042 +f 2755/6183/5067 2757/6190/5074 2762/6192/5076 +f 2758/6193/5077 2762/6192/5076 6408/6194/5078 +f 6406/6195/5079 6407/6196/5080 2762/6192/5076 +f 6407/6196/5080 7805/6197/5081 6408/6194/5078 +f 2758/6193/5077 2763/6198/5082 2759/6184/5068 +f 2763/6198/5082 6402/6199/5083 6403/6187/5071 +f 6409/6200/5084 6410/6201/5085 2763/6198/5082 +f 6410/6201/5085 7526/6202/5086 6402/6199/5083 +f 2764/6203/5087 2768/6204/5088 2769/6205/5089 +f 2769/6205/5089 6414/6206/5090 6415/6207/5091 +f 2768/6204/5088 6412/6208/5092 6413/6209/5093 +f 6413/6209/5093 7806/6210/5094 6414/6206/5090 +f 2765/6211/5095 2770/6212/5096 2766/6213/5097 +f 2770/6212/5096 6395/6214/5053 6394/6215/5052 +f 2765/6211/5095 6415/6207/5091 6416/6216/5098 +f 2770/6212/5096 6416/6216/5098 7808/6217/5054 +f 2764/6203/5087 2766/6213/5097 2771/6218/5099 +f 2767/6219/5100 2771/6218/5099 6404/6188/5072 +f 2766/6213/5097 6394/6215/5052 6393/6220/5066 +f 2771/6218/5099 6393/6220/5066 7754/6157/5046 +f 2767/6219/5100 2772/6221/5101 2768/6204/5088 +f 2772/6221/5101 6411/6222/5102 6412/6208/5092 +f 6403/6187/5071 6402/6199/5083 2772/6221/5101 +f 6402/6199/5083 7526/6202/5086 6411/6222/5102 +f 2773/6223/5103 2777/6224/5104 2778/6225/5105 +f 2774/6226/5106 2778/6225/5105 6410/6201/5085 +f 2777/6224/5104 6418/6227/5107 6419/6228/5108 +f 2778/6225/5105 6419/6228/5108 7526/6202/5086 +f 2774/6226/5106 2779/6229/5109 2775/6230/5110 +f 2775/6230/5110 2779/6229/5109 6420/6231/5111 +f 6409/6200/5084 6408/6194/5078 2779/6229/5109 +f 2779/6229/5109 6408/6194/5078 7805/6197/5081 +f 2773/6223/5103 2775/6230/5110 2780/6232/5112 +f 2780/6232/5112 6371/6233/4998 6370/6234/4990 +f 6421/6235/5113 6422/6236/5114 2780/6232/5112 +f 6422/6236/5114 7767/6237/4999 6371/6233/4998 +f 2776/6238/5115 2781/6239/5116 2777/6224/5104 +f 2781/6239/5116 6417/6240/5117 6418/6227/5107 +f 2776/6238/5115 6370/6234/4990 6369/6241/4989 +f 6369/6241/4989 7527/6242/4993 6417/6240/5117 +f 2782/6243/5118 2786/6244/5119 2787/6245/5120 +f 2787/6245/5120 6423/6246/5121 6424/6247/5122 +f 6379/6130/5019 6378/6125/5014 2787/6245/5120 +f 6378/6125/5014 7768/6127/5016 6423/6246/5121 +f 2783/6248/5123 2788/6249/5124 2784/6250/5125 +f 2788/6249/5124 6413/6251/5093 6412/6252/5092 +f 2783/6248/5123 6424/6247/5122 6425/6253/5126 +f 2788/6249/5124 6425/6253/5126 7806/6254/5094 +f 2782/6243/5118 2784/6250/5125 2789/6255/5127 +f 2785/6256/5128 2789/6255/5127 6419/6257/5108 +f 2784/6250/5125 6412/6252/5092 6411/6258/5102 +f 2789/6255/5127 6411/6258/5102 7526/6259/5086 +f 2785/6256/5128 2790/6260/5129 2786/6244/5119 +f 2786/6244/5119 2790/6260/5129 6380/6131/5020 +f 6418/6261/5107 6417/6262/5117 2790/6260/5129 +f 6417/6262/5117 7527/6100/4993 6380/6131/5020 +f 2791/6263/5130 2795/6264/5131 2796/6265/5132 +f 2792/6266/5133 2796/6265/5132 6071/5103/4201 +f 2795/6264/5131 6427/6267/5134 6428/6268/5135 +f 6428/6268/5135 7743/5065/4167 6071/5103/4201 +f 2791/6263/5130 2792/6266/5133 2797/6269/5136 +f 2793/6270/5137 2797/6269/5136 6429/6271/5138 +f 2792/6266/5133 6070/5098/4196 6069/5097/4195 +f 2797/6269/5136 6069/5097/4195 7745/5101/4199 +f 2791/6263/5130 2793/6270/5137 2798/6272/5139 +f 2794/6273/5140 2798/6272/5139 6432/6274/5141 +f 6430/6275/5142 6431/6276/5143 2798/6272/5139 +f 6431/6276/5143 7813/6277/5144 6432/6274/5141 +f 2794/6273/5140 2799/6278/5145 2795/6264/5131 +f 2799/6278/5145 6426/6279/5146 6427/6267/5134 +f 6433/6280/5147 6434/6281/5148 2799/6278/5145 +f 6434/6281/5148 7815/6282/5149 6426/6279/5146 +f 2800/6283/5150 2804/6284/5151 2805/6285/5152 +f 2805/6285/5152 6438/6286/5153 6439/6287/5154 +f 2804/6284/5151 6436/6288/5155 6437/6289/5156 +f 6437/6289/5156 7814/6290/5157 6438/6286/5153 +f 2800/6283/5150 2801/6291/5158 2806/6292/5159 +f 2802/6293/5160 2806/6292/5159 6074/5110/4208 +f 2801/6291/5158 6439/6287/5154 6440/6294/5161 +f 2806/6292/5159 6440/6294/5161 7746/5111/4209 +f 2800/6283/5150 2802/6293/5160 2807/6295/5162 +f 2803/6296/5163 2807/6295/5162 6441/6297/5164 +f 2802/6293/5160 6073/5109/4207 6072/5119/4217 +f 6072/5119/4217 7744/5075/4177 6441/6297/5164 +f 2803/6296/5163 2808/6298/5165 2804/6284/5151 +f 2808/6298/5165 6435/6299/5166 6436/6288/5155 +f 6442/6300/5167 6443/6301/5168 2808/6298/5165 +f 6443/6301/5168 7816/6302/5169 6435/6299/5166 +f 2809/6303/5170 2813/6304/5171 2814/6305/5172 +f 2810/6306/5173 2814/6305/5172 6434/6307/5148 +f 2813/6304/5171 6445/6308/5174 6446/6309/5175 +f 2814/6305/5172 6446/6309/5175 7815/6310/5149 +f 2810/6306/5173 2815/6311/5176 2811/6312/5177 +f 2815/6311/5176 6447/6313/5178 6448/6314/5179 +f 6433/6315/5147 6432/6316/5141 2815/6311/5176 +f 6432/6316/5141 7813/6317/5144 6447/6313/5178 +f 2809/6303/5170 2811/6312/5177 2816/6318/5180 +f 2812/6319/5181 2816/6318/5180 6450/6320/5182 +f 2811/6312/5177 6448/6314/5179 6449/6321/5183 +f 2816/6318/5180 6449/6321/5183 7819/6322/5184 +f 2812/6319/5181 2817/6323/5185 2813/6304/5171 +f 2817/6323/5185 6444/6324/5186 6445/6308/5174 +f 6451/6325/5187 6452/6326/5188 2817/6323/5185 +f 6452/6326/5188 7817/6327/5189 6444/6324/5186 +f 2818/6328/5190 2822/6329/5191 2823/6330/5192 +f 2819/6331/5193 2823/6330/5192 6456/6332/5194 +f 2822/6329/5191 6454/6333/5195 6455/6334/5196 +f 2823/6330/5192 6455/6334/5196 7820/6335/5197 +f 2819/6331/5193 2824/6336/5198 2820/6337/5199 +f 2824/6336/5198 6437/6289/5156 6436/6288/5155 +f 6457/6338/5200 6458/6339/5201 2824/6336/5198 +f 6458/6339/5201 7814/6290/5157 6437/6289/5156 +f 2818/6328/5190 2820/6337/5199 2825/6340/5202 +f 2821/6341/5203 2825/6340/5202 6459/6342/5204 +f 2820/6337/5199 6436/6288/5155 6435/6299/5166 +f 2825/6340/5202 6435/6299/5166 7816/6302/5169 +f 2821/6341/5203 2826/6343/5205 2822/6329/5191 +f 2826/6343/5205 6453/6344/5206 6454/6333/5195 +f 6460/6345/5207 6461/6346/5208 2826/6343/5205 +f 6461/6346/5208 7818/6347/5209 6453/6344/5206 +f 2827/6348/5210 2831/6349/5211 2832/6350/5212 +f 2828/6351/5213 2832/6350/5212 6452/6352/5188 +f 2831/6349/5211 6463/6353/5214 6464/6354/5215 +f 2832/6350/5212 6464/6354/5215 7817/6355/5189 +f 2828/6351/5213 2833/6356/5216 2829/6357/5217 +f 2833/6356/5216 6465/6358/5218 6466/6359/5219 +f 6451/6360/5187 6450/6361/5182 2833/6356/5216 +f 6450/6361/5182 7819/6362/5184 6465/6358/5218 +f 2827/6348/5210 2829/6357/5217 2834/6363/5220 +f 2830/6364/5221 2834/6363/5220 6468/6365/5222 +f 2829/6357/5217 6466/6359/5219 6467/6366/5223 +f 2834/6363/5220 6467/6366/5223 7821/6367/5224 +f 2830/6364/5221 2835/6368/5225 2831/6349/5211 +f 2835/6368/5225 6462/6369/5226 6463/6353/5214 +f 6469/6370/5227 6470/6371/5228 2835/6368/5225 +f 6470/6371/5228 7823/6372/5229 6462/6369/5226 +f 2836/6373/5230 2840/6374/5231 2841/6375/5232 +f 2837/6376/5233 2841/6375/5232 6474/6377/5234 +f 2840/6374/5231 6472/6378/5235 6473/6379/5236 +f 2841/6375/5232 6473/6379/5236 7822/6380/5237 +f 2837/6376/5233 2842/6381/5238 2838/6382/5239 +f 2842/6381/5238 6455/6334/5196 6454/6333/5195 +f 6475/6383/5240 6476/6384/5241 2842/6381/5238 +f 6476/6384/5241 7820/6335/5197 6455/6334/5196 +f 2836/6373/5230 2838/6382/5239 2843/6385/5242 +f 2839/6386/5243 2843/6385/5242 6477/6387/5244 +f 2838/6382/5239 6454/6333/5195 6453/6344/5206 +f 2843/6385/5242 6453/6344/5206 7818/6347/5209 +f 2839/6386/5243 2844/6388/5245 2840/6374/5231 +f 2844/6388/5245 6471/6389/5246 6472/6378/5235 +f 6478/6390/5247 6479/6391/5248 2844/6388/5245 +f 6479/6391/5248 7824/6392/5249 6471/6389/5246 +f 2849/6393/5250 2850/6394/5251 2846/6395/5252 +f 2846/6395/5252 2850/6394/5251 6470/6371/5228 +f 6481/6396/5253 6482/6397/5254 2850/6394/5251 +f 6482/6397/5254 7823/6372/5229 6470/6371/5228 +f 2846/6395/5252 2851/6398/5255 2847/6399/5256 +f 2847/6399/5256 2851/6398/5255 6483/6400/5257 +f 6469/6370/5227 6468/6365/5222 2851/6398/5255 +f 2851/6398/5255 6468/6365/5222 7821/6367/5224 +f 2847/6399/5256 2852/6401/5258 2848/6402/5259 +f 2852/6401/5258 6486/6403/5260 6487/6404/5261 +f 6484/6405/5262 6485/6406/5263 2852/6401/5258 +f 6485/6406/5263 7827/6407/5264 6486/6403/5260 +f 2845/6408/5265 2848/6402/5259 2853/6409/5266 +f 2849/6393/5250 2853/6409/5266 6480/6410/5267 +f 2848/6402/5259 6487/6404/5261 6488/6411/5268 +f 2853/6409/5266 6488/6411/5268 7825/6412/5269 +f 2858/6413/5270 2859/6414/5271 2855/6415/5272 +f 2859/6414/5271 6492/6416/5273 6493/6417/5274 +f 6490/6418/5275 6491/6419/5276 2859/6414/5271 +f 6491/6419/5276 7828/6420/5277 6492/6416/5273 +f 2855/6415/5272 2860/6421/5278 2856/6422/5279 +f 2860/6421/5278 6473/6379/5236 6472/6378/5235 +f 2855/6415/5272 6493/6417/5274 6494/6423/5280 +f 2860/6421/5278 6494/6423/5280 7822/6380/5237 +f 2856/6422/5279 2861/6424/5281 2857/6425/5282 +f 2861/6424/5281 6495/6426/5283 6496/6427/5284 +f 2856/6422/5279 6472/6378/5235 6471/6389/5246 +f 6471/6389/5246 7824/6392/5249 6495/6426/5283 +f 2854/6428/5285 2857/6425/5282 2862/6429/5286 +f 2858/6413/5270 2862/6429/5286 6489/6430/5287 +f 2857/6425/5282 6496/6427/5284 6497/6431/5288 +f 2862/6429/5286 6497/6431/5288 7826/6432/5289 +f 2867/6433/5290 2868/6434/5291 2864/6435/5292 +f 2864/6435/5292 2868/6434/5291 6501/6436/5293 +f 2867/6433/5290 6499/6437/5294 6500/6438/5295 +f 2868/6434/5291 6500/6438/5295 7765/6439/5296 +f 2863/6440/5297 2864/6435/5292 2869/6441/5298 +f 2865/6442/5299 2869/6441/5298 6504/6443/5300 +f 2864/6435/5292 6502/6444/5301 6503/6445/5302 +f 2869/6441/5298 6503/6445/5302 7829/6446/5303 +f 2865/6442/5299 2870/6447/5304 2866/6448/5305 +f 2870/6447/5304 6488/6411/5268 6487/6404/5261 +f 6505/6449/5306 6506/6450/5307 2870/6447/5304 +f 6506/6450/5307 7825/6412/5269 6488/6411/5268 +f 2863/6440/5297 2866/6448/5305 2871/6451/5308 +f 2871/6451/5308 6498/6452/5309 6499/6437/5294 +f 2866/6448/5305 6487/6404/5261 6486/6403/5260 +f 2871/6451/5308 6486/6403/5260 7827/6407/5264 +f 2876/6453/5310 2877/6454/5311 2873/6455/5312 +f 2877/6454/5311 6507/6456/5313 6508/6457/5314 +f 6490/6458/5275 6489/6459/5287 2877/6454/5311 +f 6489/6459/5287 7826/6460/5289 6507/6456/5313 +f 2872/6461/5315 2873/6455/5312 2878/6462/5316 +f 2874/6463/5317 2878/6462/5316 6510/6464/5318 +f 2873/6455/5312 6508/6457/5314 6509/6465/5319 +f 2878/6462/5316 6509/6465/5319 7830/6466/5320 +f 2874/6463/5317 2879/6467/5321 2875/6468/5322 +f 2875/6468/5322 2879/6467/5321 6513/6469/5323 +f 2874/6463/5317 6511/6470/5324 6512/6471/5325 +f 2879/6467/5321 6512/6471/5325 7766/6472/5326 +f 2872/6461/5315 2875/6468/5322 2880/6473/5327 +f 2876/6453/5310 2880/6473/5327 6491/6474/5276 +f 6514/6475/5328 6515/6476/5329 2880/6473/5327 +f 2880/6473/5327 6515/6476/5329 7828/6477/5277 +f 2881/6478/5330 2885/6479/5331 2886/6480/5332 +f 2882/6481/5333 2886/6480/5332 6422/6236/5114 +f 2885/6479/5331 6517/6482/5334 6518/6483/5335 +f 2886/6480/5332 6518/6483/5335 7767/6237/4999 +f 2882/6481/5333 2887/6484/5336 2883/6485/5337 +f 2887/6484/5336 6519/6486/5338 6520/6487/5339 +f 6421/6235/5113 6420/6231/5111 2887/6484/5336 +f 6420/6231/5111 7805/6197/5081 6519/6486/5338 +f 2881/6478/5330 2883/6485/5337 2888/6488/5340 +f 2884/6489/5341 2888/6488/5340 6506/6450/5307 +f 2883/6485/5337 6520/6487/5339 6521/6490/5342 +f 2888/6488/5340 6521/6490/5342 7825/6412/5269 +f 2884/6489/5341 2889/6491/5343 2885/6479/5331 +f 2889/6491/5343 6516/6492/5344 6517/6482/5334 +f 6505/6449/5306 6504/6443/5300 2889/6491/5343 +f 6504/6443/5300 7829/6446/5303 6516/6492/5344 +f 2890/6493/5345 2894/6494/5346 2895/6495/5347 +f 2891/6496/5348 2895/6495/5347 6522/6497/5349 +f 2894/6494/5346 6508/6457/5314 6507/6456/5313 +f 2895/6495/5347 6507/6456/5313 7826/6460/5289 +f 2891/6496/5348 2896/6498/5350 2892/6499/5351 +f 2896/6498/5350 6425/6253/5126 6424/6247/5122 +f 6523/6500/5352 6524/6501/5353 2896/6498/5350 +f 6524/6501/5353 7806/6254/5094 6425/6253/5126 +f 2890/6493/5345 2892/6499/5351 2897/6502/5354 +f 2893/6503/5355 2897/6502/5354 6525/6504/5356 +f 2892/6499/5351 6424/6247/5122 6423/6246/5121 +f 2897/6502/5354 6423/6246/5121 7768/6127/5016 +f 2893/6503/5355 2898/6505/5357 2894/6494/5346 +f 2898/6505/5357 6509/6465/5319 6508/6457/5314 +f 6526/6506/5358 6527/6507/5359 2898/6505/5357 +f 6527/6507/5359 7830/6466/5320 6509/6465/5319 +f 2899/6508/5360 2903/6509/5361 2904/6510/5362 +f 2900/6511/5363 2904/6510/5362 6407/6196/5080 +f 2903/6509/5361 6520/6487/5339 6519/6486/5338 +f 2904/6510/5362 6519/6486/5338 7805/6197/5081 +f 2900/6511/5363 2905/6512/5364 2901/6513/5365 +f 2905/6512/5364 6528/6514/5366 6529/6515/5367 +f 6406/6195/5079 6405/6191/5075 2905/6512/5364 +f 6405/6191/5075 7807/6153/5042 6528/6514/5366 +f 2899/6508/5360 2901/6513/5365 2906/6516/5368 +f 2902/6517/5369 2906/6516/5368 6482/6397/5254 +f 2901/6513/5365 6529/6515/5367 6530/6518/5370 +f 2906/6516/5368 6530/6518/5370 7823/6372/5229 +f 2902/6517/5369 2907/6519/5371 2903/6509/5361 +f 2907/6519/5371 6521/6490/5342 6520/6487/5339 +f 6481/6396/5253 6480/6410/5267 2907/6519/5371 +f 6480/6410/5267 7825/6412/5269 6521/6490/5342 +f 2908/6520/5372 2912/6521/5373 2913/6522/5374 +f 2909/6523/5375 2913/6522/5374 6531/6524/5376 +f 2912/6521/5373 6496/6427/5284 6495/6426/5283 +f 2913/6522/5374 6495/6426/5283 7824/6392/5249 +f 2909/6523/5375 2914/6525/5377 2910/6526/5378 +f 2914/6525/5377 6416/6527/5098 6415/6528/5091 +f 6532/6529/5379 6533/6530/5380 2914/6525/5377 +f 6533/6530/5380 7808/6165/5054 6416/6527/5098 +f 2908/6520/5372 2910/6526/5378 2915/6531/5381 +f 2911/6532/5382 2915/6531/5381 6524/6533/5353 +f 2910/6526/5378 6415/6528/5091 6414/6534/5090 +f 2915/6531/5381 6414/6534/5090 7806/6535/5094 +f 2911/6532/5382 2916/6536/5383 2912/6521/5373 +f 2916/6536/5383 6497/6431/5288 6496/6427/5284 +f 6523/6537/5352 6522/6538/5349 2916/6536/5383 +f 6522/6538/5349 7826/6432/5289 6497/6431/5288 +f 2917/6539/5384 2921/6540/5385 2922/6541/5386 +f 2918/6542/5387 2922/6541/5386 6389/6152/5041 +f 2921/6540/5385 6529/6515/5367 6528/6514/5366 +f 2922/6541/5386 6528/6514/5366 7807/6153/5042 +f 2918/6542/5387 2923/6543/5388 2919/6544/5389 +f 2923/6543/5388 6534/6545/5390 6535/6546/5391 +f 6388/6151/5040 6387/6144/5033 2923/6543/5388 +f 6387/6144/5033 7809/6146/5035 6534/6545/5390 +f 2917/6539/5384 2919/6544/5389 2924/6547/5392 +f 2920/6548/5393 2924/6547/5392 6464/6354/5215 +f 2919/6544/5389 6535/6546/5391 6536/6549/5394 +f 2924/6547/5392 6536/6549/5394 7817/6355/5189 +f 2920/6548/5393 2925/6550/5395 2921/6540/5385 +f 2925/6550/5395 6530/6518/5370 6529/6515/5367 +f 6463/6353/5214 6462/6369/5226 2925/6550/5395 +f 6462/6369/5226 7823/6372/5229 6530/6518/5370 +f 2926/6551/5396 2930/6552/5397 2931/6553/5398 +f 2927/6554/5399 2931/6553/5398 6537/6555/5400 +f 2930/6552/5397 6478/6390/5247 6477/6387/5244 +f 2931/6553/5398 6477/6387/5244 7818/6347/5209 +f 2927/6554/5399 2932/6556/5401 2928/6557/5402 +f 2932/6556/5401 6398/6170/5059 6397/6162/5051 +f 6538/6558/5403 6539/6559/5404 2932/6556/5401 +f 6539/6559/5404 7810/6171/5060 6398/6170/5059 +f 2926/6551/5396 2928/6557/5402 2933/6560/5405 +f 2929/6561/5406 2933/6560/5405 6533/6530/5380 +f 2928/6557/5402 6397/6162/5051 6396/6161/5050 +f 2933/6560/5405 6396/6161/5050 7808/6165/5054 +f 2929/6561/5406 2934/6562/5407 2930/6552/5397 +f 2934/6562/5407 6479/6391/5248 6478/6390/5247 +f 6532/6529/5379 6531/6524/5376 2934/6562/5407 +f 6531/6524/5376 7824/6392/5249 6479/6391/5248 +f 2935/6563/5408 2939/6564/5409 2940/6565/5410 +f 2936/6566/5411 2940/6565/5410 6540/6567/5412 +f 2939/6564/5409 6535/6568/5391 6534/6569/5390 +f 2940/6565/5410 6534/6569/5390 7809/6570/5035 +f 2936/6566/5411 2941/6571/5413 2937/6572/5414 +f 2941/6571/5413 6543/6573/5415 6544/6574/5416 +f 6541/6575/5417 6542/6576/5418 2941/6571/5413 +f 6542/6576/5418 7811/6577/5419 6543/6573/5415 +f 2935/6563/5408 2937/6572/5414 2942/6578/5420 +f 2942/6578/5420 6446/6309/5175 6445/6308/5174 +f 2937/6572/5414 6544/6574/5416 6545/6579/5421 +f 2942/6578/5420 6545/6579/5421 7815/6310/5149 +f 2938/6580/5422 2943/6581/5423 2939/6564/5409 +f 2943/6581/5423 6536/6582/5394 6535/6568/5391 +f 2938/6580/5422 6445/6308/5174 6444/6324/5186 +f 2943/6581/5423 6444/6324/5186 7817/6327/5189 +f 2944/6583/5424 2948/6584/5425 2949/6585/5426 +f 2945/6586/5427 2949/6585/5426 6546/6587/5428 +f 6460/6345/5207 6459/6342/5204 2949/6585/5426 +f 2949/6585/5426 6459/6342/5204 7816/6302/5169 +f 2945/6586/5427 2950/6588/5429 2946/6589/5430 +f 2950/6588/5429 6549/6590/5431 6550/6591/5432 +f 6547/6592/5433 6548/6593/5434 2950/6588/5429 +f 6548/6593/5434 7812/6594/5435 6549/6590/5431 +f 2944/6583/5424 2946/6589/5430 2951/6595/5436 +f 2947/6596/5437 2951/6595/5436 6539/6559/5404 +f 2946/6589/5430 6550/6591/5432 6551/6597/5438 +f 2951/6595/5436 6551/6597/5438 7810/6171/5060 +f 2947/6596/5437 2952/6598/5439 2948/6584/5425 +f 2948/6584/5425 2952/6598/5439 6461/6346/5208 +f 6538/6558/5403 6537/6555/5400 2952/6598/5439 +f 2952/6598/5439 6537/6555/5400 7818/6347/5209 +f 2957/6599/5440 2958/6600/5441 2954/6601/5442 +f 2958/6600/5441 6059/5067/4169 6058/5062/4164 +f 6553/6602/5443 6554/6603/5444 2958/6600/5441 +f 6554/6603/5444 7741/5024/4134 6059/5067/4169 +f 2953/6604/5445 2954/6601/5442 2959/6605/5446 +f 2955/6606/5447 2959/6605/5446 6428/6268/5135 +f 2954/6601/5442 6058/5062/4164 6057/5061/4163 +f 2959/6605/5446 6057/5061/4163 7743/5065/4167 +f 2955/6606/5447 2960/6607/5448 2956/6608/5449 +f 2960/6607/5448 6545/6609/5421 6544/6610/5416 +f 6427/6267/5134 6426/6279/5146 2960/6607/5448 +f 6426/6279/5146 7815/6282/5149 6545/6609/5421 +f 2953/6604/5445 2956/6608/5449 2961/6611/5450 +f 2957/6599/5440 2961/6611/5450 6552/6612/5451 +f 2956/6608/5449 6544/6610/5416 6543/6613/5415 +f 2961/6611/5450 6543/6613/5415 7811/6614/5419 +f 2966/6615/5452 2967/6616/5453 2963/6617/5454 +f 2967/6616/5453 6443/6301/5168 6442/6300/5167 +f 6547/6592/5433 6546/6587/5428 2967/6616/5453 +f 6546/6587/5428 7816/6302/5169 6443/6301/5168 +f 2962/6618/5455 2963/6617/5454 2968/6619/5456 +f 2964/6620/5457 2968/6619/5456 6062/5074/4176 +f 2963/6617/5454 6442/6300/5167 6441/6297/5164 +f 2968/6619/5456 6441/6297/5164 7744/5075/4177 +f 2964/6620/5457 2969/6621/5458 2965/6622/5459 +f 2969/6621/5458 6555/6623/5460 6556/6624/5461 +f 6061/5073/4175 6060/5083/4185 2969/6621/5458 +f 6060/5083/4185 7742/5035/4145 6555/6623/5460 +f 2962/6618/5455 2965/6622/5459 2970/6625/5462 +f 2966/6615/5452 2970/6625/5462 6548/6593/5434 +f 2965/6622/5459 6556/6624/5461 6557/6626/5463 +f 2970/6625/5462 6557/6626/5463 7812/6594/5435 +f 2975/6627/5464 2976/6628/5465 2972/6629/5466 +f 2976/6628/5465 6561/6630/5467 6562/6631/5468 +f 6559/6632/5469 6560/6633/5470 2976/6628/5465 +f 6560/6633/5470 7525/6634/5471 6561/6630/5467 +f 2971/6635/5472 2972/6629/5466 2977/6636/5473 +f 2973/6637/5474 2977/6636/5473 6542/6576/5418 +f 2972/6629/5466 6562/6631/5468 6563/6638/5475 +f 2977/6636/5473 6563/6638/5475 7811/6577/5419 +f 2973/6637/5474 2978/6639/5476 2974/6640/5477 +f 2978/6639/5476 6386/6641/5034 6385/6642/5026 +f 6541/6575/5417 6540/6567/5412 2978/6639/5476 +f 6540/6567/5412 7809/6570/5035 6386/6641/5034 +f 2971/6635/5472 2974/6640/5477 2979/6643/5478 +f 2975/6627/5464 2979/6643/5478 6558/6644/5479 +f 2974/6640/5477 6385/6642/5026 6384/6645/5025 +f 2979/6643/5478 6384/6645/5025 7753/6646/5029 +f 2984/6647/5480 2985/6648/5481 2981/6649/5482 +f 2985/6648/5481 6551/6650/5438 6550/6651/5432 +f 6400/6652/5063 6399/6653/5058 2985/6648/5481 +f 6399/6653/5058 7810/6654/5060 6551/6650/5438 +f 2980/6655/5483 2981/6649/5482 2986/6656/5484 +f 2982/6657/5485 2986/6656/5484 6564/6658/5486 +f 2981/6649/5482 6550/6651/5432 6549/6659/5431 +f 2986/6656/5484 6549/6659/5431 7812/6660/5435 +f 2982/6657/5485 2987/6661/5487 2983/6662/5488 +f 2987/6661/5487 6560/6633/5470 6559/6632/5469 +f 6565/6663/5489 6566/6664/5490 2987/6661/5487 +f 6566/6664/5490 7525/6634/5471 6560/6633/5470 +f 2980/6655/5483 2983/6662/5488 2988/6665/5491 +f 2984/6647/5480 2988/6665/5491 6401/6666/5064 +f 2983/6662/5488 6559/6632/5469 6558/6644/5479 +f 2988/6665/5491 6558/6644/5479 7753/6646/5029 +f 2993/6667/5492 2994/6668/5493 2990/6669/5494 +f 2994/6668/5493 6023/6670/4073 6022/6671/4068 +f 6568/6672/5495 6569/6673/5496 2994/6668/5493 +f 6569/6673/5496 7735/6674/4037 6023/6670/4073 +f 2989/6675/5497 2990/6669/5494 2995/6676/5498 +f 2991/6677/5499 2995/6676/5498 6035/6678/4105 +f 2990/6669/5494 6022/6671/4068 6021/6679/4067 +f 2995/6676/5498 6021/6679/4067 7737/6680/4071 +f 2989/6675/5497 2991/6677/5499 2996/6681/5500 +f 2992/6682/5501 2996/6681/5500 6047/6683/4137 +f 2991/6677/5499 6034/6684/4100 6033/6685/4099 +f 2996/6681/5500 6033/6685/4099 7739/6686/4103 +f 2992/6682/5501 2997/6687/5502 2993/6667/5492 +f 2997/6687/5502 6567/6688/5503 6568/6672/5495 +f 6046/6689/4136 6045/6690/4131 2997/6687/5502 +f 6045/6690/4131 7741/6691/4134 6567/6688/5503 +f 2998/6692/5504 3002/6693/5505 3003/6694/5506 +f 2999/6695/5507 3003/6694/5506 6038/6696/4112 +f 3002/6693/5505 6049/6697/4143 6048/6698/4153 +f 3003/6694/5506 6048/6698/4153 7740/6699/4113 +f 2998/6692/5504 2999/6695/5507 3004/6700/5508 +f 3000/6701/5509 3004/6700/5508 6026/6702/4079 +f 2999/6695/5507 6037/6703/4111 6036/6704/4121 +f 3004/6700/5508 6036/6704/4121 7738/6705/4080 +f 3000/6701/5509 3005/6706/5510 3001/6707/5511 +f 3005/6706/5510 6570/6708/5512 6571/6709/5513 +f 6025/6710/4078 6024/6711/4089 3005/6706/5510 +f 6024/6711/4089 7736/6712/4049 6570/6708/5512 +f 3001/6707/5511 3006/6713/5514 3002/6693/5505 +f 3006/6713/5514 6050/6714/4144 6049/6697/4143 +f 6571/6709/5513 6572/6715/5515 3006/6713/5514 +f 6572/6715/5515 7742/6716/4145 6050/6714/4144 +f 3007/6717/5516 3011/6718/5517 3012/6719/5518 +f 3008/6720/5519 3012/6719/5518 6569/6721/5496 +f 3011/6718/5517 6574/6722/5520 6575/6723/5521 +f 3012/6719/5518 6575/6723/5521 7735/6724/4037 +f 3008/6720/5519 3013/6725/5522 3009/6726/5523 +f 3013/6725/5522 6554/6727/5444 6553/6728/5443 +f 6568/6729/5495 6567/6730/5503 3013/6725/5522 +f 3013/6725/5522 6567/6730/5503 7741/6731/4134 +f 3007/6717/5516 3009/6726/5523 3014/6732/5524 +f 3010/6733/5525 3014/6732/5524 6563/6734/5475 +f 6553/6728/5443 6552/6735/5451 3014/6732/5524 +f 6552/6735/5451 7811/6736/5419 6563/6734/5475 +f 3010/6733/5525 3015/6737/5526 3011/6718/5517 +f 3015/6737/5526 6573/6738/5527 6574/6722/5520 +f 6562/6739/5468 6561/6740/5467 3015/6737/5526 +f 6561/6740/5467 7525/6741/5471 6573/6738/5527 +f 3016/6742/5528 3020/6743/5529 3021/6744/5530 +f 3021/6744/5530 6557/6626/5463 6556/6624/5461 +f 3020/6743/5529 6565/6745/5489 6564/6746/5486 +f 6564/6746/5486 7812/6594/5435 6557/6626/5463 +f 3017/6747/5531 3022/6748/5532 3018/6749/5533 +f 3022/6748/5532 6572/6750/5515 6571/6751/5513 +f 6556/6624/5461 6555/6623/5460 3022/6748/5532 +f 3022/6748/5532 6555/6623/5460 7742/5035/4145 +f 3016/6742/5528 3018/6749/5533 3023/6752/5534 +f 3019/6753/5535 3023/6752/5534 6576/6754/5536 +f 3018/6749/5533 6571/6751/5513 6570/6755/5512 +f 3023/6752/5534 6570/6755/5512 7736/6756/4049 +f 3019/6753/5535 3024/6757/5537 3020/6743/5529 +f 3024/6757/5537 6566/6758/5490 6565/6745/5489 +f 6577/6759/5538 6578/6760/5539 3024/6757/5537 +f 6578/6760/5539 7525/6761/5471 6566/6758/5490 +f 3025/6762/5540 3028/6763/5541 3029/6764/5542 +f 3029/6764/5542 6011/6765/4040 6010/6766/4034 +f 3028/6763/5541 6580/6767/5543 6581/6768/5544 +f 3029/6764/5542 6581/6768/5544 7524/6769/4041 +f 3025/6762/5540 3026/6770/5545 3030/6771/5546 +f 3030/6771/5546 6575/6772/5521 6574/6773/5520 +f 3026/6770/5545 6010/6766/4034 6009/6774/4033 +f 3030/6771/5546 6009/6774/4033 7735/6775/4037 +f 3025/6762/5540 3027/6776/5547 3031/6777/5548 +f 3031/6777/5548 6579/6778/5549 6580/6767/5543 +f 3027/6776/5547 6574/6773/5520 6573/6779/5527 +f 3031/6777/5548 6573/6779/5527 7525/6780/5471 +f 3032/6781/5550 3035/6782/5551 3036/6783/5552 +f 3033/6784/5553 3036/6783/5552 6578/6785/5539 +f 6580/6767/5543 6579/6778/5549 3036/6783/5552 +f 3036/6783/5552 6579/6778/5549 7525/6780/5471 +f 3032/6781/5550 3033/6784/5553 3037/6786/5554 +f 3034/6787/5555 3037/6786/5554 6014/6788/4048 +f 6577/6789/5538 6576/6790/5536 3037/6786/5554 +f 3037/6786/5554 6576/6790/5536 7736/6791/4049 +f 3032/6781/5550 3034/6787/5555 3038/6792/5556 +f 3035/6782/5551 3038/6792/5556 6581/6768/5544 +f 6013/6793/4047 6012/6794/4057 3038/6792/5556 +f 3038/6792/5556 6012/6794/4057 7524/6769/4041 +f 3043/6795/5557 3044/6796/5558 3040/6797/5559 +f 3044/6796/5558 6083/5139/4233 6082/5134/4228 +f 6430/6275/5142 6429/6271/5138 3044/6796/5558 +f 6429/6271/5138 7745/5101/4199 6083/5139/4233 +f 3039/6798/5560 3040/6797/5559 3045/6799/5561 +f 3041/6800/5562 3045/6799/5561 6350/6801/4951 +f 3040/6797/5559 6082/5134/4228 6081/5133/4227 +f 3045/6799/5561 6081/5133/4227 7747/5137/4231 +f 3041/6800/5562 3046/6802/5563 3042/6803/5564 +f 3046/6802/5563 6582/6804/5565 6583/6805/5566 +f 6349/6806/4950 6348/6807/4962 3046/6802/5563 +f 6348/6807/4962 7761/6808/4965 6582/6804/5565 +f 3039/6798/5560 3042/6803/5564 3047/6809/5567 +f 3043/6795/5557 3047/6809/5567 6431/6276/5143 +f 3042/6803/5564 6583/6805/5566 6584/6810/5568 +f 3047/6809/5567 6584/6810/5568 7813/6277/5144 +f 3052/6811/5569 3053/6812/5570 3049/6813/5571 +f 3053/6812/5570 6365/6814/4984 6364/6815/4983 +f 6586/6816/5572 6587/6817/5573 3053/6812/5570 +f 6587/6817/5573 7762/6818/4985 6365/6814/4984 +f 3048/6819/5574 3049/6813/5571 3054/6820/5575 +f 3050/6821/5576 3054/6820/5575 6086/6822/4240 +f 3049/6813/5571 6364/6815/4983 6363/6823/4980 +f 3054/6820/5575 6363/6823/4980 7748/6824/4241 +f 3050/6821/5576 3055/6825/5577 3051/6826/5578 +f 3055/6825/5577 6440/6294/5161 6439/6287/5154 +f 6085/6827/4239 6084/6828/4249 3055/6825/5577 +f 6084/6828/4249 7746/5111/4209 6440/6294/5161 +f 3048/6819/5574 3051/6826/5578 3056/6829/5579 +f 3052/6811/5569 3056/6829/5579 6585/6830/5580 +f 3051/6826/5578 6439/6287/5154 6438/6286/5153 +f 3056/6829/5579 6438/6286/5153 7814/6290/5157 +f 3057/6831/5581 3061/6832/5582 3062/6833/5583 +f 3058/6834/5584 3062/6833/5583 6588/6835/5585 +f 3061/6832/5582 6583/6836/5566 6582/6837/5565 +f 3062/6833/5583 6582/6837/5565 7761/6838/4965 +f 3058/6834/5584 3063/6839/5586 3059/6840/5587 +f 3063/6839/5586 6591/6841/5588 6592/6842/5589 +f 6589/6843/5590 6590/6844/5591 3063/6839/5586 +f 6590/6844/5591 7763/6845/5592 6591/6841/5588 +f 3057/6831/5581 3059/6840/5587 3064/6846/5593 +f 3060/6847/5594 3064/6846/5593 6449/6321/5183 +f 3059/6840/5587 6592/6842/5589 6593/6848/5595 +f 3064/6846/5593 6593/6848/5595 7819/6322/5184 +f 3060/6847/5594 3065/6849/5596 3061/6832/5582 +f 3065/6849/5596 6584/6850/5568 6583/6836/5566 +f 6448/6314/5179 6447/6313/5178 3065/6849/5596 +f 6447/6313/5178 7813/6317/5144 6584/6850/5568 +f 3066/6851/5597 3070/6852/5598 3071/6853/5599 +f 3067/6854/5600 3071/6853/5599 6594/6855/5601 +f 3070/6852/5598 6457/6338/5200 6456/6332/5194 +f 3071/6853/5599 6456/6332/5194 7820/6335/5197 +f 3067/6854/5600 3072/6856/5602 3068/6857/5603 +f 3072/6856/5602 6597/6858/5604 6598/6859/5605 +f 6595/6860/5606 6596/6861/5607 3072/6856/5602 +f 6596/6861/5607 7764/6862/5608 6597/6858/5604 +f 3066/6851/5597 3068/6857/5603 3073/6863/5609 +f 3069/6864/5610 3073/6863/5609 6587/6817/5573 +f 3068/6857/5603 6598/6859/5605 6599/6865/5611 +f 3073/6863/5609 6599/6865/5611 7762/6818/4985 +f 3069/6864/5610 3074/6866/5612 3070/6852/5598 +f 3074/6866/5612 6458/6339/5201 6457/6338/5200 +f 6586/6816/5572 6585/6830/5580 3074/6866/5612 +f 6585/6830/5580 7814/6290/5157 6458/6339/5201 +f 3075/6867/5613 3079/6868/5614 3080/6869/5615 +f 3076/6870/5616 3080/6869/5615 6600/6871/5617 +f 3079/6868/5614 6592/6872/5589 6591/6873/5588 +f 6591/6873/5588 7763/6874/5592 6600/6871/5617 +f 3076/6870/5616 3081/6875/5618 3077/6876/5619 +f 3081/6875/5618 6603/6877/5620 6604/6878/5621 +f 6601/6879/5622 6602/6880/5623 3081/6875/5618 +f 6602/6880/5623 7833/6881/5624 6603/6877/5620 +f 3075/6867/5613 3077/6876/5619 3082/6882/5625 +f 3078/6883/5626 3082/6882/5625 6467/6366/5223 +f 3077/6876/5619 6604/6878/5621 6605/6884/5627 +f 3082/6882/5625 6605/6884/5627 7821/6367/5224 +f 3078/6883/5626 3083/6885/5628 3079/6868/5614 +f 3083/6885/5628 6593/6886/5595 6592/6872/5589 +f 6466/6359/5219 6465/6358/5218 3083/6885/5628 +f 6465/6358/5218 7819/6362/5184 6593/6886/5595 +f 3084/6887/5629 3088/6888/5630 3089/6889/5631 +f 3085/6890/5632 3089/6889/5631 6606/6891/5633 +f 3088/6888/5630 6475/6383/5240 6474/6377/5234 +f 3089/6889/5631 6474/6377/5234 7822/6380/5237 +f 3085/6890/5632 3090/6892/5634 3086/6893/5635 +f 3090/6892/5634 6609/6894/5636 6610/6895/5637 +f 6607/6896/5638 6608/6897/5639 3090/6892/5634 +f 6608/6897/5639 7834/6898/5640 6609/6894/5636 +f 3084/6887/5629 3086/6893/5635 3091/6899/5641 +f 3087/6900/5642 3091/6899/5641 6596/6861/5607 +f 3086/6893/5635 6610/6895/5637 6611/6901/5643 +f 6611/6901/5643 7764/6862/5608 6596/6861/5607 +f 3087/6900/5642 3092/6902/5644 3088/6888/5630 +f 3092/6902/5644 6476/6384/5241 6475/6383/5240 +f 6595/6860/5606 6594/6855/5601 3092/6902/5644 +f 6594/6855/5601 7820/6335/5197 6476/6384/5241 +f 3097/6903/5645 3098/6904/5646 3094/6905/5647 +f 3098/6904/5646 6500/6906/5295 6499/6907/5294 +f 6613/6908/5648 6614/6909/5649 3098/6904/5646 +f 6614/6909/5649 7765/6910/5296 6500/6906/5295 +f 3093/6911/5650 3094/6905/5647 3099/6912/5651 +f 3095/6913/5652 3099/6912/5651 6485/6914/5263 +f 3094/6905/5647 6499/6907/5294 6498/6915/5309 +f 3099/6912/5651 6498/6915/5309 7827/6916/5264 +f 3095/6913/5652 3100/6917/5653 3096/6918/5654 +f 3100/6917/5653 6605/6919/5627 6604/6920/5621 +f 6484/6921/5262 6483/6922/5257 3100/6917/5653 +f 6483/6922/5257 7821/6923/5224 6605/6919/5627 +f 3093/6911/5650 3096/6918/5654 3101/6924/5655 +f 3097/6903/5645 3101/6924/5655 6612/6925/5656 +f 3096/6918/5654 6604/6920/5621 6603/6926/5620 +f 3101/6924/5655 6603/6926/5620 7833/6927/5624 +f 3106/6928/5657 3107/6929/5658 3103/6930/5659 +f 3107/6929/5658 6494/6423/5280 6493/6417/5274 +f 6607/6896/5638 6606/6891/5633 3107/6929/5658 +f 6606/6891/5633 7822/6380/5237 6494/6423/5280 +f 3102/6931/5660 3103/6930/5659 3108/6932/5661 +f 3104/6933/5662 3108/6932/5661 6515/6934/5329 +f 3103/6930/5659 6493/6417/5274 6492/6416/5273 +f 3108/6932/5661 6492/6416/5273 7828/6420/5277 +f 3104/6933/5662 3109/6935/5663 3105/6936/5664 +f 3109/6935/5663 6615/6937/5665 6616/6938/5666 +f 6514/6939/5328 6513/6940/5323 3109/6935/5663 +f 6513/6940/5323 7766/6941/5326 6615/6937/5665 +f 3102/6931/5660 3105/6936/5664 3110/6942/5667 +f 3106/6928/5657 3110/6942/5667 6608/6897/5639 +f 3105/6936/5664 6616/6938/5666 6617/6943/5668 +f 3110/6942/5667 6617/6943/5668 7834/6898/5640 +f 3115/6944/5669 3116/6945/5670 3112/6946/5671 +f 3116/6945/5670 6323/5945/4872 6322/5941/4868 +f 6619/6947/5672 6620/6948/5673 3116/6945/5670 +f 6620/6948/5673 7799/5946/4873 6323/5945/4872 +f 3111/6949/5674 3112/6946/5671 3117/6950/5675 +f 3113/6951/5676 3117/6950/5675 6621/6952/5677 +f 3112/6946/5671 6322/5941/4868 6321/5940/4867 +f 3117/6950/5675 6321/5940/4867 7801/5907/4838 +f 3113/6951/5676 3118/6953/5678 3114/6954/5679 +f 3118/6953/5678 6624/6955/5680 6625/6956/5681 +f 6622/6957/5682 6623/6958/5683 3118/6953/5678 +f 6623/6958/5683 7837/6959/5684 6624/6955/5680 +f 3111/6949/5674 3114/6954/5679 3119/6960/5685 +f 3115/6944/5669 3119/6960/5685 6618/6961/5686 +f 3114/6954/5679 6625/6956/5681 6626/6962/5687 +f 6626/6962/5687 7835/6963/5688 6618/6961/5686 +f 3124/6964/5689 3125/6965/5690 3121/6966/5691 +f 3125/6965/5690 6630/6967/5692 6631/6968/5693 +f 6628/6969/5694 6629/6970/5695 3125/6965/5690 +f 6629/6970/5695 7838/6971/5696 6630/6967/5692 +f 3120/6972/5697 3121/6966/5691 3126/6973/5698 +f 3122/6974/5699 3126/6973/5698 6332/5964/4890 +f 3121/6966/5691 6631/6968/5693 6632/6975/5700 +f 3126/6973/5698 6632/6975/5700 7802/5965/4849 +f 3122/6974/5699 3127/6976/5701 3123/6977/5702 +f 3127/6976/5701 6633/6978/5703 6634/6979/5704 +f 6331/5963/4889 6330/5956/4883 3127/6976/5701 +f 6330/5956/4883 7800/5959/4886 6633/6978/5703 +f 3120/6972/5697 3123/6977/5702 3128/6980/5705 +f 3124/6964/5689 3128/6980/5705 6627/6981/5706 +f 3123/6977/5702 6634/6979/5704 6635/6982/5707 +f 6635/6982/5707 7836/6983/5708 6627/6981/5706 +f 3133/6984/5709 3134/6985/5710 3130/6986/5711 +f 3130/6986/5711 3134/6985/5710 6639/6987/5712 +f 6637/6988/5713 6638/6989/5714 3134/6985/5710 +f 6638/6989/5714 7769/6062/4960 6639/6987/5712 +f 3130/6986/5711 3135/6990/5715 3131/6991/5716 +f 3135/6990/5715 6626/6962/5687 6625/6956/5681 +f 6640/6992/5717 6641/6993/5718 3135/6990/5715 +f 6641/6993/5718 7835/6963/5688 6626/6962/5687 +f 3131/6991/5716 3136/6994/5719 3132/6995/5720 +f 3136/6994/5719 6642/6996/5721 6643/6997/5722 +f 3131/6991/5716 6625/6956/5681 6624/6955/5680 +f 6624/6955/5680 7837/6959/5684 6642/6996/5721 +f 3129/6998/5723 3132/6995/5720 3137/6999/5724 +f 3133/6984/5709 3137/6999/5724 6636/7000/5725 +f 3132/6995/5720 6643/6997/5722 6644/7001/5726 +f 3137/6999/5724 6644/7001/5726 7771/7002/5727 +f 3142/7003/5728 3143/7004/5729 3139/7005/5730 +f 3139/7005/5730 3143/7004/5729 6629/6970/5695 +f 6646/7006/5731 6647/7007/5732 3143/7004/5729 +f 6647/7007/5732 7838/6971/5696 6629/6970/5695 +f 3139/7005/5730 3144/7008/5733 3140/7009/5734 +f 3144/7008/5733 6648/7010/5735 6649/7011/5736 +f 6628/6969/5694 6627/6981/5706 3144/7008/5733 +f 6627/6981/5706 7836/6983/5708 6648/7010/5735 +f 3140/7009/5734 3145/7012/5737 3141/7013/5738 +f 3145/7012/5737 6651/7014/5739 6652/7015/5740 +f 3140/7009/5734 6649/7011/5736 6650/7016/5741 +f 6650/7016/5741 7770/6075/4973 6651/7014/5739 +f 3138/7017/5742 3141/7013/5738 3146/7018/5743 +f 3142/7003/5728 3146/7018/5743 6645/7019/5744 +f 3141/7013/5738 6652/7015/5740 6653/7020/5745 +f 3146/7018/5743 6653/7020/5745 7772/7021/5746 +f 3147/7022/5747 3151/7023/5748 3152/7024/5749 +f 3148/7025/5750 3152/7024/5749 6107/5228/4297 +f 3151/7023/5748 6352/6057/4955 6351/6056/4954 +f 3152/7024/5749 6351/6056/4954 7749/5184/4262 +f 3148/7025/5750 3153/7026/5751 3149/7027/5752 +f 3153/7026/5751 6654/7028/5753 6655/7029/5754 +f 6106/5227/4296 6105/5223/4292 3153/7026/5751 +f 6105/5223/4292 7751/5225/4294 6654/7028/5753 +f 3147/7022/5747 3149/7027/5752 3154/7030/5755 +f 3150/7031/5756 3154/7030/5755 6641/6993/5718 +f 3149/7027/5752 6655/7029/5754 6656/7032/5757 +f 3154/7030/5755 6656/7032/5757 7835/6963/5688 +f 3150/7031/5756 3155/7033/5758 3151/7023/5748 +f 3155/7033/5758 6353/6061/4959 6352/6057/4955 +f 6640/6992/5717 6639/6987/5712 3155/7033/5758 +f 6639/6987/5712 7769/6062/4960 6353/6061/4959 +f 3156/7034/5759 3160/7035/5760 3161/7036/5761 +f 3157/7037/5762 3161/7036/5761 6657/7038/5763 +f 3160/7035/5760 6649/7011/5736 6648/7010/5735 +f 3161/7036/5761 6648/7010/5735 7836/6983/5708 +f 3157/7037/5762 3162/7039/5764 3158/7040/5765 +f 3162/7039/5764 6110/7041/4304 6109/7042/4303 +f 6658/7043/5766 6659/7044/5767 3162/7039/5764 +f 6659/7044/5767 7752/7045/4305 6110/7041/4304 +f 3156/7034/5759 3158/7040/5765 3163/7046/5768 +f 3159/7047/5769 3163/7046/5768 6362/6081/4977 +f 3158/7040/5765 6109/7042/4303 6108/7048/4313 +f 3163/7046/5768 6108/7048/4313 7750/6082/4273 +f 3159/7047/5769 3164/7049/5770 3160/7035/5760 +f 3164/7049/5770 6650/7016/5741 6649/7011/5736 +f 6361/6080/4976 6360/6072/4970 3164/7049/5770 +f 6360/6072/4970 7770/6075/4973 6650/7016/5741 +f 3165/7050/5771 3169/7051/5772 3170/7052/5773 +f 3166/7053/5774 3170/7052/5773 6660/7054/5775 +f 3169/7051/5772 6655/7029/5754 6654/7028/5753 +f 3170/7052/5773 6654/7028/5753 7751/5225/4294 +f 3166/7053/5774 3171/7055/5776 3167/7056/5777 +f 3171/7055/5776 6245/7057/4673 6244/7058/4667 +f 6661/7059/5778 6662/7060/5779 3171/7055/5776 +f 6662/7060/5779 7795/7061/4674 6245/7057/4673 +f 3165/7050/5771 3167/7056/5777 3172/7062/5780 +f 3168/7063/5781 3172/7062/5780 6663/7064/5782 +f 3167/7056/5777 6244/7058/4667 6243/7065/4666 +f 3172/7062/5780 6243/7065/4666 7793/6027/4669 +f 3168/7063/5781 3173/7066/5783 3169/7051/5772 +f 3173/7066/5783 6656/7032/5757 6655/7029/5754 +f 6664/7067/5784 6665/7068/5785 3173/7066/5783 +f 6665/7068/5785 7835/6963/5688 6656/7032/5757 +f 3174/7069/5786 3178/7070/5787 3179/7071/5788 +f 3175/7072/5789 3179/7071/5788 6254/7073/4691 +f 3178/7070/5787 6667/7074/5790 6668/7075/5791 +f 3179/7071/5788 6668/7075/5791 7794/7076/4692 +f 3175/7072/5789 3180/7077/5792 3176/7078/5793 +f 3180/7077/5792 6669/7079/5794 6670/7080/5795 +f 6253/7081/4690 6252/7082/4682 3180/7077/5792 +f 6252/7082/4682 7796/7083/4685 6669/7079/5794 +f 3174/7069/5786 3176/7078/5793 3181/7084/5796 +f 3177/7085/5797 3181/7084/5796 6659/7044/5767 +f 3176/7078/5793 6670/7080/5795 6671/7086/5798 +f 3181/7084/5796 6671/7086/5798 7752/7045/4305 +f 3177/7085/5797 3182/7087/5799 3178/7070/5787 +f 3182/7087/5799 6666/7088/5800 6667/7074/5790 +f 6658/7043/5766 6657/7038/5763 3182/7087/5799 +f 6657/7038/5763 7836/6983/5708 6666/7088/5800 +f 3186/7089/5801 3187/7090/5802 3184/7091/5803 +f 3187/7090/5802 6344/6026/4933 6343/6023/4931 +f 6664/7067/5784 6663/7064/5782 3187/7090/5802 +f 6663/7064/5782 7793/6027/4669 6344/6026/4933 +f 3183/7092/5804 3184/7091/5803 3188/7093/5805 +f 3185/7094/5806 3188/7093/5805 6620/6948/5673 +f 3184/7091/5803 6343/6023/4931 6342/6022/4930 +f 3188/7093/5805 6342/6022/4930 7799/5946/4873 +f 3183/7092/5804 3185/7094/5806 3189/7095/5807 +f 3186/7089/5801 3189/7095/5807 6665/7068/5785 +f 6619/6947/5672 6618/6961/5686 3189/7095/5807 +f 6618/6961/5686 7835/6963/5688 6665/7068/5785 +f 3190/7096/5808 3193/7097/5809 3194/7098/5810 +f 3194/7098/5810 6635/6982/5707 6634/6979/5704 +f 3193/7097/5809 6667/7074/5790 6666/7088/5800 +f 6666/7088/5800 7836/6983/5708 6635/6982/5707 +f 3190/7096/5808 3191/7099/5811 3195/7100/5812 +f 3192/7101/5813 3195/7100/5812 6347/7102/4938 +f 3191/7099/5811 6634/6979/5704 6633/6978/5703 +f 3195/7100/5812 6633/6978/5703 7800/5959/4886 +f 3192/7101/5813 3196/7103/5814 3193/7097/5809 +f 3196/7103/5814 6668/7075/5791 6667/7074/5790 +f 6346/7104/4937 6345/7105/4945 3196/7103/5814 +f 6345/7105/4945 7794/7076/4692 6668/7075/5791 +f 3200/7106/5815 3201/7107/5816 3198/7108/5817 +f 3201/7107/5816 6278/7109/4764 6277/7110/4760 +f 6661/7111/5778 6660/7112/5775 3201/7107/5816 +f 6660/7112/5775 7751/7113/4294 6278/7109/4764 +f 3197/7114/5818 3198/7108/5817 3202/7115/5819 +f 3199/7116/5820 3202/7115/5819 6269/7117/4736 +f 3198/7108/5817 6277/7110/4760 6276/7118/4759 +f 3202/7115/5819 6276/7118/4759 7797/7119/4737 +f 3197/7114/5818 3199/7116/5820 3203/7120/5821 +f 3200/7106/5815 3203/7120/5821 6662/7121/5779 +f 3199/7116/5820 6268/7122/4735 6267/7123/4732 +f 3203/7120/5821 6267/7123/4732 7795/7124/4674 +f 3204/7125/5822 3207/7126/5823 3208/7127/5824 +f 3205/7128/5825 3208/7127/5824 6272/7129/4742 +f 3207/7126/5823 6670/7080/5795 6669/7079/5794 +f 3208/7127/5824 6669/7079/5794 7796/7083/4685 +f 3204/7125/5822 3205/7128/5825 3209/7130/5826 +f 3206/7131/5827 3209/7130/5826 6281/7132/4774 +f 3205/7128/5825 6271/7133/4741 6270/7134/4750 +f 3209/7130/5826 6270/7134/4750 7798/7135/4753 +f 3206/7131/5827 3210/7136/5828 3207/7126/5823 +f 3210/7136/5828 6671/7086/5798 6670/7080/5795 +f 6280/7137/4773 6279/7138/4769 3210/7136/5828 +f 6279/7138/4769 7752/7045/4305 6671/7086/5798 +f 3215/7139/5829 3216/7140/5830 3212/7141/5831 +f 3216/7140/5830 6672/7142/5832 6673/7143/5833 +f 6502/7144/5301 6501/7145/5293 3216/7140/5830 +f 6501/7145/5293 7765/7146/5296 6672/7142/5832 +f 3211/7147/5834 3212/7141/5831 3217/7148/5835 +f 3217/7148/5835 6675/7149/5836 6676/7150/5837 +f 3212/7141/5831 6673/7143/5833 6674/7151/5838 +f 6674/7151/5838 7773/7152/5839 6675/7149/5836 +f 3213/7153/5840 3218/7154/5841 3214/7155/5842 +f 3214/7155/5842 3218/7154/5841 6678/7156/5843 +f 3213/7153/5840 6676/7150/5837 6677/7157/5844 +f 6677/7157/5844 7831/7158/5845 6678/7156/5843 +f 3211/7147/5834 3214/7155/5842 3219/7159/5846 +f 3219/7159/5846 6503/7160/5302 6502/7144/5301 +f 6679/7161/5847 6680/7162/5848 3219/7159/5846 +f 6680/7162/5848 7829/7163/5303 6503/7160/5302 +f 3224/7164/5849 3225/7165/5850 3221/7166/5851 +f 3221/7166/5851 3225/7165/5850 6684/7167/5852 +f 3224/7164/5849 6682/7168/5853 6683/7169/5854 +f 6683/7169/5854 7832/7170/5855 6684/7167/5852 +f 3220/7171/5856 3221/7166/5851 3226/7172/5857 +f 3222/7173/5858 3226/7172/5857 6687/7174/5859 +f 6685/7175/5860 6686/7176/5861 3226/7172/5857 +f 6686/7176/5861 7774/7177/5862 6687/7174/5859 +f 3222/7173/5858 3227/7178/5863 3223/7179/5864 +f 3227/7178/5863 6512/6471/5325 6511/6470/5324 +f 6688/7180/5865 6689/7181/5866 3227/7178/5863 +f 6689/7181/5866 7766/6472/5326 6512/6471/5325 +f 3220/7171/5856 3223/7179/5864 3228/7182/5867 +f 3228/7182/5867 6681/7183/5868 6682/7168/5853 +f 6511/6470/5324 6510/6464/5318 3228/7182/5867 +f 6510/6464/5318 7830/6466/5320 6681/7183/5868 +f 3233/7184/5869 3234/7185/5870 3230/7186/5871 +f 3234/7185/5870 6518/7187/5335 6517/7188/5334 +f 6373/7189/5002 6372/7190/4997 3234/7185/5870 +f 6372/7190/4997 7767/7191/4999 6518/7187/5335 +f 3229/7192/5872 3230/7186/5871 3235/7193/5873 +f 3231/7194/5874 3235/7193/5873 6680/7162/5848 +f 3230/7186/5871 6517/7188/5334 6516/7195/5344 +f 3235/7193/5873 6516/7195/5344 7829/7163/5303 +f 3231/7194/5874 3236/7196/5875 3232/7197/5876 +f 3236/7196/5875 6690/7198/5877 6691/7199/5878 +f 6679/7161/5847 6678/7156/5843 3236/7196/5875 +f 6678/7156/5843 7831/7158/5845 6690/7198/5877 +f 3229/7192/5872 3232/7197/5876 3237/7200/5879 +f 3233/7184/5869 3237/7200/5879 6374/7201/5003 +f 3232/7197/5876 6691/7199/5878 6692/7202/5880 +f 6692/7202/5880 7775/5855/4791 6374/7201/5003 +f 3242/7203/5881 3243/7204/5882 3239/7205/5883 +f 3243/7204/5882 6683/7169/5854 6682/7168/5853 +f 6694/7206/5884 6695/7207/5885 3243/7204/5882 +f 6695/7207/5885 7832/7170/5855 6683/7169/5854 +f 3238/7208/5886 3239/7205/5883 3244/7209/5887 +f 3240/7210/5888 3244/7209/5887 6527/6507/5359 +f 3239/7205/5883 6682/7168/5853 6681/7183/5868 +f 3244/7209/5887 6681/7183/5868 7830/6466/5320 +f 3240/7210/5888 3245/7211/5889 3241/7212/5890 +f 3245/7211/5889 6377/6126/5015 6376/6121/5010 +f 6526/6506/5358 6525/6504/5356 3245/7211/5889 +f 6525/6504/5356 7768/6127/5016 6377/6126/5015 +f 3238/7208/5886 3241/7212/5890 3246/7213/5891 +f 3242/7203/5881 3246/7213/5891 6693/7214/5892 +f 3241/7212/5890 6376/6121/5010 6375/6120/5009 +f 6375/6120/5009 7776/5880/4816 6693/7214/5892 +f 3251/7215/5893 3252/7216/5894 3248/7217/5895 +f 3252/7216/5894 6644/7001/5726 6643/6997/5722 +f 6697/7218/5896 6698/7219/5897 3252/7216/5894 +f 6698/7219/5897 7771/7002/5727 6644/7001/5726 +f 3247/7220/5898 3248/7217/5895 3253/7221/5899 +f 3253/7221/5899 6699/7222/5900 6700/7223/5901 +f 3248/7217/5895 6643/6997/5722 6642/6996/5721 +f 6642/6996/5721 7837/6959/5684 6699/7222/5900 +f 3249/7224/5902 3254/7225/5903 3250/7226/5904 +f 3254/7225/5903 6677/7157/5844 6676/7150/5837 +f 3249/7224/5902 6700/7223/5901 6701/7227/5905 +f 6701/7227/5905 7831/7158/5845 6677/7157/5844 +f 3247/7220/5898 3250/7226/5904 3255/7228/5906 +f 3251/7215/5893 3255/7228/5906 6696/7229/5907 +f 3250/7226/5904 6676/7150/5837 6675/7149/5836 +f 3255/7228/5906 6675/7149/5836 7773/7152/5839 +f 3260/7230/5908 3261/7231/5909 3257/7232/5910 +f 3257/7232/5910 3261/7231/5909 6702/7233/5911 +f 6685/7175/5860 6684/7167/5852 3261/7231/5909 +f 6684/7167/5852 7832/7170/5855 6702/7233/5911 +f 3256/7234/5912 3257/7232/5910 3262/7235/5913 +f 3258/7236/5914 3262/7235/5913 6647/7237/5732 +f 6703/7238/5915 6704/7239/5916 3262/7235/5913 +f 6704/7239/5916 7838/7240/5696 6647/7237/5732 +f 3258/7236/5914 3263/7241/5917 3259/7242/5918 +f 3263/7241/5917 6705/7243/5919 6706/7244/5920 +f 6646/7245/5731 6645/7246/5744 3263/7241/5917 +f 6645/7246/5744 7772/7247/5746 6705/7243/5919 +f 3256/7234/5912 3259/7242/5918 3264/7248/5921 +f 3260/7230/5908 3264/7248/5921 6686/7176/5861 +f 3259/7242/5918 6706/7244/5920 6707/7249/5922 +f 3264/7248/5921 6707/7249/5922 7774/7177/5862 +f 3269/7250/5923 3270/7251/5924 3266/7252/5925 +f 3270/7251/5924 6308/5906/4837 6307/5900/4831 +f 6622/6957/5682 6621/6952/5677 3270/7251/5924 +f 6621/6952/5677 7801/5907/4838 6308/5906/4837 +f 3265/7253/5926 3266/7252/5925 3271/7254/5927 +f 3267/7255/5928 3271/7254/5927 6708/7256/5929 +f 3266/7252/5925 6307/5900/4831 6306/5899/4830 +f 3271/7254/5927 6306/5899/4830 7803/5862/4798 +f 3267/7255/5928 3272/7257/5930 3268/7258/5931 +f 3272/7257/5930 6701/7227/5905 6700/7223/5901 +f 6709/7259/5932 6710/7260/5933 3272/7257/5930 +f 6710/7260/5933 7831/7158/5845 6701/7227/5905 +f 3265/7253/5926 3268/7258/5931 3273/7261/5934 +f 3269/7250/5923 3273/7261/5934 6623/6958/5683 +f 3268/7258/5931 6700/7223/5901 6699/7222/5900 +f 3273/7261/5934 6699/7222/5900 7837/6959/5684 +f 3278/7262/5935 3279/7263/5936 3275/7264/5937 +f 3279/7263/5936 6711/7265/5938 6712/7266/5939 +f 6703/7238/5915 6702/7233/5911 3279/7263/5936 +f 6702/7233/5911 7832/7170/5855 6711/7265/5938 +f 3274/7267/5940 3275/7264/5937 3280/7268/5941 +f 3276/7269/5942 3280/7268/5941 6317/5923/4854 +f 3275/7264/5937 6712/7266/5939 6713/7270/5943 +f 3280/7268/5941 6713/7270/5943 7804/5874/4810 +f 3276/7269/5942 3281/7271/5944 3277/7272/5945 +f 3281/7271/5944 6632/7273/5700 6631/7274/5693 +f 6316/5922/4853 6315/5915/4846 3281/7271/5944 +f 6315/5915/4846 7802/5918/4849 6632/7273/5700 +f 3274/7267/5940 3277/7272/5945 3282/7275/5946 +f 3278/7262/5935 3282/7275/5946 6704/7239/5916 +f 3277/7272/5945 6631/7274/5693 6630/7276/5692 +f 3282/7275/5946 6630/7276/5692 7838/7240/5696 +f 3283/7277/5947 3286/7278/5948 3287/7279/5949 +f 3287/7279/5949 6692/7202/5880 6691/7199/5878 +f 3286/7278/5948 6289/5860/4796 6288/5853/4789 +f 3287/7279/5949 6288/5853/4789 7775/5855/4791 +f 3283/7277/5947 3284/7280/5950 3288/7281/5951 +f 3285/7282/5952 3288/7281/5951 6710/7260/5933 +f 3284/7280/5950 6691/7199/5878 6690/7198/5877 +f 3288/7281/5951 6690/7198/5877 7831/7158/5845 +f 3283/7277/5947 3285/7282/5952 3289/7283/5953 +f 3286/7278/5948 3289/7283/5953 6290/5861/4797 +f 6709/7259/5932 6708/7256/5929 3289/7283/5953 +f 6708/7256/5929 7803/5862/4798 6290/5861/4797 +f 3290/7284/5954 3293/7285/5955 3294/7286/5956 +f 3294/7286/5956 6713/7270/5943 6712/7266/5939 +f 3293/7285/5955 6298/5871/4807 6297/5870/4806 +f 6297/5870/4806 7804/5874/4810 6713/7270/5943 +f 3290/7284/5954 3291/7287/5957 3295/7288/5958 +f 3292/7289/5959 3295/7288/5958 6695/7207/5885 +f 3291/7287/5957 6712/7266/5939 6711/7265/5938 +f 3295/7288/5958 6711/7265/5938 7832/7170/5855 +f 3290/7284/5954 3292/7289/5959 3296/7290/5960 +f 3293/7285/5955 3296/7290/5960 6299/5879/4815 +f 6694/7206/5884 6693/7214/5892 3296/7290/5960 +f 3296/7290/5960 6693/7214/5892 7776/5880/4816 +f 3301/7291/5961 3302/7292/5962 3298/7293/5963 +f 3302/7292/5962 6717/7294/5964 6718/7295/5965 +f 6715/7296/5966 6716/7297/5967 3302/7292/5962 +f 6716/7297/5967 7851/7298/5968 6717/7294/5964 +f 3297/7299/5969 3298/7293/5963 3303/7300/5970 +f 3299/7301/5971 3303/7300/5970 6720/7302/5972 +f 3298/7293/5963 6718/7295/5965 6719/7303/5973 +f 3303/7300/5970 6719/7303/5973 7839/7304/5974 +f 3299/7301/5971 3304/7305/5975 3300/7306/5976 +f 3304/7305/5975 6723/7307/5977 6724/7308/5978 +f 6721/7309/5979 6722/7310/5980 3304/7305/5975 +f 6722/7310/5980 7865/7311/5981 6723/7307/5977 +f 3297/7299/5969 3300/7306/5976 3305/7312/5982 +f 3301/7291/5961 3305/7312/5982 6714/7313/5983 +f 3300/7306/5976 6724/7308/5978 6725/7314/5984 +f 3305/7312/5982 6725/7314/5984 7853/7315/5985 +f 3310/7316/5986 3311/7317/5987 3307/7318/5988 +f 3311/7317/5987 6729/7319/5989 6730/7320/5990 +f 6727/7321/5991 6728/7322/5992 3311/7317/5987 +f 6728/7322/5992 7866/7323/5993 6729/7319/5989 +f 3306/7324/5994 3307/7318/5988 3312/7325/5995 +f 3308/7326/5996 3312/7325/5995 6732/7327/5997 +f 3307/7318/5988 6730/7320/5990 6731/7328/5998 +f 3312/7325/5995 6731/7328/5998 7840/7329/5999 +f 3308/7326/5996 3313/7330/6000 3309/7331/6001 +f 3313/7330/6000 6735/7332/6002 6736/7333/6003 +f 6733/7334/6004 6734/7335/6005 3313/7330/6000 +f 6734/7335/6005 7852/7336/6006 6735/7332/6002 +f 3306/7324/5994 3309/7331/6001 3314/7337/6007 +f 3310/7316/5986 3314/7337/6007 6726/7338/6008 +f 3309/7331/6001 6736/7333/6003 6737/7339/6009 +f 3314/7337/6007 6737/7339/6009 7854/7340/6010 +f 3315/7341/6011 3319/7342/6012 3320/7343/6013 +f 3316/7344/6014 3320/7343/6013 6716/7297/5967 +f 3319/7342/6012 6739/7345/6015 6740/7346/6016 +f 3320/7343/6013 6740/7346/6016 7851/7298/5968 +f 3316/7344/6014 3321/7347/6017 3317/7348/6018 +f 3321/7347/6017 6741/7349/6019 6742/7350/6020 +f 6715/7296/5966 6714/7313/5983 3321/7347/6017 +f 6714/7313/5983 7853/7315/5985 6741/7349/6019 +f 3315/7341/6011 3317/7348/6018 3322/7351/6021 +f 3318/7352/6022 3322/7351/6021 6744/7353/6023 +f 3317/7348/6018 6742/7350/6020 6743/7354/6024 +f 3322/7351/6021 6743/7354/6024 7855/7355/6025 +f 3318/7352/6022 3323/7356/6026 3319/7342/6012 +f 3323/7356/6026 6738/7357/6027 6739/7345/6015 +f 6745/7358/6028 6746/7359/6029 3323/7356/6026 +f 6746/7359/6029 7849/7360/6030 6738/7357/6027 +f 3324/7361/6031 3328/7362/6032 3329/7363/6033 +f 3325/7364/6034 3329/7363/6033 6750/7365/6035 +f 3328/7362/6032 6748/7366/6036 6749/7367/6037 +f 3329/7363/6033 6749/7367/6037 7856/7368/6038 +f 3325/7364/6034 3330/7369/6039 3326/7370/6040 +f 3330/7369/6039 6737/7371/6009 6736/7372/6003 +f 6751/7373/6041 6752/7374/6042 3330/7369/6039 +f 6752/7374/6042 7854/7375/6010 6737/7371/6009 +f 3324/7361/6031 3326/7370/6040 3331/7376/6043 +f 3327/7377/6044 3331/7376/6043 6753/7378/6045 +f 3326/7370/6040 6736/7372/6003 6735/7379/6002 +f 3331/7376/6043 6735/7379/6002 7852/7380/6006 +f 3327/7377/6044 3332/7381/6046 3328/7362/6032 +f 3332/7381/6046 6747/7382/6047 6748/7366/6036 +f 6754/7383/6048 6755/7384/6049 3332/7381/6046 +f 6755/7384/6049 7850/7385/6050 6747/7382/6047 +f 3333/7386/6051 3337/7387/6052 3338/7388/6053 +f 3334/7389/6054 3338/7388/6053 6746/7359/6029 +f 3337/7387/6052 6757/7390/6055 6758/7391/6056 +f 3338/7388/6053 6758/7391/6056 7849/7360/6030 +f 3333/7386/6051 3334/7389/6054 3339/7392/6057 +f 3335/7393/6058 3339/7392/6057 6759/7394/6059 +f 6745/7358/6028 6744/7353/6023 3339/7392/6057 +f 3339/7392/6057 6744/7353/6023 7855/7355/6025 +f 3335/7393/6058 3340/7395/6060 3336/7396/6061 +f 3340/7395/6060 6762/7397/6062 6763/7398/6063 +f 6760/7399/6064 6761/7400/6065 3340/7395/6060 +f 6761/7400/6065 7857/7401/6066 6762/7397/6062 +f 3333/7386/6051 3336/7396/6061 3341/7402/6067 +f 3341/7402/6067 6756/7403/6068 6757/7390/6055 +f 3336/7396/6061 6763/7398/6063 6764/7404/6069 +f 3341/7402/6067 6764/7404/6069 7847/7405/6070 +f 3346/7406/6071 3347/7407/6072 3343/7408/6073 +f 3347/7407/6072 6768/7409/6074 6769/7410/6075 +f 6766/7411/6076 6767/7412/6077 3347/7407/6072 +f 6767/7412/6077 7858/7413/6078 6768/7409/6074 +f 3342/7414/6079 3343/7408/6073 3348/7415/6080 +f 3348/7415/6080 6749/7416/6037 6748/7417/6036 +f 3343/7408/6073 6769/7410/6075 6770/7418/6081 +f 3348/7415/6080 6770/7418/6081 7856/7419/6038 +f 3342/7414/6079 3344/7420/6082 3349/7421/6083 +f 3345/7422/6084 3349/7421/6083 6771/7423/6085 +f 3344/7420/6082 6748/7417/6036 6747/7424/6047 +f 3349/7421/6083 6747/7424/6047 7850/7425/6050 +f 3342/7414/6079 3345/7422/6084 3350/7426/6086 +f 3346/7406/6071 3350/7426/6086 6765/7427/6087 +f 6772/7428/6088 6773/7429/6089 3350/7426/6086 +f 3350/7426/6086 6773/7429/6089 7848/7430/6090 +f 3355/7431/6091 3356/7432/6092 3352/7433/6093 +f 3356/7432/6092 6764/7404/6069 6763/7398/6063 +f 6775/7434/6094 6776/7435/6095 3356/7432/6092 +f 6776/7435/6095 7847/7405/6070 6764/7404/6069 +f 3351/7436/6096 3352/7433/6093 3357/7437/6097 +f 3353/7438/6098 3357/7437/6097 6777/7439/6099 +f 3352/7433/6093 6763/7398/6063 6762/7397/6062 +f 3357/7437/6097 6762/7397/6062 7857/7401/6066 +f 3353/7438/6098 3358/7440/6100 3354/7441/6101 +f 3358/7440/6100 6780/7442/6102 6781/7443/6103 +f 6778/7444/6104 6779/7445/6105 3358/7440/6100 +f 6779/7445/6105 7859/7446/6106 6780/7442/6102 +f 3351/7436/6096 3354/7441/6101 3359/7447/6107 +f 3355/7431/6091 3359/7447/6107 6774/7448/6108 +f 3354/7441/6101 6781/7443/6103 6782/7449/6109 +f 3359/7447/6107 6782/7449/6109 7845/7450/6110 +f 3364/7451/6111 3365/7452/6112 3361/7453/6113 +f 3365/7452/6112 6786/7454/6114 6787/7455/6115 +f 6784/7456/6116 6785/7457/6117 3365/7452/6112 +f 6785/7457/6117 7860/7458/6118 6786/7454/6114 +f 3360/7459/6119 3361/7453/6113 3366/7460/6120 +f 3362/7461/6121 3366/7460/6120 6767/7412/6077 +f 3361/7453/6113 6787/7455/6115 6788/7462/6122 +f 3366/7460/6120 6788/7462/6122 7858/7413/6078 +f 3362/7461/6121 3367/7463/6123 3363/7464/6124 +f 3367/7463/6123 6789/7465/6125 6790/7466/6126 +f 6766/7411/6076 6765/7427/6087 3367/7463/6123 +f 6765/7427/6087 7848/7430/6090 6789/7465/6125 +f 3360/7459/6119 3363/7464/6124 3368/7467/6127 +f 3364/7451/6111 3368/7467/6127 6783/7468/6128 +f 3363/7464/6124 6790/7466/6126 6791/7469/6129 +f 3368/7467/6127 6791/7469/6129 7846/7470/6130 +f 3373/7471/6131 3374/7472/6132 3370/7473/6133 +f 3374/7472/6132 6782/7449/6109 6781/7443/6103 +f 6793/7474/6134 6794/7475/6135 3374/7472/6132 +f 6794/7475/6135 7845/7450/6110 6782/7449/6109 +f 3369/7476/6136 3370/7473/6133 3375/7477/6137 +f 3371/7478/6138 3375/7477/6137 6795/7479/6139 +f 3370/7473/6133 6781/7443/6103 6780/7442/6102 +f 3375/7477/6137 6780/7442/6102 7859/7446/6106 +f 3371/7478/6138 3376/7480/6140 3372/7481/6141 +f 3372/7481/6141 3376/7480/6140 6798/7482/6142 +f 3371/7478/6138 6796/7483/6143 6797/7484/6144 +f 3376/7480/6140 6797/7484/6144 7861/7485/6145 +f 3369/7476/6136 3372/7481/6141 3377/7486/6146 +f 3373/7471/6131 3377/7486/6146 6792/7487/6147 +f 3372/7481/6141 6799/7488/6148 6800/7489/6149 +f 3377/7486/6146 6800/7489/6149 7843/7490/6150 +f 3382/7491/6151 3383/7492/6152 3379/7493/6153 +f 3379/7493/6153 3383/7492/6152 6804/7494/6154 +f 3382/7491/6151 6802/7495/6155 6803/7496/6156 +f 3383/7492/6152 6803/7496/6156 7862/7497/6157 +f 3378/7498/6158 3379/7493/6153 3384/7499/6159 +f 3380/7500/6160 3384/7499/6159 6785/7501/6117 +f 3379/7493/6153 6805/7502/6161 6806/7503/6162 +f 3384/7499/6159 6806/7503/6162 7860/7504/6118 +f 3380/7500/6160 3385/7505/6163 3381/7506/6164 +f 3385/7505/6163 6807/7507/6165 6808/7508/6166 +f 6784/7509/6116 6783/7510/6128 3385/7505/6163 +f 6783/7510/6128 7846/7511/6130 6807/7507/6165 +f 3378/7498/6158 3381/7506/6164 3386/7512/6167 +f 3382/7491/6151 3386/7512/6167 6801/7513/6168 +f 3381/7506/6164 6808/7508/6166 6809/7514/6169 +f 3386/7512/6167 6809/7514/6169 7844/7515/6170 +f 3391/7516/6171 3392/7517/6172 3388/7518/6173 +f 3392/7517/6172 6800/7489/6149 6799/7488/6148 +f 6811/7519/6174 6812/7520/6175 3392/7517/6172 +f 6812/7520/6175 7843/7490/6150 6800/7489/6149 +f 3388/7518/6173 3393/7521/6176 3389/7522/6177 +f 3393/7521/6176 6813/7523/6178 6814/7524/6179 +f 6799/7488/6148 6798/7482/6142 3393/7521/6176 +f 6798/7482/6142 7861/7485/6145 6813/7523/6178 +f 3387/7525/6180 3389/7522/6177 3394/7526/6181 +f 3390/7527/6182 3394/7526/6181 6816/7528/6183 +f 3389/7522/6177 6814/7524/6179 6815/7529/6184 +f 3394/7526/6181 6815/7529/6184 7863/7530/6185 +f 3390/7527/6182 3395/7531/6186 3391/7516/6171 +f 3391/7516/6171 3395/7531/6186 6810/7532/6187 +f 6817/7533/6188 6818/7534/6189 3395/7531/6186 +f 3395/7531/6186 6818/7534/6189 7841/7535/6190 +f 3396/7536/6191 3400/7537/6192 3401/7538/6193 +f 3397/7539/6194 3401/7538/6193 6822/7540/6195 +f 3400/7537/6192 6820/7541/6196 6821/7542/6197 +f 3401/7538/6193 6821/7542/6197 7864/7543/6198 +f 3397/7539/6194 3402/7544/6199 3398/7545/6200 +f 3402/7544/6199 6803/7496/6156 6802/7495/6155 +f 6823/7546/6201 6824/7547/6202 3402/7544/6199 +f 6824/7547/6202 7862/7497/6157 6803/7496/6156 +f 3398/7545/6200 3403/7548/6203 3399/7549/6204 +f 3403/7548/6203 6825/7550/6205 6826/7551/6206 +f 6802/7495/6155 6801/7513/6168 3403/7548/6203 +f 6801/7513/6168 7844/7515/6170 6825/7550/6205 +f 3399/7549/6204 3404/7552/6207 3400/7537/6192 +f 3404/7552/6207 6819/7553/6208 6820/7541/6196 +f 3399/7549/6204 6826/7551/6206 6827/7554/6209 +f 3404/7552/6207 6827/7554/6209 7842/7555/6210 +f 3405/7556/6211 3409/7557/6212 3410/7558/6213 +f 3406/7559/6214 3410/7558/6213 6828/7560/6215 +f 3409/7557/6212 6814/7524/6179 6813/7523/6178 +f 3410/7558/6213 6813/7523/6178 7861/7485/6145 +f 3406/7559/6214 3411/7561/6216 3407/7562/6217 +f 3411/7561/6216 6831/7563/6218 6832/7564/6219 +f 6829/7565/6220 6830/7566/6221 3411/7561/6216 +f 6830/7566/6221 7871/7567/6222 6831/7563/6218 +f 3405/7556/6211 3407/7562/6217 3412/7568/6223 +f 3408/7569/6224 3412/7568/6223 6834/7570/6225 +f 3407/7562/6217 6832/7564/6219 6833/7571/6226 +f 3412/7568/6223 6833/7571/6226 7869/7572/6227 +f 3408/7569/6224 3413/7573/6228 3409/7557/6212 +f 3413/7573/6228 6815/7529/6184 6814/7524/6179 +f 6835/7574/6229 6836/7575/6230 3413/7573/6228 +f 6836/7575/6230 7863/7530/6185 6815/7529/6184 +f 3414/7576/6231 3418/7577/6232 3419/7578/6233 +f 3415/7579/6234 3419/7578/6233 6840/7580/6235 +f 3418/7577/6232 6838/7581/6236 6839/7582/6237 +f 3419/7578/6233 6839/7582/6237 7870/7583/6238 +f 3415/7579/6234 3420/7584/6239 3416/7585/6240 +f 3420/7584/6239 6843/7586/6241 6844/7587/6242 +f 6841/7588/6243 6842/7589/6244 3420/7584/6239 +f 6842/7589/6244 7872/7590/6245 6843/7586/6241 +f 3414/7576/6231 3416/7585/6240 3421/7591/6246 +f 3417/7592/6247 3421/7591/6246 6824/7593/6202 +f 3416/7585/6240 6844/7587/6242 6845/7594/6248 +f 3421/7591/6246 6845/7594/6248 7862/7595/6157 +f 3417/7592/6247 3422/7596/6249 3418/7577/6232 +f 3422/7596/6249 6837/7597/6250 6838/7581/6236 +f 6823/7598/6201 6822/7599/6195 3422/7596/6249 +f 6822/7599/6195 7864/7600/6198 6837/7597/6250 +f 3423/7601/6251 3427/7602/6252 3428/7603/6253 +f 3424/7604/6254 3428/7603/6253 6846/7605/6255 +f 3427/7602/6252 6796/7606/6143 6795/7607/6139 +f 6795/7607/6139 7859/7608/6106 6846/7605/6255 +f 3424/7604/6254 3429/7609/6256 3425/7610/6257 +f 3429/7609/6256 6849/7611/6258 6850/7612/6259 +f 6847/7613/6260 6848/7614/6261 3429/7609/6256 +f 6848/7614/6261 7873/7615/6262 6849/7611/6258 +f 3423/7601/6251 3425/7610/6257 3430/7616/6263 +f 3426/7617/6264 3430/7616/6263 6830/7618/6221 +f 3425/7610/6257 6850/7612/6259 6851/7619/6265 +f 3430/7616/6263 6851/7619/6265 7871/7620/6222 +f 3426/7617/6264 3431/7621/6266 3427/7602/6252 +f 3431/7621/6266 6797/7622/6144 6796/7606/6143 +f 6829/7623/6220 6828/7624/6215 3431/7621/6266 +f 6828/7624/6215 7861/7625/6145 6797/7622/6144 +f 3432/7626/6267 3436/7627/6268 3437/7628/6269 +f 3433/7629/6270 3437/7628/6269 6852/7630/6271 +f 3436/7627/6268 6844/7631/6242 6843/7632/6241 +f 3437/7628/6269 6843/7632/6241 7872/7633/6245 +f 3433/7629/6270 3438/7634/6272 3434/7635/6273 +f 3438/7634/6272 6855/7636/6274 6856/7637/6275 +f 6853/7638/6276 6854/7639/6277 3438/7634/6272 +f 6854/7639/6277 7874/7640/6278 6855/7636/6274 +f 3432/7626/6267 3434/7635/6273 3439/7641/6279 +f 3435/7642/6280 3439/7641/6279 6806/7643/6162 +f 3434/7635/6273 6856/7637/6275 6857/7644/6281 +f 6857/7644/6281 7860/7458/6118 6806/7643/6162 +f 3435/7642/6280 3440/7645/6282 3436/7627/6268 +f 3440/7645/6282 6845/7646/6248 6844/7631/6242 +f 6805/7647/6161 6804/7648/6154 3440/7645/6282 +f 6804/7648/6154 7862/7649/6157 6845/7646/6248 +f 3445/7650/6283 3446/7651/6284 3442/7652/6285 +f 3446/7651/6284 6858/7653/6286 6859/7654/6287 +f 6778/7655/6104 6777/7656/6099 3446/7651/6284 +f 6777/7656/6099 7857/7657/6066 6858/7653/6286 +f 3441/7658/6288 3442/7652/6285 3447/7659/6289 +f 3443/7660/6290 3447/7659/6289 6861/7661/6291 +f 3442/7652/6285 6859/7654/6287 6860/7662/6292 +f 3447/7659/6289 6860/7662/6292 7875/7663/6293 +f 3443/7660/6290 3448/7664/6294 3444/7665/6295 +f 3444/7665/6295 3448/7664/6294 6848/7614/6261 +f 6862/7666/6296 6863/7667/6297 3448/7664/6294 +f 3448/7664/6294 6863/7667/6297 7873/7615/6262 +f 3441/7658/6288 3444/7665/6295 3449/7668/6298 +f 3445/7650/6283 3449/7668/6298 6779/7669/6105 +f 3444/7665/6295 6847/7613/6260 6846/7605/6255 +f 3449/7668/6298 6846/7605/6255 7859/7608/6106 +f 3454/7670/6299 3455/7671/6300 3451/7672/6301 +f 3455/7671/6300 6864/7673/6302 6865/7674/6303 +f 3454/7670/6299 6856/7637/6275 6855/7636/6274 +f 3455/7671/6300 6855/7636/6274 7874/7640/6278 +f 3450/7675/6304 3451/7672/6301 3456/7676/6305 +f 3452/7677/6306 3456/7676/6305 6867/7678/6307 +f 3451/7672/6301 6865/7674/6303 6866/7679/6308 +f 3456/7676/6305 6866/7679/6308 7876/7680/6309 +f 3452/7677/6306 3457/7681/6310 3453/7682/6311 +f 3457/7681/6310 6788/7462/6122 6787/7455/6115 +f 6868/7683/6312 6869/7684/6313 3457/7681/6310 +f 6869/7684/6313 7858/7413/6078 6788/7462/6122 +f 3450/7675/6304 3453/7682/6311 3458/7685/6314 +f 3454/7670/6299 3458/7685/6314 6857/7644/6281 +f 3453/7682/6311 6787/7455/6115 6786/7454/6114 +f 3458/7685/6314 6786/7454/6114 7860/7458/6118 +f 3463/7686/6315 3464/7687/6316 3460/7688/6317 +f 3464/7687/6316 6870/7689/6318 6871/7690/6319 +f 6760/7691/6064 6759/7692/6059 3464/7687/6316 +f 6759/7692/6059 7855/7693/6025 6870/7689/6318 +f 3459/7694/6320 3460/7688/6317 3465/7695/6321 +f 3461/7696/6322 3465/7695/6321 6873/7697/6323 +f 3460/7688/6317 6871/7690/6319 6872/7698/6324 +f 3465/7695/6321 6872/7698/6324 7877/7699/6325 +f 3461/7696/6322 3466/7700/6326 3462/7701/6327 +f 3466/7700/6326 6860/7702/6292 6859/7703/6287 +f 6874/7704/6328 6875/7705/6329 3466/7700/6326 +f 6875/7705/6329 7875/7706/6293 6860/7702/6292 +f 3459/7694/6320 3462/7701/6327 3467/7707/6330 +f 3463/7686/6315 3467/7707/6330 6761/7708/6065 +f 3462/7701/6327 6859/7703/6287 6858/7709/6286 +f 3467/7707/6330 6858/7709/6286 7857/7710/6066 +f 3472/7711/6331 3473/7712/6332 3469/7713/6333 +f 3473/7712/6332 6876/7714/6334 6877/7715/6335 +f 6868/7683/6312 6867/7678/6307 3473/7712/6332 +f 6867/7678/6307 7876/7680/6309 6876/7714/6334 +f 3468/7716/6336 3469/7713/6333 3474/7717/6337 +f 3470/7718/6338 3474/7717/6337 6879/7719/6339 +f 3469/7713/6333 6877/7715/6335 6878/7720/6340 +f 3474/7717/6337 6878/7720/6340 7878/7721/6341 +f 3470/7718/6338 3475/7722/6342 3471/7723/6343 +f 3475/7722/6342 6770/7418/6081 6769/7410/6075 +f 6880/7724/6344 6881/7725/6345 3475/7722/6342 +f 6881/7725/6345 7856/7419/6038 6770/7418/6081 +f 3468/7716/6336 3471/7723/6343 3476/7726/6346 +f 3472/7711/6331 3476/7726/6346 6869/7684/6313 +f 3471/7723/6343 6769/7410/6075 6768/7409/6074 +f 3476/7726/6346 6768/7409/6074 7858/7413/6078 +f 3481/7727/6347 3482/7728/6348 3478/7729/6349 +f 3482/7728/6348 6882/7730/6350 6883/7731/6351 +f 3481/7727/6347 6742/7350/6020 6741/7349/6019 +f 6741/7349/6019 7853/7315/5985 6882/7730/6350 +f 3477/7732/6352 3478/7729/6349 3483/7733/6353 +f 3479/7734/6354 3483/7733/6353 6885/7735/6355 +f 3478/7729/6349 6883/7731/6351 6884/7736/6356 +f 3483/7733/6353 6884/7736/6356 7879/7737/6357 +f 3479/7734/6354 3484/7738/6358 3480/7739/6359 +f 3484/7738/6358 6872/7740/6324 6871/7741/6319 +f 6886/7742/6360 6887/7743/6361 3484/7738/6358 +f 6887/7743/6361 7877/7744/6325 6872/7740/6324 +f 3477/7732/6352 3480/7739/6359 3485/7745/6362 +f 3481/7727/6347 3485/7745/6362 6743/7354/6024 +f 3480/7739/6359 6871/7741/6319 6870/7746/6318 +f 3485/7745/6362 6870/7746/6318 7855/7355/6025 +f 3490/7747/6363 3491/7748/6364 3487/7749/6365 +f 3491/7748/6364 6888/7750/6366 6889/7751/6367 +f 6880/7752/6344 6879/7753/6339 3491/7748/6364 +f 6879/7753/6339 7878/7754/6341 6888/7750/6366 +f 3486/7755/6368 3487/7749/6365 3492/7756/6369 +f 3488/7757/6370 3492/7756/6369 6891/7758/6371 +f 3487/7749/6365 6889/7751/6367 6890/7759/6372 +f 3492/7756/6369 6890/7759/6372 7880/7760/6373 +f 3488/7757/6370 3493/7761/6374 3489/7762/6375 +f 3489/7762/6375 3493/7761/6374 6752/7763/6042 +f 6892/7764/6376 6893/7765/6377 3493/7761/6374 +f 6893/7765/6377 7854/7766/6010 6752/7763/6042 +f 3486/7755/6368 3489/7762/6375 3494/7767/6378 +f 3490/7747/6363 3494/7767/6378 6881/7768/6345 +f 3489/7762/6375 6751/7769/6041 6750/7770/6035 +f 3494/7767/6378 6750/7770/6035 7856/7771/6038 +f 3495/7772/6379 3499/7773/6380 3500/7774/6381 +f 3496/7775/6382 3500/7774/6381 6725/7314/5984 +f 3499/7773/6380 6883/7731/6351 6882/7730/6350 +f 3500/7774/6381 6882/7730/6350 7853/7315/5985 +f 3496/7775/6382 3501/7776/6383 3497/7777/6384 +f 3501/7776/6383 6894/7778/6385 6895/7779/6386 +f 6724/7308/5978 6723/7307/5977 3501/7776/6383 +f 6723/7307/5977 7865/7311/5981 6894/7778/6385 +f 3495/7772/6379 3497/7777/6384 3502/7780/6387 +f 3498/7781/6388 3502/7780/6387 6897/7782/6389 +f 3497/7777/6384 6895/7779/6386 6896/7783/6390 +f 3502/7780/6387 6896/7783/6390 7867/7784/6391 +f 3498/7781/6388 3503/7785/6392 3499/7773/6380 +f 3503/7785/6392 6884/7736/6356 6883/7731/6351 +f 6898/7786/6393 6899/7787/6394 3503/7785/6392 +f 6899/7787/6394 7879/7737/6357 6884/7736/6356 +f 3504/7788/6395 3508/7789/6396 3509/7790/6397 +f 3505/7791/6398 3509/7790/6397 6903/7792/6399 +f 3508/7789/6396 6901/7793/6400 6902/7794/6401 +f 3509/7790/6397 6902/7794/6401 7868/7795/6402 +f 3505/7791/6398 3510/7796/6403 3506/7797/6404 +f 3510/7796/6403 6728/7798/5992 6727/7799/5991 +f 6904/7800/6405 6905/7801/6406 3510/7796/6403 +f 6905/7801/6406 7866/7802/5993 6728/7798/5992 +f 3504/7788/6395 3506/7797/6404 3511/7803/6407 +f 3507/7804/6408 3511/7803/6407 6893/7765/6377 +f 3506/7797/6404 6727/7799/5991 6726/7805/6008 +f 3511/7803/6407 6726/7805/6008 7854/7766/6010 +f 3507/7804/6408 3512/7806/6409 3508/7789/6396 +f 3512/7806/6409 6900/7807/6410 6901/7793/6400 +f 6892/7764/6376 6891/7758/6371 3512/7806/6409 +f 6891/7758/6371 7880/7760/6373 6900/7807/6410 +f 3517/7808/6411 3518/7809/6412 3514/7810/6413 +f 3514/7810/6413 3518/7809/6412 6638/6989/5714 +f 6907/7811/6414 6908/7812/6415 3518/7809/6412 +f 6908/7812/6415 7769/6062/4960 6638/6989/5714 +f 3514/7810/6413 3519/7813/6416 3515/7814/6417 +f 3519/7813/6416 6909/7815/6418 6910/7816/6419 +f 6637/6988/5713 6636/7000/5725 3519/7813/6416 +f 6636/7000/5725 7771/7002/5727 6909/7815/6418 +f 3515/7814/6417 3520/7817/6420 3516/7818/6421 +f 3520/7817/6420 6912/7819/6422 6913/7820/6423 +f 6910/7816/6419 6911/7821/6424 3520/7817/6420 +f 6911/7821/6424 7895/7822/6425 6912/7819/6422 +f 3513/7823/6426 3516/7818/6421 3521/7824/6427 +f 3521/7824/6427 6906/7825/6428 6907/7811/6414 +f 3516/7818/6421 6913/7820/6423 6914/7826/6429 +f 3521/7824/6427 6914/7826/6429 7893/7827/6430 +f 3526/7828/6431 3527/7829/6432 3523/7830/6433 +f 3527/7829/6432 6918/7831/6434 6919/7832/6435 +f 6916/7833/6436 6917/7834/6437 3527/7829/6432 +f 6917/7834/6437 7896/7835/6438 6918/7831/6434 +f 3523/7830/6433 3528/7836/6439 3524/7837/6440 +f 3528/7836/6439 6653/7020/5745 6652/7015/5740 +f 6919/7832/6435 6920/7838/6441 3528/7836/6439 +f 6920/7838/6441 7772/7021/5746 6653/7020/5745 +f 3524/7837/6440 3529/7839/6442 3525/7840/6443 +f 3529/7839/6442 6921/7841/6444 6922/7842/6445 +f 3524/7837/6440 6652/7015/5740 6651/7014/5739 +f 6651/7014/5739 7770/6075/4973 6921/7841/6444 +f 3522/7843/6446 3525/7840/6443 3530/7844/6447 +f 3526/7828/6431 3530/7844/6447 6915/7845/6448 +f 6922/7842/6445 6923/7846/6449 3530/7844/6447 +f 3530/7844/6447 6923/7846/6449 7894/7847/6450 +f 3535/7848/6451 3536/7849/6452 3532/7850/6453 +f 3536/7849/6452 6924/7851/6454 6925/7852/6455 +f 6910/7853/6419 6909/7854/6418 3536/7849/6452 +f 6909/7854/6418 7771/7855/5727 6924/7851/6454 +f 3531/7856/6456 3532/7850/6453 3537/7857/6457 +f 3533/7858/6458 3537/7857/6457 6818/7534/6189 +f 3532/7850/6453 6925/7852/6455 6926/7859/6459 +f 3537/7857/6457 6926/7859/6459 7841/7535/6190 +f 3533/7858/6458 3538/7860/6460 3534/7861/6461 +f 3538/7860/6460 6927/7862/6462 6928/7863/6463 +f 6817/7533/6188 6816/7528/6183 3538/7860/6460 +f 6816/7528/6183 7863/7530/6185 6927/7862/6462 +f 3531/7856/6456 3534/7861/6461 3539/7864/6464 +f 3535/7848/6451 3539/7864/6464 6911/7865/6424 +f 3534/7861/6461 6928/7863/6463 6929/7866/6465 +f 3539/7864/6464 6929/7866/6465 7895/7867/6425 +f 3544/7868/6466 3545/7869/6467 3541/7870/6468 +f 3545/7869/6467 6821/7871/6197 6820/7872/6196 +f 6931/7873/6469 6932/7874/6470 3545/7869/6467 +f 6932/7874/6470 7864/7875/6198 6821/7871/6197 +f 3540/7876/6471 3541/7870/6468 3546/7877/6472 +f 3542/7878/6473 3546/7877/6472 6933/7879/6474 +f 3541/7870/6468 6820/7872/6196 6819/7880/6208 +f 3546/7877/6472 6819/7880/6208 7842/7881/6210 +f 3542/7878/6473 3547/7882/6475 3543/7883/6476 +f 3547/7882/6475 6920/7884/6441 6919/7885/6435 +f 6934/7886/6477 6935/7887/6478 3547/7882/6475 +f 6935/7887/6478 7772/7888/5746 6920/7884/6441 +f 3540/7876/6471 3543/7883/6476 3548/7889/6479 +f 3544/7868/6466 3548/7889/6479 6930/7890/6480 +f 3543/7883/6476 6919/7885/6435 6918/7891/6434 +f 3548/7889/6479 6918/7891/6434 7896/7892/6438 +f 3549/7893/6481 3553/7894/6482 3554/7895/6483 +f 3550/7896/6484 3554/7895/6483 6356/6066/4964 +f 3553/7894/6482 6589/7897/5590 6588/7898/5585 +f 3554/7895/6483 6588/7898/5585 7761/6067/4965 +f 3550/7896/6484 3555/7899/6485 3551/7900/6486 +f 3555/7899/6485 6908/7812/6415 6907/7811/6414 +f 6355/6065/4963 6354/6060/4958 3555/7899/6485 +f 6354/6060/4958 7769/6062/4960 6908/7812/6415 +f 3549/7893/6481 3551/7900/6486 3556/7901/6487 +f 3552/7902/6488 3556/7901/6487 6936/7903/6489 +f 3551/7900/6486 6907/7811/6414 6906/7825/6428 +f 3556/7901/6487 6906/7825/6428 7893/7827/6430 +f 3552/7902/6488 3557/7904/6490 3553/7894/6482 +f 3557/7904/6490 6590/7905/5591 6589/7897/5590 +f 6937/7906/6491 6938/7907/6492 3557/7904/6490 +f 6938/7907/6492 7763/7908/5592 6590/7905/5591 +f 3558/7909/6493 3562/7910/6494 3563/7911/6495 +f 3559/7912/6496 3563/7911/6495 6923/7913/6449 +f 3562/7910/6494 6940/7914/6497 6941/7915/6498 +f 3563/7911/6495 6941/7915/6498 7894/7916/6450 +f 3559/7912/6496 3564/7917/6499 3560/7918/6500 +f 3564/7917/6499 6359/7919/4972 6358/7920/4971 +f 6922/7921/6445 6921/7922/6444 3564/7917/6499 +f 6921/7922/6444 7770/7923/4973 6359/7919/4972 +f 3558/7909/6493 3560/7918/6500 3565/7924/6501 +f 3561/7925/6502 3565/7924/6501 6599/7926/5611 +f 3560/7918/6500 6358/7920/4971 6357/7927/4982 +f 3565/7924/6501 6357/7927/4982 7762/7928/4985 +f 3561/7925/6502 3566/7929/6503 3562/7910/6494 +f 3566/7929/6503 6939/7930/6504 6940/7914/6497 +f 6598/7931/5605 6597/7932/5604 3566/7929/6503 +f 6597/7932/5604 7764/7933/5608 6939/7930/6504 +f 3567/7934/6505 3571/7935/6506 3572/7936/6507 +f 3568/7937/6508 3572/7936/6507 6945/7938/6509 +f 3571/7935/6506 6943/7939/6510 6944/7940/6511 +f 3572/7936/6507 6944/7940/6511 7833/7941/5624 +f 3568/7937/6508 3573/7942/6512 3569/7943/6513 +f 3573/7942/6512 6948/7944/6514 6949/7945/6515 +f 6946/7946/6516 6947/7947/6517 3573/7942/6512 +f 6947/7947/6517 7881/7948/6518 6948/7944/6514 +f 3567/7934/6505 3569/7943/6513 3574/7949/6519 +f 3570/7950/6520 3574/7949/6519 6722/7310/5980 +f 3569/7943/6513 6949/7945/6515 6950/7951/6521 +f 3574/7949/6519 6950/7951/6521 7865/7311/5981 +f 3570/7950/6520 3575/7952/6522 3571/7935/6506 +f 3575/7952/6522 6942/7953/6523 6943/7939/6510 +f 6721/7309/5979 6720/7302/5972 3575/7952/6522 +f 6720/7302/5972 7839/7304/5974 6942/7953/6523 +f 3576/7954/6524 3580/7955/6525 3581/7956/6526 +f 3577/7957/6527 3581/7956/6526 6951/7958/6528 +f 3580/7955/6525 6730/7320/5990 6729/7319/5989 +f 3581/7956/6526 6729/7319/5989 7866/7323/5993 +f 3577/7957/6527 3582/7959/6529 3578/7960/6530 +f 3582/7959/6529 6954/7961/6531 6955/7962/6532 +f 6952/7963/6533 6953/7964/6534 3582/7959/6529 +f 6953/7964/6534 7882/7965/6535 6954/7961/6531 +f 3576/7954/6524 3578/7960/6530 3583/7966/6536 +f 3579/7967/6537 3583/7966/6536 6957/7968/6538 +f 3578/7960/6530 6955/7962/6532 6956/7969/6539 +f 3583/7966/6536 6956/7969/6539 7834/7970/5640 +f 3579/7967/6537 3584/7971/6540 3580/7955/6525 +f 3584/7971/6540 6731/7328/5998 6730/7320/5990 +f 6958/7972/6541 6959/7973/6542 3584/7971/6540 +f 6959/7973/6542 7840/7329/5999 6731/7328/5998 +f 3585/7974/6543 3589/7975/6544 3590/7976/6545 +f 3586/7977/6546 3590/7976/6545 6836/7978/6230 +f 3589/7975/6544 6928/7979/6463 6927/7980/6462 +f 3590/7976/6545 6927/7980/6462 7863/7981/6185 +f 3586/7977/6546 3591/7982/6547 3587/7983/6548 +f 3591/7982/6547 6960/7984/6549 6961/7985/6550 +f 6835/7986/6229 6834/7987/6225 3591/7982/6547 +f 6834/7987/6225 7869/7988/6227 6960/7984/6549 +f 3585/7974/6543 3587/7983/6548 3592/7989/6551 +f 3588/7990/6552 3592/7989/6551 6963/7991/6553 +f 3587/7983/6548 6961/7985/6550 6962/7992/6554 +f 6962/7992/6554 7891/7993/6555 6963/7991/6553 +f 3588/7990/6552 3593/7994/6556 3589/7975/6544 +f 3593/7994/6556 6929/7995/6465 6928/7979/6463 +f 6964/7996/6557 6965/7997/6558 3593/7994/6556 +f 6965/7997/6558 7895/7998/6425 6929/7995/6465 +f 3594/7999/6559 3598/8000/6560 3599/8001/6561 +f 3595/8002/6562 3599/8001/6561 6969/8003/6563 +f 3598/8000/6560 6967/8004/6564 6968/8005/6565 +f 6968/8005/6565 7892/8006/6566 6969/8003/6563 +f 3595/8002/6562 3600/8007/6567 3596/8008/6568 +f 3600/8007/6567 6839/7582/6237 6838/7581/6236 +f 6970/8009/6569 6971/8010/6570 3600/8007/6567 +f 6971/8010/6570 7870/7583/6238 6839/7582/6237 +f 3594/7999/6559 3596/8008/6568 3601/8011/6571 +f 3597/8012/6572 3601/8011/6571 6932/8013/6470 +f 3596/8008/6568 6838/7581/6236 6837/7597/6250 +f 3601/8011/6571 6837/7597/6250 7864/7600/6198 +f 3597/8012/6572 3602/8014/6573 3598/8000/6560 +f 3602/8014/6573 6966/8015/6574 6967/8004/6564 +f 6931/8016/6469 6930/8017/6480 3602/8014/6573 +f 6930/8017/6480 7896/8018/6438 6966/8015/6574 +f 3607/8019/6575 3608/8020/6576 3604/8021/6577 +f 3608/8020/6576 6975/8022/6578 6976/8023/6579 +f 6973/8024/6580 6974/8025/6581 3608/8020/6576 +f 6974/8025/6581 7889/8026/6582 6975/8022/6578 +f 3603/8027/6583 3604/8021/6577 3609/8028/6584 +f 3605/8029/6585 3609/8028/6584 6978/8030/6586 +f 3604/8021/6577 6976/8023/6579 6977/8031/6587 +f 3609/8028/6584 6977/8031/6587 7897/8032/6588 +f 3605/8029/6585 3610/8033/6589 3606/8034/6590 +f 3610/8033/6589 6965/8035/6558 6964/8036/6557 +f 6979/8037/6591 6980/8038/6592 3610/8033/6589 +f 6980/8038/6592 7895/8039/6425 6965/8035/6558 +f 3603/8027/6583 3606/8034/6590 3611/8040/6593 +f 3607/8019/6575 3611/8040/6593 6972/8041/6594 +f 3606/8034/6590 6964/8036/6557 6963/8042/6553 +f 3611/8040/6593 6963/8042/6553 7891/8043/6555 +f 3616/8044/6595 3617/8045/6596 3613/8046/6597 +f 3617/8045/6596 6981/8047/6598 6982/8048/6599 +f 6967/8049/6564 6966/8050/6574 3617/8045/6596 +f 6966/8050/6574 7896/8051/6438 6981/8047/6598 +f 3612/8052/6600 3613/8046/6597 3618/8053/6601 +f 3614/8054/6602 3618/8053/6601 6984/8055/6603 +f 3613/8046/6597 6982/8048/6599 6983/8056/6604 +f 3618/8053/6601 6983/8056/6604 7898/8057/6605 +f 3614/8054/6602 3619/8058/6606 3615/8059/6607 +f 3619/8058/6606 6987/8060/6608 6988/8061/6609 +f 6985/8062/6610 6986/8063/6611 3619/8058/6606 +f 6986/8063/6611 7890/8064/6612 6987/8060/6608 +f 3612/8052/6600 3615/8059/6607 3620/8065/6613 +f 3616/8044/6595 3620/8065/6613 6968/8066/6565 +f 3615/8059/6607 6988/8061/6609 6989/8067/6614 +f 3620/8065/6613 6989/8067/6614 7892/8068/6566 +f 3621/8069/6615 3625/8070/6616 3626/8071/6617 +f 3626/8071/6617 6993/8072/6618 6994/8073/6619 +f 3625/8070/6616 6991/8074/6620 6992/8075/6621 +f 6992/8075/6621 7885/8076/6622 6993/8072/6618 +f 3622/8077/6623 3627/8078/6624 3623/8079/6625 +f 3627/8078/6624 6977/8031/6587 6976/8023/6579 +f 6994/8073/6619 6995/8080/6626 3627/8078/6624 +f 6995/8080/6626 7897/8032/6588 6977/8031/6587 +f 3621/8069/6615 3623/8079/6625 3628/8081/6627 +f 3624/8082/6628 3628/8081/6627 6996/8083/6629 +f 3623/8079/6625 6976/8023/6579 6975/8022/6578 +f 3628/8081/6627 6975/8022/6578 7889/8026/6582 +f 3624/8082/6628 3629/8084/6630 3625/8070/6616 +f 3629/8084/6630 6990/8085/6631 6991/8074/6620 +f 3624/8082/6628 6997/8086/6632 6998/8087/6633 +f 3629/8084/6630 6998/8087/6633 7887/8088/6634 +f 3630/8089/6635 3634/8090/6636 3635/8091/6637 +f 3631/8092/6638 3635/8091/6637 6986/8093/6611 +f 3634/8090/6636 7000/8094/6639 7001/8095/6640 +f 3635/8091/6637 7001/8095/6640 7890/8096/6612 +f 3631/8092/6638 3636/8097/6641 3632/8098/6642 +f 3636/8097/6641 7002/8099/6643 7003/8100/6644 +f 6985/8101/6610 6984/8102/6603 3636/8097/6641 +f 6984/8102/6603 7898/8103/6605 7002/8099/6643 +f 3630/8089/6635 3632/8098/6642 3637/8104/6645 +f 3633/8105/6646 3637/8104/6645 7005/8106/6647 +f 7003/8100/6644 7004/8107/6648 3637/8104/6645 +f 7004/8107/6648 7886/8108/6649 7005/8106/6647 +f 3633/8105/6646 3638/8109/6650 3634/8090/6636 +f 3634/8090/6636 3638/8109/6650 6999/8110/6651 +f 7006/8111/6652 7007/8112/6653 3638/8109/6650 +f 3638/8109/6650 7007/8112/6653 7888/8113/6654 +f 3643/8114/6655 3644/8115/6656 3640/8116/6657 +f 3644/8115/6656 7011/8117/6658 7012/8118/6659 +f 7009/8119/6660 7010/8120/6661 3644/8115/6656 +f 7010/8120/6661 7883/8121/6662 7011/8117/6658 +f 3639/8122/6663 3640/8116/6657 3645/8123/6664 +f 3645/8123/6664 7014/8124/6665 7015/8125/6666 +f 7012/8118/6659 7013/8126/6667 3645/8123/6664 +f 7013/8126/6667 7899/8127/6668 7014/8124/6665 +f 3641/8128/6669 3646/8129/6670 3642/8130/6671 +f 3646/8129/6670 6995/8131/6626 6994/8132/6619 +f 7015/8125/6666 7016/8133/6672 3646/8129/6670 +f 7016/8133/6672 7897/8134/6588 6995/8131/6626 +f 3639/8122/6663 3642/8130/6671 3647/8135/6673 +f 3643/8114/6655 3647/8135/6673 7008/8136/6674 +f 3642/8130/6671 6994/8132/6619 6993/8137/6618 +f 3647/8135/6673 6993/8137/6618 7885/8138/6622 +f 3652/8139/6675 3653/8140/6676 3649/8141/6677 +f 3653/8140/6676 7017/8142/6678 7018/8143/6679 +f 7003/8144/6644 7002/8145/6643 3653/8140/6676 +f 7002/8145/6643 7898/8146/6605 7017/8142/6678 +f 3648/8147/6680 3649/8141/6677 3654/8148/6681 +f 3654/8148/6681 7020/8149/6682 7021/8150/6683 +f 7018/8143/6679 7019/8151/6684 3654/8148/6681 +f 7019/8151/6684 7900/8152/6685 7020/8149/6682 +f 3650/8153/6686 3655/8154/6687 3651/8155/6688 +f 3655/8154/6687 7023/8156/6689 7024/8157/6690 +f 7021/8150/6683 7022/8158/6691 3655/8154/6687 +f 7022/8158/6691 7884/8159/6692 7023/8156/6689 +f 3648/8147/6680 3651/8155/6688 3656/8160/6693 +f 3652/8139/6675 3656/8160/6693 7004/8161/6648 +f 3651/8155/6688 7024/8157/6690 7025/8162/6694 +f 3656/8160/6693 7025/8162/6694 7886/8163/6649 +f 3661/8164/6695 3662/8165/6696 3658/8166/6697 +f 3662/8165/6696 7029/8167/6698 7030/8168/6699 +f 7027/8169/6700 7028/8170/6701 3662/8165/6696 +f 7028/8170/6701 7881/7948/6518 7029/8167/6698 +f 3657/8171/6702 3658/8166/6697 3663/8172/6703 +f 3659/8173/6704 3663/8172/6703 7013/8174/6667 +f 3658/8166/6697 7030/8168/6699 7031/8175/6705 +f 3663/8172/6703 7031/8175/6705 7899/8176/6668 +f 3659/8173/6704 3664/8177/6706 3660/8178/6707 +f 3664/8177/6706 7032/8179/6708 7033/8180/6709 +f 7012/8181/6659 7011/8182/6658 3664/8177/6706 +f 3664/8177/6706 7011/8182/6658 7883/8183/6662 +f 3657/8171/6702 3660/8178/6707 3665/8184/6710 +f 3661/8164/6695 3665/8184/6710 7026/8185/6711 +f 3660/8178/6707 7033/8180/6709 7034/8186/6712 +f 3665/8184/6710 7034/8186/6712 7901/8187/6713 +f 3670/8188/6714 3671/8189/6715 3667/8190/6716 +f 3671/8189/6715 7022/8158/6691 7021/8150/6683 +f 7036/8191/6717 7037/8192/6718 3671/8189/6715 +f 3671/8189/6715 7037/8192/6718 7884/8159/6692 +f 3666/8193/6719 3667/8190/6716 3672/8194/6720 +f 3668/8195/6721 3672/8194/6720 7038/8196/6722 +f 3667/8190/6716 7021/8150/6683 7020/8149/6682 +f 3672/8194/6720 7020/8149/6682 7900/8152/6685 +f 3668/8195/6721 3673/8197/6723 3669/8198/6724 +f 3673/8197/6723 7041/8199/6725 7042/8200/6726 +f 7039/8201/6727 7040/8202/6728 3673/8197/6723 +f 7040/8202/6728 7882/8203/6535 7041/8199/6725 +f 3666/8193/6719 3669/8198/6724 3674/8204/6729 +f 3670/8188/6714 3674/8204/6729 7035/8205/6730 +f 3669/8198/6724 7042/8200/6726 7043/8206/6731 +f 3674/8204/6729 7043/8206/6731 7902/8207/6732 +f 3675/8208/6733 3679/8209/6734 3680/8210/6735 +f 3680/8210/6735 6950/7951/6521 6949/7945/6515 +f 3679/8209/6734 6895/7779/6386 6894/7778/6385 +f 3680/8210/6735 6894/7778/6385 7865/7311/5981 +f 3675/8208/6733 3676/8211/6736 3681/8212/6737 +f 3677/8213/6738 3681/8212/6737 7028/8170/6701 +f 3676/8211/6736 6949/7945/6515 6948/7944/6514 +f 3681/8212/6737 6948/7944/6514 7881/7948/6518 +f 3675/8208/6733 3677/8213/6738 3682/8214/6739 +f 3678/8215/6740 3682/8214/6739 7044/8216/6741 +f 7027/8169/6700 7026/8185/6711 3682/8214/6739 +f 7026/8185/6711 7901/8187/6713 7044/8216/6741 +f 3678/8215/6740 3683/8217/6742 3679/8209/6734 +f 3683/8217/6742 6896/7783/6390 6895/7779/6386 +f 7045/8218/6743 7046/8219/6744 3683/8217/6742 +f 7046/8219/6744 7867/7784/6391 6896/7783/6390 +f 3684/8220/6745 3688/8221/6746 3689/8222/6747 +f 3689/8222/6747 7043/8206/6731 7042/8200/6726 +f 3688/8221/6746 7048/8223/6748 7049/8224/6749 +f 7049/8224/6749 7902/8207/6732 7043/8206/6731 +f 3684/8220/6745 3685/8225/6750 3690/8226/6751 +f 3686/8227/6752 3690/8226/6751 6953/8228/6534 +f 3685/8225/6750 7042/8200/6726 7041/8199/6725 +f 3690/8226/6751 7041/8199/6725 7882/8203/6535 +f 3684/8220/6745 3686/8227/6752 3691/8229/6753 +f 3687/8230/6754 3691/8229/6753 6905/7801/6406 +f 6952/8231/6533 6951/8232/6528 3691/8229/6753 +f 3691/8229/6753 6951/8232/6528 7866/7802/5993 +f 3687/8230/6754 3692/8233/6755 3688/8221/6746 +f 3692/8233/6755 7047/8234/6756 7048/8223/6748 +f 6904/7800/6405 6903/7792/6399 3692/8233/6755 +f 6903/7792/6399 7868/7795/6402 7047/8234/6756 +f 3693/8235/6757 3697/8236/6758 3698/8237/6759 +f 3694/8238/6760 3698/8237/6759 7050/8239/6761 +f 6601/8240/5622 6600/8241/5617 3698/8237/6759 +f 3698/8237/6759 6600/8241/5617 7763/7908/5592 +f 3694/8238/6760 3699/8242/6762 3695/8243/6763 +f 3699/8242/6762 7031/8244/6705 7030/8245/6699 +f 7051/8246/6764 7052/8247/6765 3699/8242/6762 +f 7052/8247/6765 7899/8127/6668 7031/8244/6705 +f 3693/8235/6757 3695/8243/6763 3700/8248/6766 +f 3696/8249/6767 3700/8248/6766 6947/8250/6517 +f 3695/8243/6763 7030/8245/6699 7029/8251/6698 +f 3700/8248/6766 7029/8251/6698 7881/8252/6518 +f 3693/8235/6757 3696/8249/6767 3701/8253/6768 +f 3697/8236/6758 3701/8253/6768 6602/8254/5623 +f 6946/8255/6516 6945/8256/6509 3701/8253/6768 +f 6945/8256/6509 7833/8257/5624 6602/8254/5623 +f 3702/8258/6769 3706/8259/6770 3707/8260/6771 +f 3703/8261/6772 3707/8260/6771 7040/8262/6728 +f 3706/8259/6770 6955/8263/6532 6954/8264/6531 +f 3707/8260/6771 6954/8264/6531 7882/8265/6535 +f 3703/8261/6772 3708/8266/6773 3704/8267/6774 +f 3708/8266/6773 7053/8268/6775 7054/8269/6776 +f 7039/8270/6727 7038/8271/6722 3708/8266/6773 +f 7038/8271/6722 7900/8272/6685 7053/8268/6775 +f 3702/8258/6769 3704/8267/6774 3709/8273/6777 +f 3709/8273/6777 6611/6901/5643 6610/6895/5637 +f 3704/8267/6774 7054/8269/6776 7055/8274/6778 +f 3709/8273/6777 7055/8274/6778 7764/6862/5608 +f 3702/8258/6769 3705/8275/6779 3710/8276/6780 +f 3710/8276/6780 6956/8277/6539 6955/8263/6532 +f 3705/8275/6779 6610/6895/5637 6609/6894/5636 +f 6609/6894/5636 7834/6898/5640 6956/8277/6539 +f 3715/8278/6781 3716/8279/6782 3712/8280/6783 +f 3716/8279/6782 6938/7907/6492 6937/7906/6491 +f 7051/8246/6764 7050/8239/6761 3716/8279/6782 +f 7050/8239/6761 7763/7908/5592 6938/7907/6492 +f 3711/8281/6784 3712/8280/6783 3717/8282/6785 +f 3713/8283/6786 3717/8282/6785 7056/8284/6787 +f 3712/8280/6783 6937/7906/6491 6936/7903/6489 +f 3717/8282/6785 6936/7903/6489 7893/7827/6430 +f 3713/8283/6786 3718/8285/6788 3714/8286/6789 +f 3718/8285/6788 7016/8133/6672 7015/8125/6666 +f 7057/8287/6790 7058/8288/6791 3718/8285/6788 +f 7058/8288/6791 7897/8134/6588 7016/8133/6672 +f 3711/8281/6784 3714/8286/6789 3719/8289/6792 +f 3715/8278/6781 3719/8289/6792 7052/8247/6765 +f 3714/8286/6789 7015/8125/6666 7014/8124/6665 +f 3719/8289/6792 7014/8124/6665 7899/8127/6668 +f 3724/8290/6793 3725/8291/6794 3721/8292/6795 +f 3725/8291/6794 7059/8293/6796 7060/8294/6797 +f 7018/8143/6679 7017/8142/6678 3725/8291/6794 +f 7017/8142/6678 7898/8146/6605 7059/8293/6796 +f 3720/8295/6798 3721/8292/6795 3726/8296/6799 +f 3722/8297/6800 3726/8296/6799 6941/8298/6498 +f 3721/8292/6795 7060/8294/6797 7061/8299/6801 +f 3726/8296/6799 7061/8299/6801 7894/8300/6450 +f 3722/8297/6800 3727/8301/6802 3723/8302/6803 +f 3727/8301/6802 7055/8303/6778 7054/8304/6776 +f 6940/8305/6497 6939/8306/6504 3727/8301/6802 +f 6939/8306/6504 7764/8307/5608 7055/8303/6778 +f 3720/8295/6798 3723/8302/6803 3728/8308/6804 +f 3724/8290/6793 3728/8308/6804 7019/8151/6684 +f 3723/8302/6803 7054/8304/6776 7053/8309/6775 +f 3728/8308/6804 7053/8309/6775 7900/8152/6685 +f 3729/8310/6805 3732/8311/6806 3733/8312/6807 +f 3733/8312/6807 6914/7826/6429 6913/7820/6423 +f 7057/8287/6790 7056/8284/6787 3733/8312/6807 +f 7056/8284/6787 7893/7827/6430 6914/7826/6429 +f 3729/8310/6805 3730/8313/6808 3734/8314/6809 +f 3731/8315/6810 3734/8314/6809 6980/8316/6592 +f 3730/8313/6808 6913/7820/6423 6912/7819/6422 +f 3734/8314/6809 6912/7819/6422 7895/7822/6425 +f 3729/8310/6805 3731/8315/6810 3735/8317/6811 +f 3735/8317/6811 7058/8288/6791 7057/8287/6790 +f 6979/8318/6591 6978/8319/6586 3735/8317/6811 +f 6978/8319/6586 7897/8134/6588 7058/8288/6791 +f 3736/8320/6812 3739/8321/6813 3740/8322/6814 +f 3740/8322/6814 6983/8323/6604 6982/8324/6599 +f 7060/8325/6797 7059/8326/6796 3740/8322/6814 +f 7059/8326/6796 7898/8327/6605 6983/8323/6604 +f 3736/8320/6812 3737/8328/6815 3741/8329/6816 +f 3738/8330/6817 3741/8329/6816 6917/8331/6437 +f 3737/8328/6815 6982/8324/6599 6981/8332/6598 +f 3741/8329/6816 6981/8332/6598 7896/8333/6438 +f 3736/8320/6812 3738/8330/6817 3742/8334/6818 +f 3742/8334/6818 7061/8335/6801 7060/8325/6797 +f 6916/8336/6436 6915/8337/6448 3742/8334/6818 +f 6915/8337/6448 7894/8338/6450 7061/8335/6801 +f 3743/8339/6819 3747/8340/6820 3748/8341/6821 +f 3744/8342/6822 3748/8341/6821 7046/8343/6744 +f 3747/8340/6820 7063/8344/6823 7064/8345/6824 +f 3748/8341/6821 7064/8345/6824 7867/8346/6391 +f 3744/8342/6822 3749/8347/6825 3745/8348/6826 +f 3749/8347/6825 7065/8349/6827 7066/8350/6828 +f 7045/8351/6743 7044/8352/6741 3749/8347/6825 +f 7044/8352/6741 7901/8353/6713 7065/8349/6827 +f 3743/8339/6819 3745/8348/6826 3750/8354/6829 +f 3746/8355/6830 3750/8354/6829 7068/8356/6831 +f 3745/8348/6826 7066/8350/6828 7067/8357/6832 +f 3750/8354/6829 7067/8357/6832 7903/8358/6833 +f 3746/8355/6830 3751/8359/6834 3747/8340/6820 +f 3751/8359/6834 7062/8360/6835 7063/8344/6823 +f 7069/8361/6836 7070/8362/6837 3751/8359/6834 +f 7070/8362/6837 7927/8363/6838 7062/8360/6835 +f 3752/8364/6839 3756/8365/6840 3757/8366/6841 +f 3753/8367/6842 3757/8366/6841 7074/8368/6843 +f 3756/8365/6840 7072/8369/6844 7073/8370/6845 +f 3757/8366/6841 7073/8370/6845 7904/8371/6846 +f 3753/8367/6842 3758/8372/6847 3754/8373/6848 +f 3758/8372/6847 7049/8224/6749 7048/8223/6748 +f 7075/8374/6849 7076/8375/6850 3758/8372/6847 +f 7076/8375/6850 7902/8207/6732 7049/8224/6749 +f 3752/8364/6839 3754/8373/6848 3759/8376/6851 +f 3755/8377/6852 3759/8376/6851 7077/8378/6853 +f 3754/8373/6848 7048/8223/6748 7047/8234/6756 +f 3759/8376/6851 7047/8234/6756 7868/7795/6402 +f 3755/8377/6852 3760/8379/6854 3756/8365/6840 +f 3760/8379/6854 7071/8380/6855 7072/8369/6844 +f 7078/8381/6856 7079/8382/6857 3760/8379/6854 +f 7079/8382/6857 7928/8383/6858 7071/8380/6855 +f 3765/8384/6859 3766/8385/6860 3762/8386/6861 +f 3766/8385/6860 7034/8387/6712 7033/8388/6709 +f 7066/8350/6828 7065/8349/6827 3766/8385/6860 +f 7065/8349/6827 7901/8353/6713 7034/8387/6712 +f 3761/8389/6862 3762/8386/6861 3767/8390/6863 +f 3767/8390/6863 7080/8391/6864 7081/8392/6865 +f 3762/8386/6861 7033/8388/6709 7032/8393/6708 +f 7032/8393/6708 7883/8121/6662 7080/8391/6864 +f 3763/8394/6866 3768/8395/6867 3764/8396/6868 +f 3768/8395/6867 7083/8397/6869 7084/8398/6870 +f 3763/8394/6866 7081/8392/6865 7082/8399/6871 +f 7082/8399/6871 7913/8400/6872 7083/8397/6869 +f 3761/8389/6862 3764/8396/6868 3769/8401/6873 +f 3769/8401/6873 7067/8357/6832 7066/8350/6828 +f 3764/8396/6868 7084/8398/6870 7085/8402/6874 +f 7085/8402/6874 7903/8358/6833 7067/8357/6832 +f 3774/8403/6875 3775/8404/6876 3771/8405/6877 +f 3771/8405/6877 3775/8404/6876 7089/8406/6878 +f 7087/8407/6879 7088/8408/6880 3775/8404/6876 +f 7088/8408/6880 7914/8409/6881 7089/8406/6878 +f 3770/8410/6882 3771/8405/6877 3776/8411/6883 +f 3772/8412/6884 3776/8411/6883 7037/8192/6718 +f 7090/8413/6885 7091/8414/6886 3776/8411/6883 +f 7091/8414/6886 7884/8159/6692 7037/8192/6718 +f 3772/8412/6884 3777/8415/6887 3773/8416/6888 +f 3777/8415/6887 7076/8375/6850 7075/8374/6849 +f 7036/8191/6717 7035/8205/6730 3777/8415/6887 +f 7035/8205/6730 7902/8207/6732 7076/8375/6850 +f 3770/8410/6882 3773/8416/6888 3778/8417/6889 +f 3774/8403/6875 3778/8417/6889 7086/8418/6890 +f 7075/8374/6849 7074/8368/6843 3778/8417/6889 +f 7074/8368/6843 7904/8371/6846 7086/8418/6890 +f 3779/8419/6891 3783/8420/6892 3784/8421/6893 +f 3780/8422/6894 3784/8421/6893 7010/8120/6661 +f 3783/8420/6892 7081/8392/6865 7080/8391/6864 +f 3784/8421/6893 7080/8391/6864 7883/8121/6662 +f 3780/8422/6894 3785/8423/6895 3781/8424/6896 +f 3785/8423/6895 7092/8425/6897 7093/8426/6898 +f 7009/8119/6660 7008/8136/6674 3785/8423/6895 +f 7008/8136/6674 7885/8138/6622 7092/8425/6897 +f 3779/8419/6891 3781/8424/6896 3786/8427/6899 +f 3782/8428/6900 3786/8427/6899 7095/8429/6901 +f 3781/8424/6896 7093/8426/6898 7094/8430/6902 +f 3786/8427/6899 7094/8430/6902 7911/8431/6903 +f 3782/8428/6900 3787/8432/6904 3783/8420/6892 +f 3787/8432/6904 7082/8399/6871 7081/8392/6865 +f 7096/8433/6905 7097/8434/6906 3787/8432/6904 +f 7097/8434/6906 7913/8400/6872 7082/8399/6871 +f 3788/8435/6907 3792/8436/6908 3793/8437/6909 +f 3789/8438/6910 3793/8437/6909 7101/8439/6911 +f 3792/8436/6908 7099/8440/6912 7100/8441/6913 +f 3793/8437/6909 7100/8441/6913 7912/8442/6914 +f 3789/8438/6910 3794/8443/6915 3790/8444/6916 +f 3794/8443/6915 7025/8162/6694 7024/8157/6690 +f 7102/8445/6917 7103/8446/6918 3794/8443/6915 +f 7103/8446/6918 7886/8163/6649 7025/8162/6694 +f 3788/8435/6907 3790/8444/6916 3795/8447/6919 +f 3791/8448/6920 3795/8447/6919 7091/8414/6886 +f 3790/8444/6916 7024/8157/6690 7023/8156/6689 +f 3795/8447/6919 7023/8156/6689 7884/8159/6692 +f 3791/8448/6920 3796/8449/6921 3792/8436/6908 +f 3796/8449/6921 7098/8450/6922 7099/8440/6912 +f 7090/8413/6885 7089/8406/6878 3796/8449/6921 +f 7089/8406/6878 7914/8409/6881 7098/8450/6922 +f 3801/8451/6923 3802/8452/6924 3798/8453/6925 +f 3802/8452/6924 6992/8454/6621 6991/8455/6620 +f 3801/8451/6923 7093/8456/6898 7092/8457/6897 +f 3802/8452/6924 7092/8457/6897 7885/8458/6622 +f 3797/8459/6926 3798/8453/6925 3803/8460/6927 +f 3799/8461/6928 3803/8460/6927 7104/8462/6929 +f 3798/8453/6925 6991/8455/6620 6990/8463/6631 +f 3803/8460/6927 6990/8463/6631 7887/8464/6634 +f 3799/8461/6928 3804/8465/6930 3800/8466/6931 +f 3804/8465/6930 7107/8467/6932 7108/8468/6933 +f 7105/8469/6934 7106/8470/6935 3804/8465/6930 +f 7106/8470/6935 7909/8471/6936 7107/8467/6932 +f 3797/8459/6926 3800/8466/6931 3805/8472/6937 +f 3805/8472/6937 7094/8473/6902 7093/8456/6898 +f 3800/8466/6931 7108/8468/6933 7109/8474/6938 +f 3805/8472/6937 7109/8474/6938 7911/8475/6903 +f 3810/8476/6939 3811/8477/6940 3807/8478/6941 +f 3811/8477/6940 7113/8479/6942 7114/8480/6943 +f 7111/8481/6944 7112/8482/6945 3811/8477/6940 +f 7112/8482/6945 7910/8483/6946 7113/8479/6942 +f 3806/8484/6947 3807/8478/6941 3812/8485/6948 +f 3808/8486/6949 3812/8485/6948 7007/8112/6653 +f 3807/8478/6941 7114/8480/6943 7115/8487/6950 +f 3812/8485/6948 7115/8487/6950 7888/8113/6654 +f 3808/8486/6949 3813/8488/6951 3809/8489/6952 +f 3809/8489/6952 3813/8488/6951 7103/8490/6918 +f 7006/8111/6652 7005/8106/6647 3813/8488/6951 +f 3813/8488/6951 7005/8106/6647 7886/8108/6649 +f 3806/8484/6947 3809/8489/6952 3814/8491/6953 +f 3810/8476/6939 3814/8491/6953 7110/8492/6954 +f 7102/8493/6917 7101/8494/6911 3814/8491/6953 +f 3814/8491/6953 7101/8494/6911 7912/8495/6914 +f 3819/8496/6955 3820/8497/6956 3816/8498/6957 +f 3820/8497/6956 6998/8087/6633 6997/8086/6632 +f 7105/8499/6934 7104/8500/6929 3820/8497/6956 +f 7104/8500/6929 7887/8088/6634 6998/8087/6633 +f 3815/8501/6958 3816/8498/6957 3821/8502/6959 +f 3817/8503/6960 3821/8502/6959 7116/8504/6961 +f 3816/8498/6957 6997/8086/6632 6996/8083/6629 +f 3821/8502/6959 6996/8083/6629 7889/8026/6582 +f 3817/8503/6960 3822/8505/6962 3818/8506/6963 +f 3818/8506/6963 3822/8505/6962 7119/8507/6964 +f 3817/8503/6960 7117/8508/6965 7118/8509/6966 +f 3822/8505/6962 7118/8509/6966 7907/8510/6967 +f 3815/8501/6958 3818/8506/6963 3823/8511/6968 +f 3819/8496/6955 3823/8511/6968 7106/8512/6935 +f 3818/8506/6963 7120/8513/6969 7121/8514/6970 +f 3823/8511/6968 7121/8514/6970 7909/8515/6936 +f 3828/8516/6971 3829/8517/6972 3825/8518/6973 +f 3825/8518/6973 3829/8517/6972 7125/8519/6974 +f 3828/8516/6971 7123/8520/6975 7124/8521/6976 +f 3829/8517/6972 7124/8521/6976 7908/8522/6977 +f 3824/8523/6978 3825/8518/6973 3830/8524/6979 +f 3826/8525/6980 3830/8524/6979 7001/8095/6640 +f 3825/8518/6973 7126/8526/6981 7127/8527/6982 +f 3830/8524/6979 7127/8527/6982 7890/8096/6612 +f 3826/8525/6980 3831/8528/6983 3827/8529/6984 +f 3831/8528/6983 7115/8487/6950 7114/8480/6943 +f 7000/8094/6639 6999/8110/6651 3831/8528/6983 +f 6999/8110/6651 7888/8113/6654 7115/8487/6950 +f 3824/8523/6978 3827/8529/6984 3832/8530/6985 +f 3828/8516/6971 3832/8530/6985 7122/8531/6986 +f 3827/8529/6984 7114/8480/6943 7113/8479/6942 +f 3832/8530/6985 7113/8479/6942 7910/8483/6946 +f 3833/8532/6987 3837/8533/6988 3838/8534/6989 +f 3834/8535/6990 3838/8534/6989 6974/8536/6581 +f 3837/8533/6988 7117/8537/6965 7116/8538/6961 +f 3838/8534/6989 7116/8538/6961 7889/8539/6582 +f 3834/8535/6990 3839/8540/6991 3835/8541/6992 +f 3839/8540/6991 7128/8542/6993 7129/8543/6994 +f 6973/8544/6580 6972/8545/6594 3839/8540/6991 +f 6972/8545/6594 7891/8546/6555 7128/8542/6993 +f 3833/8532/6987 3835/8541/6992 3840/8547/6995 +f 3836/8548/6996 3840/8547/6995 7131/8549/6997 +f 3835/8541/6992 7129/8543/6994 7130/8550/6998 +f 3840/8547/6995 7130/8550/6998 7905/8551/6999 +f 3836/8548/6996 3841/8552/7000 3837/8533/6988 +f 3841/8552/7000 7118/8553/6966 7117/8537/6965 +f 7132/8554/7001 7133/8555/7002 3841/8552/7000 +f 7133/8555/7002 7907/8556/6967 7118/8553/6966 +f 3842/8557/7003 3846/8558/7004 3847/8559/7005 +f 3843/8560/7006 3847/8559/7005 7137/8561/7007 +f 3846/8558/7004 7135/8562/7008 7136/8563/7009 +f 3847/8559/7005 7136/8563/7009 7906/8564/7010 +f 3843/8560/7006 3848/8565/7011 3844/8566/7012 +f 3848/8565/7011 6989/8067/6614 6988/8061/6609 +f 7138/8567/7013 7139/8568/7014 3848/8565/7011 +f 7139/8568/7014 7892/8068/6566 6989/8067/6614 +f 3842/8557/7003 3844/8566/7012 3849/8569/7015 +f 3845/8570/7016 3849/8569/7015 7127/8571/6982 +f 3844/8566/7012 6988/8061/6609 6987/8060/6608 +f 3849/8569/7015 6987/8060/6608 7890/8064/6612 +f 3845/8570/7016 3850/8572/7017 3846/8558/7004 +f 3850/8572/7017 7134/8573/7018 7135/8562/7008 +f 7126/8574/6981 7125/8575/6974 3850/8572/7017 +f 7125/8575/6974 7908/8576/6977 7134/8573/7018 +f 3851/8577/7019 3855/8578/7020 3856/8579/7021 +f 3852/8580/7022 3856/8579/7021 6962/7992/6554 +f 3855/8578/7020 7129/8581/6994 7128/8582/6993 +f 3856/8579/7021 7128/8582/6993 7891/7993/6555 +f 3851/8577/7019 3852/8580/7022 3857/8583/7023 +f 3853/8584/7024 3857/8583/7023 7140/8585/7025 +f 3852/8580/7022 6961/7985/6550 6960/7984/6549 +f 3857/8583/7023 6960/7984/6549 7869/7988/6227 +f 3853/8584/7024 3858/8586/7026 3854/8587/7027 +f 3858/8586/7026 7143/8588/7028 7144/8589/7029 +f 7141/8590/7030 7142/8591/7031 3858/8586/7026 +f 7142/8591/7031 7925/8592/7032 7143/8588/7028 +f 3854/8587/7027 3859/8593/7033 3855/8578/7020 +f 3859/8593/7033 7130/8594/6998 7129/8581/6994 +f 3854/8587/7027 7144/8589/7029 7145/8595/7034 +f 3859/8593/7033 7145/8595/7034 7905/8596/6999 +f 3864/8597/7035 3865/8598/7036 3861/8599/7037 +f 3865/8598/7036 7149/8600/7038 7150/8601/7039 +f 7147/8602/7040 7148/8603/7041 3865/8598/7036 +f 7148/8603/7041 7926/8604/7042 7149/8600/7038 +f 3860/8605/7043 3861/8599/7037 3866/8606/7044 +f 3862/8607/7045 3866/8606/7044 6971/8608/6570 +f 3861/8599/7037 7150/8601/7039 7151/8609/7046 +f 3866/8606/7044 7151/8609/7046 7870/8610/6238 +f 3860/8605/7043 3862/8607/7045 3867/8611/7047 +f 3863/8612/7048 3867/8611/7047 7139/8613/7014 +f 3862/8607/7045 6970/8614/6569 6969/8615/6563 +f 3867/8611/7047 6969/8615/6563 7892/8616/6566 +f 3863/8612/7048 3868/8617/7049 3864/8597/7035 +f 3864/8597/7035 3868/8617/7049 7146/8618/7050 +f 7138/8619/7013 7137/8620/7007 3868/8617/7049 +f 3868/8617/7049 7137/8620/7007 7906/8621/7010 +f 3869/8622/7051 3873/8623/7052 3874/8624/7053 +f 3870/8625/7054 3874/8624/7053 6899/8626/6394 +f 3873/8623/7052 7153/8627/7055 7154/8628/7056 +f 3874/8624/7053 7154/8628/7056 7879/8629/6357 +f 3870/8625/7054 3875/8630/7057 3871/8631/7058 +f 3875/8630/7057 7064/8345/6824 7063/8344/6823 +f 6898/8632/6393 6897/8633/6389 3875/8630/7057 +f 6897/8633/6389 7867/8346/6391 7064/8345/6824 +f 3869/8622/7051 3871/8631/7058 3876/8634/7059 +f 3872/8635/7060 3876/8634/7059 7155/8636/7061 +f 3871/8631/7058 7063/8344/6823 7062/8360/6835 +f 3876/8634/7059 7062/8360/6835 7927/8363/6838 +f 3872/8635/7060 3877/8637/7062 3873/8623/7052 +f 3877/8637/7062 7152/8638/7063 7153/8627/7055 +f 7156/8639/7064 7157/8640/7065 3877/8637/7062 +f 7157/8640/7065 7915/8641/7066 7152/8638/7063 +f 3878/8642/7067 3882/8643/7068 3883/8644/7069 +f 3879/8645/7070 3883/8644/7069 7079/8382/6857 +f 3882/8643/7068 7159/8646/7071 7160/8647/7072 +f 3883/8644/7069 7160/8647/7072 7928/8383/6858 +f 3879/8645/7070 3884/8648/7073 3880/8649/7074 +f 3884/8648/7073 6902/7794/6401 6901/7793/6400 +f 7078/8381/6856 7077/8378/6853 3884/8648/7073 +f 7077/8378/6853 7868/7795/6402 6902/7794/6401 +f 3878/8642/7067 3880/8649/7074 3885/8650/7075 +f 3881/8651/7076 3885/8650/7075 7161/8652/7077 +f 3880/8649/7074 6901/7793/6400 6900/7807/6410 +f 3885/8650/7075 6900/7807/6410 7880/7760/6373 +f 3881/8651/7076 3886/8653/7078 3882/8643/7068 +f 3886/8653/7078 7158/8654/7079 7159/8646/7071 +f 7162/8655/7080 7163/8656/7081 3886/8653/7078 +f 7163/8656/7081 7916/8657/7082 7158/8654/7079 +f 3887/8658/7083 3891/8659/7084 3892/8660/7085 +f 3888/8661/7086 3892/8660/7085 6887/8662/6361 +f 3891/8659/7084 7165/8663/7087 7166/8664/7088 +f 3892/8660/7085 7166/8664/7088 7877/8665/6325 +f 3888/8661/7086 3893/8666/7089 3889/8667/7090 +f 3893/8666/7089 7154/8628/7056 7153/8627/7055 +f 6886/8668/6360 6885/8669/6355 3893/8666/7089 +f 6885/8669/6355 7879/8629/6357 7154/8628/7056 +f 3887/8658/7083 3889/8667/7090 3894/8670/7091 +f 3890/8671/7092 3894/8670/7091 7167/8672/7093 +f 3889/8667/7090 7153/8627/7055 7152/8638/7063 +f 3894/8670/7091 7152/8638/7063 7915/8641/7066 +f 3890/8671/7092 3895/8673/7094 3891/8659/7084 +f 3895/8673/7094 7164/8674/7095 7165/8663/7087 +f 7168/8675/7096 7169/8676/7097 3895/8673/7094 +f 3895/8673/7094 7169/8676/7097 7917/8677/7098 +f 3896/8678/7099 3900/8679/7100 3901/8680/7101 +f 3897/8681/7102 3901/8680/7101 7163/8656/7081 +f 3900/8679/7100 7171/8682/7103 7172/8683/7104 +f 3901/8680/7101 7172/8683/7104 7916/8657/7082 +f 3897/8681/7102 3902/8684/7105 3898/8685/7106 +f 3902/8684/7105 6890/7759/6372 6889/7751/6367 +f 7162/8655/7080 7161/8652/7077 3902/8684/7105 +f 7161/8652/7077 7880/7760/6373 6890/7759/6372 +f 3896/8678/7099 3898/8685/7106 3903/8686/7107 +f 3899/8687/7108 3903/8686/7107 7173/8688/7109 +f 3898/8685/7106 6889/7751/6367 6888/7750/6366 +f 3903/8686/7107 6888/7750/6366 7878/7754/6341 +f 3899/8687/7108 3904/8689/7110 3900/8679/7100 +f 3904/8689/7110 7170/8690/7111 7171/8682/7103 +f 7174/8691/7112 7175/8692/7113 3904/8689/7110 +f 3904/8689/7110 7175/8692/7113 7918/8693/7114 +f 3905/8694/7115 3909/8695/7116 3910/8696/7117 +f 3906/8697/7118 3910/8696/7117 6875/8698/6329 +f 3909/8695/7116 7177/8699/7119 7178/8700/7120 +f 3910/8696/7117 7178/8700/7120 7875/8701/6293 +f 3906/8697/7118 3911/8702/7121 3907/8703/7122 +f 3911/8702/7121 7166/8704/7088 7165/8705/7087 +f 6874/8706/6328 6873/8707/6323 3911/8702/7121 +f 6873/8707/6323 7877/8708/6325 7166/8704/7088 +f 3905/8694/7115 3907/8703/7122 3912/8709/7123 +f 3912/8709/7123 7179/8710/7124 7180/8711/7125 +f 3907/8703/7122 7165/8705/7087 7164/8712/7095 +f 7164/8712/7095 7917/8713/7098 7179/8710/7124 +f 3908/8714/7126 3913/8715/7127 3909/8695/7116 +f 3913/8715/7127 7176/8716/7128 7177/8699/7119 +f 7180/8711/7125 7181/8717/7129 3913/8715/7127 +f 7181/8717/7129 7919/8718/7130 7176/8716/7128 +f 3914/8719/7131 3918/8720/7132 3919/8721/7133 +f 3915/8722/7134 3919/8721/7133 7175/8723/7113 +f 7183/8724/7135 7184/8725/7136 3919/8721/7133 +f 7184/8725/7136 7918/8726/7114 7175/8723/7113 +f 3915/8722/7134 3920/8727/7137 3916/8728/7138 +f 3920/8727/7137 6878/8729/6340 6877/8730/6335 +f 7174/8731/7112 7173/8732/7109 3920/8727/7137 +f 7173/8732/7109 7878/8733/6341 6878/8729/6340 +f 3914/8719/7131 3916/8728/7138 3921/8734/7139 +f 3917/8735/7140 3921/8734/7139 7185/8736/7141 +f 3916/8728/7138 6877/8730/6335 6876/8737/6334 +f 3921/8734/7139 6876/8737/6334 7876/8738/6309 +f 3917/8735/7140 3922/8739/7142 3918/8720/7132 +f 3922/8739/7142 7182/8740/7143 7183/8724/7135 +f 7186/8741/7144 7187/8742/7145 3922/8739/7142 +f 7187/8742/7145 7920/8743/7146 7182/8740/7143 +f 3927/8744/7147 3928/8745/7148 3924/8746/7149 +f 3924/8746/7149 3928/8745/7148 6863/8747/6297 +f 7189/8748/7150 7190/8749/7151 3928/8745/7148 +f 7190/8749/7151 7873/8750/6262 6863/8747/6297 +f 3924/8746/7149 3929/8751/7152 3925/8752/7153 +f 3929/8751/7152 7178/8700/7120 7177/8699/7119 +f 6862/8753/6296 6861/8754/6291 3929/8751/7152 +f 6861/8754/6291 7875/8701/6293 7178/8700/7120 +f 3923/8755/7154 3925/8752/7153 3930/8756/7155 +f 3926/8757/7156 3930/8756/7155 7191/8758/7157 +f 3925/8752/7153 7177/8699/7119 7176/8716/7128 +f 3930/8756/7155 7176/8716/7128 7919/8718/7130 +f 3926/8757/7156 3931/8759/7158 3927/8744/7147 +f 3927/8744/7147 3931/8759/7158 7188/8760/7159 +f 7192/8761/7160 7193/8762/7161 3931/8759/7158 +f 7193/8762/7161 7921/8763/7162 7188/8760/7159 +f 3932/8764/7163 3936/8765/7164 3937/8766/7165 +f 3933/8767/7166 3937/8766/7165 7187/8768/7145 +f 3936/8765/7164 7195/8769/7167 7196/8770/7168 +f 3937/8766/7165 7196/8770/7168 7920/8771/7146 +f 3933/8767/7166 3938/8772/7169 3934/8773/7170 +f 3938/8772/7169 6866/8774/6308 6865/8775/6303 +f 7186/8776/7144 7185/8777/7141 3938/8772/7169 +f 7185/8777/7141 7876/8778/6309 6866/8774/6308 +f 3934/8773/7170 3939/8779/7171 3935/8780/7172 +f 3939/8779/7171 7197/8781/7173 7198/8782/7174 +f 3934/8773/7170 6865/8775/6303 6864/8783/6302 +f 6864/8783/6302 7874/8784/6278 7197/8781/7173 +f 3935/8780/7172 3940/8785/7175 3936/8765/7164 +f 3940/8785/7175 7194/8786/7176 7195/8769/7167 +f 3935/8780/7172 7198/8782/7174 7199/8787/7177 +f 7199/8787/7177 7922/8788/7178 7194/8786/7176 +f 3945/8789/7179 3946/8790/7180 3942/8791/7181 +f 3946/8790/7180 6851/7619/6265 6850/7612/6259 +f 7201/8792/7182 7202/8793/7183 3946/8790/7180 +f 7202/8793/7183 7871/7620/6222 6851/7619/6265 +f 3941/8794/7184 3942/8791/7181 3947/8795/7185 +f 3943/8796/7186 3947/8795/7185 7190/8797/7151 +f 3942/8791/7181 6850/7612/6259 6849/7611/6258 +f 3947/8795/7185 6849/7611/6258 7873/7615/6262 +f 3943/8796/7186 3948/8798/7187 3944/8799/7188 +f 3944/8799/7188 3948/8798/7187 7203/8800/7189 +f 7189/8801/7150 7188/8802/7159 3948/8798/7187 +f 3948/8798/7187 7188/8802/7159 7921/8803/7162 +f 3941/8794/7184 3944/8799/7188 3949/8804/7190 +f 3945/8789/7179 3949/8804/7190 7200/8805/7191 +f 3944/8799/7188 7204/8806/7192 7205/8807/7193 +f 3949/8804/7190 7205/8807/7193 7923/8808/7194 +f 3954/8809/7195 3955/8810/7196 3951/8811/7197 +f 3955/8810/7196 7199/8787/7177 7198/8782/7174 +f 3954/8809/7195 7207/8812/7198 7208/8813/7199 +f 3955/8810/7196 7208/8813/7199 7922/8788/7178 +f 3950/8814/7200 3951/8811/7197 3956/8815/7201 +f 3952/8816/7202 3956/8815/7201 6854/8817/6277 +f 3951/8811/7197 7198/8782/7174 7197/8781/7173 +f 3956/8815/7201 7197/8781/7173 7874/8784/6278 +f 3952/8816/7202 3957/8818/7203 3953/8819/7204 +f 3957/8818/7203 7209/8820/7205 7210/8821/7206 +f 6853/8822/6276 6852/8823/6271 3957/8818/7203 +f 6852/8823/6271 7872/8824/6245 7209/8820/7205 +f 3950/8814/7200 3953/8819/7204 3958/8825/7207 +f 3954/8809/7195 3958/8825/7207 7206/8826/7208 +f 3953/8819/7204 7210/8821/7206 7211/8827/7209 +f 3958/8825/7207 7211/8827/7209 7924/8828/7210 +f 3963/8829/7211 3964/8830/7212 3960/8831/7213 +f 3964/8830/7212 6833/8832/6226 6832/8833/6219 +f 7141/8590/7030 7140/8585/7025 3964/8830/7212 +f 7140/8585/7025 7869/7988/6227 6833/8832/6226 +f 3959/8834/7214 3960/8831/7213 3965/8835/7215 +f 3961/8836/7216 3965/8835/7215 7202/8837/7183 +f 3960/8831/7213 6832/8833/6219 6831/8838/6218 +f 3965/8835/7215 6831/8838/6218 7871/8839/6222 +f 3961/8836/7216 3966/8840/7217 3962/8841/7218 +f 3966/8840/7217 7212/8842/7219 7213/8843/7220 +f 7201/8844/7182 7200/8845/7191 3966/8840/7217 +f 7200/8845/7191 7923/8846/7194 7212/8842/7219 +f 3959/8834/7214 3962/8841/7218 3967/8847/7221 +f 3963/8829/7211 3967/8847/7221 7142/8591/7031 +f 3962/8841/7218 7213/8843/7220 7214/8848/7222 +f 3967/8847/7221 7214/8848/7222 7925/8592/7032 +f 3972/8849/7223 3973/8850/7224 3969/8851/7225 +f 3973/8850/7224 7211/8827/7209 7210/8821/7206 +f 7216/8852/7226 7217/8853/7227 3973/8850/7224 +f 7217/8853/7227 7924/8828/7210 7211/8827/7209 +f 3968/8854/7228 3969/8851/7225 3974/8855/7229 +f 3970/8856/7230 3974/8855/7229 6842/8857/6244 +f 3969/8851/7225 7210/8821/7206 7209/8820/7205 +f 3974/8855/7229 7209/8820/7205 7872/8824/6245 +f 3970/8856/7230 3975/8858/7231 3971/8859/7232 +f 3975/8858/7231 7151/8609/7046 7150/8601/7039 +f 6841/8860/6243 6840/8861/6235 3975/8858/7231 +f 6840/8861/6235 7870/8610/6238 7151/8609/7046 +f 3968/8854/7228 3971/8859/7232 3976/8862/7233 +f 3972/8849/7223 3976/8862/7233 7215/8863/7234 +f 3971/8859/7232 7150/8601/7039 7149/8600/7038 +f 3976/8862/7233 7149/8600/7038 7926/8604/7042 +f 3981/8864/7235 3982/8865/7236 3978/8866/7237 +f 3982/8865/7236 7121/8514/6970 7120/8513/6969 +f 7219/8867/7238 7220/8868/7239 3982/8865/7236 +f 7220/8868/7239 7909/8515/6936 7121/8514/6970 +f 3978/8866/7237 3983/8869/7240 3979/8870/7241 +f 3983/8869/7240 7221/8871/7242 7222/8872/7243 +f 7120/8513/6969 7119/8507/6964 3983/8869/7240 +f 7119/8507/6964 7907/8510/6967 7221/8871/7242 +f 3977/8873/7244 3979/8870/7241 3984/8874/7245 +f 3980/8875/7246 3984/8874/7245 7224/8876/7247 +f 3979/8870/7241 7222/8872/7243 7223/8877/7248 +f 3984/8874/7245 7223/8877/7248 7931/8878/7249 +f 3977/8873/7244 3980/8875/7246 3985/8879/7250 +f 3981/8864/7235 3985/8879/7250 7218/8880/7251 +f 3980/8875/7246 7225/8881/7252 7226/8882/7253 +f 3985/8879/7250 7226/8882/7253 7929/8883/7254 +f 3986/8884/7255 3990/8885/7256 3991/8886/7257 +f 3987/8887/7258 3991/8886/7257 7230/8888/7259 +f 3990/8885/7256 7228/8889/7260 7229/8890/7261 +f 3991/8886/7257 7229/8890/7261 7932/8891/7262 +f 3987/8887/7258 3992/8892/7263 3988/8893/7264 +f 3992/8892/7263 7124/8894/6976 7123/8895/6975 +f 7231/8896/7265 7232/8897/7266 3992/8892/7263 +f 7232/8897/7266 7908/8898/6977 7124/8894/6976 +f 3988/8893/7264 3993/8899/7267 3989/8900/7268 +f 3993/8899/7267 7233/8901/7269 7234/8902/7270 +f 7123/8895/6975 7122/8903/6986 3993/8899/7267 +f 7122/8903/6986 7910/8904/6946 7233/8901/7269 +f 3986/8884/7255 3989/8900/7268 3994/8905/7271 +f 3990/8885/7256 3994/8905/7271 7227/8906/7272 +f 3989/8900/7268 7234/8902/7270 7235/8907/7273 +f 3994/8905/7271 7235/8907/7273 7930/8908/7274 +f 3999/8909/7275 4000/8910/7276 3996/8911/7277 +f 4000/8910/7276 7226/8882/7253 7225/8881/7252 +f 7237/8912/7278 7238/8913/7279 4000/8910/7276 +f 7238/8913/7279 7929/8883/7254 7226/8882/7253 +f 3996/8911/7277 4001/8914/7280 3997/8915/7281 +f 4001/8914/7280 7239/8916/7282 7240/8917/7283 +f 7225/8881/7252 7224/8876/7247 4001/8914/7280 +f 7224/8876/7247 7931/8878/7249 7239/8916/7282 +f 3995/8918/7284 3997/8915/7281 4002/8919/7285 +f 3998/8920/7286 4002/8919/7285 7242/8921/7287 +f 3997/8915/7281 7240/8917/7283 7241/8922/7288 +f 4002/8919/7285 7241/8922/7288 7933/8923/7289 +f 3998/8920/7286 4003/8924/7290 3999/8909/7275 +f 3999/8909/7275 4003/8924/7290 7236/8925/7291 +f 7243/8926/7292 7244/8927/7293 4003/8924/7290 +f 4003/8924/7290 7244/8927/7293 7935/8928/7294 +f 4004/8929/7295 4008/8930/7296 4009/8931/7297 +f 4005/8932/7298 4009/8931/7297 7248/8933/7299 +f 4008/8930/7296 7246/8934/7300 7247/8935/7301 +f 4009/8931/7297 7247/8935/7301 7934/8936/7302 +f 4005/8932/7298 4010/8937/7303 4006/8938/7304 +f 4010/8937/7303 7229/8890/7261 7228/8889/7260 +f 7249/8939/7305 7250/8940/7306 4010/8937/7303 +f 7250/8940/7306 7932/8891/7262 7229/8890/7261 +f 4006/8938/7304 4011/8941/7307 4007/8942/7308 +f 4011/8941/7307 7251/8943/7309 7252/8944/7310 +f 7228/8889/7260 7227/8906/7272 4011/8941/7307 +f 7227/8906/7272 7930/8908/7274 7251/8943/7309 +f 4007/8942/7308 4012/8945/7311 4008/8930/7296 +f 4012/8945/7311 7245/8946/7312 7246/8934/7300 +f 4007/8942/7308 7252/8944/7310 7253/8947/7313 +f 4012/8945/7311 7253/8947/7313 7936/8948/7314 +f 4013/8949/7315 4017/8950/7316 4018/8951/7317 +f 4014/8952/7318 4018/8951/7317 7244/8927/7293 +f 7255/8953/7319 7256/8954/7320 4018/8951/7317 +f 7256/8954/7320 7935/8928/7294 7244/8927/7293 +f 4014/8952/7318 4019/8955/7321 4015/8956/7322 +f 4015/8956/7322 4019/8955/7321 7257/8957/7323 +f 7243/8926/7292 7242/8921/7287 4019/8955/7321 +f 7242/8921/7287 7933/8923/7289 7257/8957/7323 +f 4015/8956/7322 4020/8958/7324 4016/8959/7325 +f 4020/8958/7324 7260/8960/7326 7261/8961/7327 +f 7258/8962/7328 7259/8963/7329 4020/8958/7324 +f 7259/8963/7329 7939/8964/7330 7260/8960/7326 +f 4013/8949/7315 4016/8959/7325 4021/8965/7331 +f 4017/8950/7316 4021/8965/7331 7254/8966/7332 +f 4016/8959/7325 7261/8961/7327 7262/8967/7333 +f 4021/8965/7331 7262/8967/7333 7937/8968/7334 +f 4026/8969/7335 4027/8970/7336 4023/8971/7337 +f 4027/8970/7336 7266/8972/7338 7267/8973/7339 +f 7264/8974/7340 7265/8975/7341 4027/8970/7336 +f 7265/8975/7341 7940/8976/7342 7266/8972/7338 +f 4023/8971/7337 4028/8977/7343 4024/8978/7344 +f 4028/8977/7343 7247/8935/7301 7246/8934/7300 +f 4023/8971/7337 7267/8973/7339 7268/8979/7345 +f 7268/8979/7345 7934/8936/7302 7247/8935/7301 +f 4022/8980/7346 4024/8978/7344 4029/8981/7347 +f 4029/8981/7347 7269/8982/7348 7270/8983/7349 +f 4024/8978/7344 7246/8934/7300 7245/8946/7312 +f 7245/8946/7312 7936/8948/7314 7269/8982/7348 +f 4022/8980/7346 4025/8984/7350 4030/8985/7351 +f 4026/8969/7335 4030/8985/7351 7263/8986/7352 +f 4025/8984/7350 7270/8983/7349 7271/8987/7353 +f 4030/8985/7351 7271/8987/7353 7938/8988/7354 +f 4035/8989/7355 4036/8990/7356 4032/8991/7357 +f 4036/8990/7356 7262/8967/7333 7261/8961/7327 +f 7273/8992/7358 7274/8993/7359 4036/8990/7356 +f 7274/8993/7359 7937/8968/7334 7262/8967/7333 +f 4031/8994/7360 4032/8991/7357 4037/8995/7361 +f 4033/8996/7362 4037/8995/7361 7275/8997/7363 +f 4032/8991/7357 7261/8961/7327 7260/8960/7326 +f 4037/8995/7361 7260/8960/7326 7939/8964/7330 +f 4033/8996/7362 4038/8998/7364 4034/8999/7365 +f 4038/8998/7364 7278/9000/7366 7279/9001/7367 +f 7276/9002/7368 7277/9003/7369 4038/8998/7364 +f 7277/9003/7369 7941/9004/7370 7278/9000/7366 +f 4031/8994/7360 4034/8999/7365 4039/9005/7371 +f 4035/8989/7355 4039/9005/7371 7272/9006/7372 +f 4034/8999/7365 7279/9001/7367 7280/9007/7373 +f 4039/9005/7371 7280/9007/7373 7943/9008/7374 +f 4044/9009/7375 4045/9010/7376 4041/9011/7377 +f 4045/9010/7376 7284/9012/7378 7285/9013/7379 +f 7282/9014/7380 7283/9015/7381 4045/9010/7376 +f 7283/9015/7381 7942/9016/7382 7284/9012/7378 +f 4040/9017/7383 4041/9011/7377 4046/9018/7384 +f 4042/9019/7385 4046/9018/7384 7265/8975/7341 +f 4041/9011/7377 7285/9013/7379 7286/9020/7386 +f 4046/9018/7384 7286/9020/7386 7940/8976/7342 +f 4042/9019/7385 4047/9021/7387 4043/9022/7388 +f 4047/9021/7387 7287/9023/7389 7288/9024/7390 +f 7264/8974/7340 7263/8986/7352 4047/9021/7387 +f 7263/8986/7352 7938/8988/7354 7287/9023/7389 +f 4040/9017/7383 4043/9022/7388 4048/9025/7391 +f 4044/9009/7375 4048/9025/7391 7281/9026/7392 +f 4043/9022/7388 7288/9024/7390 7289/9027/7393 +f 4048/9025/7391 7289/9027/7393 7944/9028/7394 +f 4049/9029/7395 4053/9030/7396 4054/9031/7397 +f 4050/9032/7398 4054/9031/7397 7157/9033/7065 +f 4053/9030/7396 7291/9034/7399 7292/9035/7400 +f 4054/9031/7397 7292/9035/7400 7915/9036/7066 +f 4050/9032/7398 4055/9037/7401 4051/9038/7402 +f 4055/9037/7401 7293/9039/7403 7294/9040/7404 +f 7156/9041/7064 7155/9042/7061 4055/9037/7401 +f 7155/9042/7061 7927/9043/6838 7293/9039/7403 +f 4049/9029/7395 4051/9038/7402 4056/9044/7405 +f 4052/9045/7406 4056/9044/7405 7274/8993/7359 +f 4051/9038/7402 7294/9040/7404 7295/9046/7407 +f 4056/9044/7405 7295/9046/7407 7937/8968/7334 +f 4052/9045/7406 4057/9047/7408 4053/9030/7396 +f 4057/9047/7408 7290/9048/7409 7291/9034/7399 +f 7273/8992/7358 7272/9006/7372 4057/9047/7408 +f 7272/9006/7372 7943/9008/7374 7290/9048/7409 +f 4058/9049/7410 4062/9050/7411 4063/9051/7412 +f 4059/9052/7413 4063/9051/7412 7296/9053/7414 +f 4062/9050/7411 7288/9024/7390 7287/9023/7389 +f 4063/9051/7412 7287/9023/7389 7938/8988/7354 +f 4059/9052/7413 4064/9054/7415 4060/9055/7416 +f 4064/9054/7415 7160/9056/7072 7159/9057/7071 +f 7297/9058/7417 7298/9059/7418 4064/9054/7415 +f 7298/9059/7418 7928/9060/6858 7160/9056/7072 +f 4058/9049/7410 4060/9055/7416 4065/9061/7419 +f 4061/9062/7420 4065/9061/7419 7299/9063/7421 +f 4060/9055/7416 7159/9057/7071 7158/9064/7079 +f 4065/9061/7419 7158/9064/7079 7916/9065/7082 +f 4061/9062/7420 4066/9066/7422 4062/9050/7411 +f 4066/9066/7422 7289/9027/7393 7288/9024/7390 +f 7300/9067/7423 7301/9068/7424 4066/9066/7422 +f 7301/9068/7424 7944/9028/7394 7289/9027/7393 +f 4071/9069/7425 4072/9070/7426 4068/9071/7427 +f 4072/9070/7426 7302/9072/7428 7303/9073/7429 +f 7069/9074/6836 7068/9075/6831 4072/9070/7426 +f 7068/9075/6831 7903/9076/6833 7302/9072/7428 +f 4067/9077/7430 4068/9071/7427 4073/9078/7431 +f 4069/9079/7432 4073/9078/7431 7256/8954/7320 +f 4068/9071/7427 7303/9073/7429 7304/9080/7433 +f 4073/9078/7431 7304/9080/7433 7935/8928/7294 +f 4069/9079/7432 4074/9081/7434 4070/9082/7435 +f 4074/9081/7434 7295/9046/7407 7294/9040/7404 +f 7255/8953/7319 7254/8966/7332 4074/9081/7434 +f 7254/8966/7332 7937/8968/7334 7295/9046/7407 +f 4067/9077/7430 4070/9082/7435 4075/9083/7436 +f 4071/9069/7425 4075/9083/7436 7070/9084/6837 +f 4070/9082/7435 7294/9040/7404 7293/9039/7403 +f 4075/9083/7436 7293/9039/7403 7927/9043/6838 +f 4080/9085/7437 4081/9086/7438 4077/9087/7439 +f 4081/9086/7438 7271/9088/7353 7270/9089/7349 +f 7297/9090/7417 7296/9091/7414 4081/9086/7438 +f 7296/9091/7414 7938/9092/7354 7271/9088/7353 +f 4076/9093/7440 4077/9087/7439 4082/9094/7441 +f 4078/9095/7442 4082/9094/7441 7305/9096/7443 +f 4077/9087/7439 7270/9089/7349 7269/9097/7348 +f 4082/9094/7441 7269/9097/7348 7936/9098/7314 +f 4078/9095/7442 4083/9099/7444 4079/9100/7445 +f 4083/9099/7444 7073/8370/6845 7072/8369/6844 +f 7306/9101/7446 7307/9102/7447 4083/9099/7444 +f 7307/9102/7447 7904/8371/6846 7073/8370/6845 +f 4076/9093/7440 4079/9100/7445 4084/9103/7448 +f 4080/9085/7437 4084/9103/7448 7298/9104/7418 +f 4079/9100/7445 7072/8369/6844 7071/8380/6855 +f 4084/9103/7448 7071/8380/6855 7928/8383/6858 +f 4085/9105/7449 4089/9106/7450 4090/9107/7451 +f 4090/9107/7451 7085/9108/6874 7084/9109/6870 +f 4089/9106/7450 7303/9073/7429 7302/9072/7428 +f 4090/9107/7451 7302/9072/7428 7903/9076/6833 +f 4085/9105/7449 4086/9110/7452 4091/9111/7453 +f 4091/9111/7453 7308/9112/7454 7309/9113/7455 +f 4086/9110/7452 7084/9109/6870 7083/9114/6869 +f 4091/9111/7453 7083/9114/6869 7913/9115/6872 +f 4085/9105/7449 4087/9116/7456 4092/9117/7457 +f 4088/9118/7458 4092/9117/7457 7238/8913/7279 +f 4087/9116/7456 7309/9113/7455 7310/9119/7459 +f 4092/9117/7457 7310/9119/7459 7929/8883/7254 +f 4088/9118/7458 4093/9120/7460 4089/9106/7450 +f 4093/9120/7460 7304/9080/7433 7303/9073/7429 +f 7237/8912/7278 7236/8925/7291 4093/9120/7460 +f 7236/8925/7291 7935/8928/7294 7304/9080/7433 +f 4094/9121/7461 4098/9122/7462 4099/9123/7463 +f 4095/9124/7464 4099/9123/7463 7311/9125/7465 +f 4098/9122/7462 7252/8944/7310 7251/8943/7309 +f 4099/9123/7463 7251/8943/7309 7930/8908/7274 +f 4094/9121/7461 4095/9124/7464 4100/9126/7466 +f 4096/9127/7467 4100/9126/7466 7088/9128/6880 +f 7312/9129/7468 7313/9130/7469 4100/9126/7466 +f 4100/9126/7466 7313/9130/7469 7914/9131/6881 +f 4094/9121/7461 4096/9127/7467 4101/9132/7470 +f 4097/9133/7471 4101/9132/7470 7307/9134/7447 +f 7087/9135/6879 7086/9136/6890 4101/9132/7470 +f 4101/9132/7470 7086/9136/6890 7904/9137/6846 +f 4097/9133/7471 4102/9138/7472 4098/9122/7462 +f 4102/9138/7472 7253/8947/7313 7252/8944/7310 +f 7306/9139/7446 7305/9140/7443 4102/9138/7472 +f 7305/9140/7443 7936/8948/7314 7253/8947/7313 +f 4103/9141/7473 4107/9142/7474 4108/9143/7475 +f 4104/9144/7476 4108/9143/7475 7220/8868/7239 +f 4107/9142/7474 7108/9145/6933 7107/9146/6932 +f 4108/9143/7475 7107/9146/6932 7909/8515/6936 +f 4104/9144/7476 4109/9147/7477 4105/9148/7478 +f 4109/9147/7477 7310/9119/7459 7309/9113/7455 +f 7219/8867/7238 7218/8880/7251 4109/9147/7477 +f 7218/8880/7251 7929/8883/7254 7310/9119/7459 +f 4103/9141/7473 4105/9148/7478 4110/9149/7479 +f 4106/9150/7480 4110/9149/7479 7097/9151/6906 +f 4105/9148/7478 7309/9113/7455 7308/9112/7454 +f 4110/9149/7479 7308/9112/7454 7913/9115/6872 +f 4106/9150/7480 4111/9152/7481 4107/9142/7474 +f 4111/9152/7481 7109/9153/6938 7108/9145/6933 +f 7096/9154/6905 7095/9155/6901 4111/9152/7481 +f 4111/9152/7481 7095/9155/6901 7911/9156/6903 +f 4112/9157/7482 4116/9158/7483 4117/9159/7484 +f 4113/9160/7485 4117/9159/7484 7313/9130/7469 +f 4116/9158/7483 7099/9161/6912 7098/9162/6922 +f 4117/9159/7484 7098/9162/6922 7914/9131/6881 +f 4113/9160/7485 4118/9163/7486 4114/9164/7487 +f 4118/9163/7486 7235/8907/7273 7234/8902/7270 +f 7312/9129/7468 7311/9125/7465 4118/9163/7486 +f 7311/9125/7465 7930/8908/7274 7235/8907/7273 +f 4112/9157/7482 4114/9164/7487 4119/9165/7488 +f 4115/9166/7489 4119/9165/7488 7112/9167/6945 +f 4114/9164/7487 7234/8902/7270 7233/8901/7269 +f 4119/9165/7488 7233/8901/7269 7910/8904/6946 +f 4115/9166/7489 4120/9168/7490 4116/9158/7483 +f 4120/9168/7490 7100/9169/6913 7099/9161/6912 +f 7111/9170/6944 7110/9171/6954 4120/9168/7490 +f 4120/9168/7490 7110/9171/6954 7912/9172/6914 +f 4125/9173/7491 4126/9174/7492 4122/9175/7493 +f 4126/9174/7492 7145/9176/7034 7144/9177/7029 +f 7132/9178/7001 7131/9179/6997 4126/9174/7492 +f 7131/9179/6997 7905/9180/6999 7145/9176/7034 +f 4121/9181/7494 4122/9175/7493 4127/9182/7495 +f 4123/9183/7496 4127/9182/7495 7314/9184/7497 +f 4122/9175/7493 7144/9177/7029 7143/9185/7028 +f 4127/9182/7495 7143/9185/7028 7925/9186/7032 +f 4123/9183/7496 4128/9187/7498 4124/9188/7499 +f 4128/9187/7498 7223/9189/7248 7222/9190/7243 +f 7315/9191/7500 7316/9192/7501 4128/9187/7498 +f 7316/9192/7501 7931/9193/7249 7223/9189/7248 +f 4121/9181/7494 4124/9188/7499 4129/9194/7502 +f 4125/9173/7491 4129/9194/7502 7133/9195/7002 +f 4124/9188/7499 7222/9190/7243 7221/9196/7242 +f 4129/9194/7502 7221/9196/7242 7907/9197/6967 +f 4134/9198/7503 4135/9199/7504 4131/9200/7505 +f 4135/9199/7504 7317/9201/7506 7318/9202/7507 +f 7231/8896/7265 7230/8888/7259 4135/9199/7504 +f 7230/8888/7259 7932/8891/7262 7317/9201/7506 +f 4130/9203/7508 4131/9200/7505 4136/9204/7509 +f 4132/9205/7510 4136/9204/7509 7148/9206/7041 +f 4131/9200/7505 7318/9202/7507 7319/9207/7511 +f 4136/9204/7509 7319/9207/7511 7926/9208/7042 +f 4132/9205/7510 4137/9209/7512 4133/9210/7513 +f 4137/9209/7512 7136/9211/7009 7135/9212/7008 +f 7147/9213/7040 7146/9214/7050 4137/9209/7512 +f 7146/9214/7050 7906/9215/7010 7136/9211/7009 +f 4130/9203/7508 4133/9210/7513 4138/9216/7514 +f 4134/9198/7503 4138/9216/7514 7232/8897/7266 +f 4133/9210/7513 7135/9212/7008 7134/9217/7018 +f 4138/9216/7514 7134/9217/7018 7908/8898/6977 +f 4139/9218/7515 4143/9219/7516 4144/9220/7517 +f 4140/9221/7518 4144/9220/7517 7320/9222/7519 +f 4143/9219/7516 7213/9223/7220 7212/9224/7219 +f 4144/9220/7517 7212/9224/7219 7923/9225/7194 +f 4140/9221/7518 4145/9226/7520 4141/9227/7521 +f 4145/9226/7520 7241/8922/7288 7240/8917/7283 +f 7321/9228/7522 7322/9229/7523 4145/9226/7520 +f 7322/9229/7523 7933/8923/7289 7241/8922/7288 +f 4139/9218/7515 4141/9227/7521 4146/9230/7524 +f 4142/9231/7525 4146/9230/7524 7316/9232/7501 +f 4141/9227/7521 7240/8917/7283 7239/8916/7282 +f 4146/9230/7524 7239/8916/7282 7931/8878/7249 +f 4142/9231/7525 4147/9233/7526 4143/9219/7516 +f 4147/9233/7526 7214/9234/7222 7213/9223/7220 +f 7315/9235/7500 7314/9236/7497 4147/9233/7526 +f 7314/9236/7497 7925/9237/7032 7214/9234/7222 +f 4148/9238/7527 4152/9239/7528 4153/9240/7529 +f 4149/9241/7530 4153/9240/7529 7250/9242/7306 +f 4152/9239/7528 7318/9243/7507 7317/9244/7506 +f 4153/9240/7529 7317/9244/7506 7932/9245/7262 +f 4149/9241/7530 4154/9246/7531 4150/9247/7532 +f 4154/9246/7531 7323/9248/7533 7324/9249/7534 +f 7249/9250/7305 7248/9251/7299 4154/9246/7531 +f 7248/9251/7299 7934/9252/7302 7323/9248/7533 +f 4148/9238/7527 4150/9247/7532 4155/9253/7535 +f 4151/9254/7536 4155/9253/7535 7217/9255/7227 +f 4150/9247/7532 7324/9249/7534 7325/9256/7537 +f 4155/9253/7535 7325/9256/7537 7924/9257/7210 +f 4151/9254/7536 4156/9258/7538 4152/9239/7528 +f 4156/9258/7538 7319/9259/7511 7318/9243/7507 +f 7216/9260/7226 7215/9261/7234 4156/9258/7538 +f 7215/9261/7234 7926/9262/7042 7319/9259/7511 +f 4161/9263/7539 4162/9264/7540 4158/9265/7541 +f 4162/9264/7540 7326/9266/7542 7327/9267/7543 +f 7204/9268/7192 7203/9269/7189 4162/9264/7540 +f 7203/9269/7189 7921/9270/7162 7326/9266/7542 +f 4157/9271/7544 4158/9265/7541 4163/9272/7545 +f 4159/9273/7546 4163/9272/7545 7259/8963/7329 +f 4158/9265/7541 7327/9267/7543 7328/9274/7547 +f 4163/9272/7545 7328/9274/7547 7939/8964/7330 +f 4159/9273/7546 4164/9275/7548 4160/9276/7549 +f 4164/9275/7548 7322/9229/7523 7321/9228/7522 +f 7258/8962/7328 7257/8957/7323 4164/9275/7548 +f 7257/8957/7323 7933/8923/7289 7322/9229/7523 +f 4157/9271/7544 4160/9276/7549 4165/9277/7550 +f 4161/9263/7539 4165/9277/7550 7205/9278/7193 +f 4160/9276/7549 7321/9228/7522 7320/9222/7519 +f 4165/9277/7550 7320/9222/7519 7923/9225/7194 +f 4170/9279/7551 4171/9280/7552 4167/9281/7553 +f 4171/9280/7552 7268/9282/7345 7267/9283/7339 +f 7324/9249/7534 7323/9248/7533 4171/9280/7552 +f 7323/9248/7533 7934/9252/7302 7268/9282/7345 +f 4166/9284/7554 4167/9281/7553 4172/9285/7555 +f 4168/9286/7556 4172/9285/7555 7329/9287/7557 +f 4167/9281/7553 7267/9283/7339 7266/9288/7338 +f 4172/9285/7555 7266/9288/7338 7940/9289/7342 +f 4168/9286/7556 4173/9290/7558 4169/9291/7559 +f 4173/9290/7558 7208/9292/7199 7207/9293/7198 +f 7330/9294/7560 7331/9295/7561 4173/9290/7558 +f 7331/9295/7561 7922/9296/7178 7208/9292/7199 +f 4166/9284/7554 4169/9291/7559 4174/9297/7562 +f 4170/9279/7551 4174/9297/7562 7325/9256/7537 +f 4169/9291/7559 7207/9293/7198 7206/9298/7208 +f 4174/9297/7562 7206/9298/7208 7924/9257/7210 +f 4179/9299/7563 4180/9300/7564 4176/9301/7565 +f 4180/9300/7564 7332/9302/7566 7333/9303/7567 +f 7192/9304/7160 7191/9305/7157 4180/9300/7564 +f 7191/9305/7157 7919/9306/7130 7332/9302/7566 +f 4175/9307/7568 4176/9301/7565 4181/9308/7569 +f 4177/9309/7570 4181/9308/7569 7277/9003/7369 +f 4176/9301/7565 7333/9303/7567 7334/9310/7571 +f 4181/9308/7569 7334/9310/7571 7941/9004/7370 +f 4177/9309/7570 4182/9311/7572 4178/9312/7573 +f 4182/9311/7572 7328/9274/7547 7327/9267/7543 +f 7276/9002/7368 7275/8997/7363 4182/9311/7572 +f 7275/8997/7363 7939/8964/7330 7328/9274/7547 +f 4175/9307/7568 4178/9312/7573 4183/9313/7574 +f 4179/9299/7563 4183/9313/7574 7193/9314/7161 +f 4178/9312/7573 7327/9267/7543 7326/9266/7542 +f 4183/9313/7574 7326/9266/7542 7921/9270/7162 +f 4188/9315/7575 4189/9316/7576 4185/9317/7577 +f 4189/9316/7576 7286/9318/7386 7285/9319/7379 +f 7330/9294/7560 7329/9287/7557 4189/9316/7576 +f 7329/9287/7557 7940/9289/7342 7286/9318/7386 +f 4184/9320/7578 4185/9317/7577 4190/9321/7579 +f 4186/9322/7580 4190/9321/7579 7335/9323/7581 +f 4185/9317/7577 7285/9319/7379 7284/9324/7378 +f 4190/9321/7579 7284/9324/7378 7942/9325/7382 +f 4186/9322/7580 4191/9326/7582 4187/9327/7583 +f 4191/9326/7582 7196/9328/7168 7195/9329/7167 +f 7336/9330/7584 7337/9331/7585 4191/9326/7582 +f 7337/9331/7585 7920/9332/7146 7196/9328/7168 +f 4184/9320/7578 4187/9327/7583 4192/9333/7586 +f 4188/9315/7575 4192/9333/7586 7331/9295/7561 +f 4187/9327/7583 7195/9329/7167 7194/9334/7176 +f 4192/9333/7586 7194/9334/7176 7922/9296/7178 +f 4193/9335/7587 4197/9336/7588 4198/9337/7589 +f 4194/9338/7590 4198/9337/7589 7338/9339/7591 +f 4197/9336/7588 7180/9340/7125 7179/9341/7124 +f 4198/9337/7589 7179/9341/7124 7917/9342/7098 +f 4194/9338/7590 4199/9343/7592 4195/9344/7593 +f 4199/9343/7592 7280/9007/7373 7279/9001/7367 +f 7339/9345/7594 7340/9346/7595 4199/9343/7592 +f 7340/9346/7595 7943/9008/7374 7280/9007/7373 +f 4193/9335/7587 4195/9344/7593 4200/9347/7596 +f 4196/9348/7597 4200/9347/7596 7334/9310/7571 +f 4195/9344/7593 7279/9001/7367 7278/9000/7366 +f 4200/9347/7596 7278/9000/7366 7941/9004/7370 +f 4196/9348/7597 4201/9349/7598 4197/9336/7588 +f 4201/9349/7598 7181/9350/7129 7180/9340/7125 +f 4196/9348/7597 7333/9303/7567 7332/9302/7566 +f 4201/9349/7598 7332/9302/7566 7919/9306/7130 +f 4202/9351/7599 4206/9352/7600 4207/9353/7601 +f 4203/9354/7602 4207/9353/7601 7283/9355/7381 +f 4206/9352/7600 7336/9356/7584 7335/9357/7581 +f 4207/9353/7601 7335/9357/7581 7942/9358/7382 +f 4203/9354/7602 4208/9359/7603 4204/9360/7604 +f 4208/9359/7603 7341/9361/7605 7342/9362/7606 +f 7282/9363/7380 7281/9364/7392 4208/9359/7603 +f 7281/9364/7392 7944/9365/7394 7341/9361/7605 +f 4202/9351/7599 4204/9360/7604 4209/9366/7607 +f 4205/9367/7608 4209/9366/7607 7184/9368/7136 +f 4204/9360/7604 7342/9362/7606 7343/9369/7609 +f 4209/9366/7607 7343/9369/7609 7918/9370/7114 +f 4205/9367/7608 4210/9371/7610 4206/9352/7600 +f 4206/9352/7600 4210/9371/7610 7337/9372/7585 +f 7183/9373/7135 7182/9374/7143 4210/9371/7610 +f 4210/9371/7610 7182/9374/7143 7920/9375/7146 +f 4211/9376/7611 4214/9377/7612 4215/9378/7613 +f 4215/9378/7613 7292/9035/7400 7291/9034/7399 +f 4214/9377/7612 7168/9379/7096 7167/9380/7093 +f 7167/9380/7093 7915/9036/7066 7292/9035/7400 +f 4211/9376/7611 4212/9381/7614 4216/9382/7615 +f 4213/9383/7616 4216/9382/7615 7340/9346/7595 +f 4212/9381/7614 7291/9034/7399 7290/9048/7409 +f 4216/9382/7615 7290/9048/7409 7943/9008/7374 +f 4213/9383/7616 4217/9384/7617 4214/9377/7612 +f 4217/9384/7617 7169/9385/7097 7168/9379/7096 +f 7339/9345/7594 7338/9339/7591 4217/9384/7617 +f 7338/9339/7591 7917/9342/7098 7169/9385/7097 +f 4221/9386/7618 4222/9387/7619 4219/9388/7620 +f 4222/9387/7619 7343/9369/7609 7342/9362/7606 +f 7171/9389/7103 7170/9390/7111 4222/9387/7619 +f 7170/9390/7111 7918/9370/7114 7343/9369/7609 +f 4218/9391/7621 4219/9388/7620 4223/9392/7622 +f 4220/9393/7623 4223/9392/7622 7301/9394/7424 +f 4219/9388/7620 7342/9362/7606 7341/9361/7605 +f 4223/9392/7622 7341/9361/7605 7944/9365/7394 +f 4218/9391/7621 4220/9393/7623 4224/9395/7624 +f 4221/9386/7618 4224/9395/7624 7172/9396/7104 +f 7300/9397/7423 7299/9398/7421 4224/9395/7624 +f 7299/9398/7421 7916/9399/7082 7172/9396/7104 +f 4225/9400/7625 4229/9401/7626 4230/9402/7627 +f 4226/9403/7628 4230/9402/7627 6812/9404/6175 +f 4229/9401/7626 7345/9405/7629 7346/9406/7630 +f 4230/9402/7627 7346/9406/7630 7843/9407/6150 +f 4226/9403/7628 4231/9408/7631 4227/9409/7632 +f 4231/9408/7631 7347/9410/7633 7348/9411/7634 +f 6811/9412/6174 6810/9413/6187 4231/9408/7631 +f 6810/9413/6187 7841/9414/6190 7347/9410/7633 +f 4225/9400/7625 4227/9409/7632 4232/9415/7635 +f 4228/9416/7636 4232/9415/7635 7350/9417/7637 +f 4227/9409/7632 7348/9411/7634 7349/9418/7638 +f 4232/9415/7635 7349/9418/7638 7955/9419/7639 +f 4228/9416/7636 4233/9420/7640 4229/9401/7626 +f 4233/9420/7640 7344/9421/7641 7345/9405/7629 +f 7351/9422/7642 7352/9423/7643 4233/9420/7640 +f 7352/9423/7643 7953/9424/7644 7344/9421/7641 +f 4234/9425/7645 4238/9426/7646 4239/9427/7647 +f 4235/9428/7648 4239/9427/7647 7356/9429/7649 +f 4238/9426/7646 7354/9430/7650 7355/9431/7651 +f 4239/9427/7647 7355/9431/7651 7956/9432/7652 +f 4235/9428/7648 4240/9433/7653 4236/9434/7654 +f 4240/9433/7653 6827/7554/6209 6826/7551/6206 +f 7357/9435/7655 7358/9436/7656 4240/9433/7653 +f 7358/9436/7656 7842/7555/6210 6827/7554/6209 +f 4234/9425/7645 4236/9434/7654 4241/9437/7657 +f 4237/9438/7658 4241/9437/7657 7359/9439/7659 +f 4236/9434/7654 6826/7551/6206 6825/7550/6205 +f 4241/9437/7657 6825/7550/6205 7844/7515/6170 +f 4237/9438/7658 4242/9440/7660 4238/9426/7646 +f 4242/9440/7660 7353/9441/7661 7354/9430/7650 +f 7360/9442/7662 7361/9443/7663 4242/9440/7660 +f 7361/9443/7663 7954/9444/7664 7353/9441/7661 +f 4243/9445/7665 4247/9446/7666 4248/9447/7667 +f 4244/9448/7668 4248/9447/7667 6794/9449/6135 +f 4247/9446/7666 7363/9450/7669 7364/9451/7670 +f 4248/9447/7667 7364/9451/7670 7845/9452/6110 +f 4244/9448/7668 4249/9453/7671 4245/9454/7672 +f 4249/9453/7671 7346/9406/7630 7345/9405/7629 +f 6793/9455/6134 6792/9456/6147 4249/9453/7671 +f 6792/9456/6147 7843/9407/6150 7346/9406/7630 +f 4243/9445/7665 4245/9454/7672 4250/9457/7673 +f 4246/9458/7674 4250/9457/7673 7365/9459/7675 +f 4245/9454/7672 7345/9405/7629 7344/9421/7641 +f 4250/9457/7673 7344/9421/7641 7953/9424/7644 +f 4246/9458/7674 4251/9460/7676 4247/9446/7666 +f 4251/9460/7676 7362/9461/7677 7363/9450/7669 +f 7366/9462/7678 7367/9463/7679 4251/9460/7676 +f 7367/9463/7679 7951/9464/7680 7362/9461/7677 +f 4252/9465/7681 4256/9466/7682 4257/9467/7683 +f 4253/9468/7684 4257/9467/7683 7361/9443/7663 +f 4256/9466/7682 7369/9469/7685 7370/9470/7686 +f 4257/9467/7683 7370/9470/7686 7954/9444/7664 +f 4253/9468/7684 4258/9471/7687 4254/9472/7688 +f 4258/9471/7687 6809/7514/6169 6808/7508/6166 +f 7360/9442/7662 7359/9439/7659 4258/9471/7687 +f 7359/9439/7659 7844/7515/6170 6809/7514/6169 +f 4252/9465/7681 4254/9472/7688 4259/9473/7689 +f 4255/9474/7690 4259/9473/7689 7371/9475/7691 +f 4254/9472/7688 6808/7508/6166 6807/7507/6165 +f 4259/9473/7689 6807/7507/6165 7846/7511/6130 +f 4255/9474/7690 4260/9476/7692 4256/9466/7682 +f 4260/9476/7692 7368/9477/7693 7369/9469/7685 +f 7372/9478/7694 7373/9479/7695 4260/9476/7692 +f 7373/9479/7695 7952/9480/7696 7368/9477/7693 +f 4261/9481/7697 4265/9482/7698 4266/9483/7699 +f 4262/9484/7700 4266/9483/7699 6776/9485/6095 +f 7375/9486/7701 7376/9487/7702 4266/9483/7699 +f 7376/9487/7702 7847/9488/6070 6776/9485/6095 +f 4262/9484/7700 4267/9489/7703 4263/9490/7704 +f 4267/9489/7703 7364/9451/7670 7363/9450/7669 +f 6775/9491/6094 6774/9492/6108 4267/9489/7703 +f 6774/9492/6108 7845/9452/6110 7364/9451/7670 +f 4261/9481/7697 4263/9490/7704 4268/9493/7705 +f 4264/9494/7706 4268/9493/7705 7377/9495/7707 +f 4263/9490/7704 7363/9450/7669 7362/9461/7677 +f 4268/9493/7705 7362/9461/7677 7951/9464/7680 +f 4264/9494/7706 4269/9496/7708 4265/9482/7698 +f 4265/9482/7698 4269/9496/7708 7374/9497/7709 +f 7378/9498/7710 7379/9499/7711 4269/9496/7708 +f 7379/9499/7711 7949/9500/7712 7374/9497/7709 +f 4270/9501/7713 4274/9502/7714 4275/9503/7715 +f 4271/9504/7716 4275/9503/7715 7373/9479/7695 +f 4274/9502/7714 7381/9505/7717 7382/9506/7718 +f 4275/9503/7715 7382/9506/7718 7952/9480/7696 +f 4271/9504/7716 4276/9507/7719 4272/9508/7720 +f 4276/9507/7719 6791/9509/6129 6790/9510/6126 +f 7372/9478/7694 7371/9475/7691 4276/9507/7719 +f 7371/9475/7691 7846/7511/6130 6791/9509/6129 +f 4270/9501/7713 4272/9508/7720 4277/9511/7721 +f 4277/9511/7721 7383/9512/7722 7384/9513/7723 +f 4272/9508/7720 6790/9510/6126 6789/9514/6125 +f 6789/9514/6125 7848/9515/6090 7383/9512/7722 +f 4273/9516/7724 4278/9517/7725 4274/9502/7714 +f 4278/9517/7725 7380/9518/7726 7381/9505/7717 +f 4273/9516/7724 7384/9513/7723 7385/9519/7727 +f 7385/9519/7727 7950/9520/7728 7380/9518/7726 +f 4283/9521/7729 4284/9522/7730 4280/9523/7731 +f 4284/9522/7730 6758/9524/6056 6757/9525/6055 +f 7387/9526/7732 7388/9527/7733 4284/9522/7730 +f 7388/9527/7733 7849/9528/6030 6758/9524/6056 +f 4279/9529/7734 4280/9523/7731 4285/9530/7735 +f 4281/9531/7736 4285/9530/7735 7376/9532/7702 +f 4280/9523/7731 6757/9525/6055 6756/9533/6068 +f 4285/9530/7735 6756/9533/6068 7847/9534/6070 +f 4281/9531/7736 4286/9535/7737 4282/9536/7738 +f 4286/9535/7737 7389/9537/7739 7390/9538/7740 +f 7375/9539/7701 7374/9540/7709 4286/9535/7737 +f 7374/9540/7709 7949/9541/7712 7389/9537/7739 +f 4279/9529/7734 4282/9536/7738 4287/9542/7741 +f 4283/9521/7729 4287/9542/7741 7386/9543/7742 +f 4282/9536/7738 7390/9538/7740 7391/9544/7743 +f 4287/9542/7741 7391/9544/7743 7947/9545/7744 +f 4292/9546/7745 4293/9547/7746 4289/9548/7747 +f 4293/9547/7746 7385/9549/7727 7384/9550/7723 +f 7393/9551/7748 7394/9552/7749 4293/9547/7746 +f 7394/9552/7749 7950/9553/7728 7385/9549/7727 +f 4288/9554/7750 4289/9548/7747 4294/9555/7751 +f 4290/9556/7752 4294/9555/7751 6773/9557/6089 +f 4289/9548/7747 7384/9550/7723 7383/9558/7722 +f 4294/9555/7751 7383/9558/7722 7848/9559/6090 +f 4290/9556/7752 4295/9560/7753 4291/9561/7754 +f 4295/9560/7753 7395/9562/7755 7396/9563/7756 +f 6772/9564/6088 6771/9565/6085 4295/9560/7753 +f 6771/9565/6085 7850/9566/6050 7395/9562/7755 +f 4288/9554/7750 4291/9561/7754 4296/9567/7757 +f 4292/9546/7745 4296/9567/7757 7392/9568/7758 +f 4291/9561/7754 7396/9563/7756 7397/9569/7759 +f 4296/9567/7757 7397/9569/7759 7948/9570/7760 +f 4301/9571/7761 4302/9572/7762 4298/9573/7763 +f 4302/9572/7762 6740/9574/6016 6739/9575/6015 +f 7399/9576/7764 7400/9577/7765 4302/9572/7762 +f 7400/9577/7765 7851/9578/5968 6740/9574/6016 +f 4297/9579/7766 4298/9573/7763 4303/9580/7767 +f 4299/9581/7768 4303/9580/7767 7388/9527/7733 +f 4298/9573/7763 6739/9575/6015 6738/9582/6027 +f 4303/9580/7767 6738/9582/6027 7849/9528/6030 +f 4299/9581/7768 4304/9583/7769 4300/9584/7770 +f 4304/9583/7769 7401/9585/7771 7402/9586/7772 +f 7387/9526/7732 7386/9543/7742 4304/9583/7769 +f 7386/9543/7742 7947/9545/7744 7401/9585/7771 +f 4297/9579/7766 4300/9584/7770 4305/9587/7773 +f 4301/9571/7761 4305/9587/7773 7398/9588/7774 +f 4300/9584/7770 7402/9586/7772 7403/9589/7775 +f 7403/9589/7775 7945/9590/7776 7398/9588/7774 +f 4310/9591/7777 4311/9592/7778 4307/9593/7779 +f 4311/9592/7778 7397/9594/7759 7396/9595/7756 +f 7405/9596/7780 7406/9597/7781 4311/9592/7778 +f 7406/9597/7781 7948/9598/7760 7397/9594/7759 +f 4306/9599/7782 4307/9593/7779 4312/9600/7783 +f 4308/9601/7784 4312/9600/7783 6755/9602/6049 +f 4307/9593/7779 7396/9595/7756 7395/9603/7755 +f 4312/9600/7783 7395/9603/7755 7850/9604/6050 +f 4308/9601/7784 4313/9605/7785 4309/9606/7786 +f 4313/9605/7785 7407/9607/7787 7408/9608/7788 +f 6754/9609/6048 6753/9610/6045 4313/9605/7785 +f 6753/9610/6045 7852/9611/6006 7407/9607/7787 +f 4306/9599/7782 4309/9606/7786 4314/9612/7789 +f 4310/9591/7777 4314/9612/7789 7404/9613/7790 +f 4309/9606/7786 7408/9608/7788 7409/9614/7791 +f 7409/9614/7791 7946/9615/7792 7404/9613/7790 +f 4319/9616/7793 4320/9617/7794 4316/9618/7795 +f 4320/9617/7794 6719/9619/5973 6718/9620/5965 +f 7411/9621/7796 7412/9622/7797 4320/9617/7794 +f 7412/9622/7797 7839/9623/5974 6719/9619/5973 +f 4315/9624/7798 4316/9618/7795 4321/9625/7799 +f 4317/9626/7800 4321/9625/7799 7400/9627/7765 +f 4316/9618/7795 6718/9620/5965 6717/9628/5964 +f 4321/9625/7799 6717/9628/5964 7851/9629/5968 +f 4317/9626/7800 4322/9630/7801 4318/9631/7802 +f 4318/9631/7802 4322/9630/7801 7413/9632/7803 +f 7399/9633/7764 7398/9634/7774 4322/9630/7801 +f 4322/9630/7801 7398/9634/7774 7945/9635/7776 +f 4318/9631/7802 4323/9636/7804 4319/9616/7793 +f 4323/9636/7804 7410/9637/7805 7411/9621/7796 +f 7414/9638/7806 7415/9639/7807 4323/9636/7804 +f 7415/9639/7807 7957/9640/7808 7410/9637/7805 +f 4328/9641/7809 4329/9642/7810 4325/9643/7811 +f 4329/9642/7810 7409/9614/7791 7408/9608/7788 +f 4328/9641/7809 7417/9644/7812 7418/9645/7813 +f 4329/9642/7810 7418/9645/7813 7946/9615/7792 +f 4324/9646/7814 4325/9643/7811 4330/9647/7815 +f 4326/9648/7816 4330/9647/7815 6734/9649/6005 +f 4325/9643/7811 7408/9608/7788 7407/9607/7787 +f 4330/9647/7815 7407/9607/7787 7852/9611/6006 +f 4326/9648/7816 4331/9650/7817 4327/9651/7818 +f 4331/9650/7817 7419/9652/7819 7420/9653/7820 +f 6733/9654/6004 6732/9655/5997 4331/9650/7817 +f 6732/9655/5997 7840/9656/5999 7419/9652/7819 +f 4327/9651/7818 4332/9657/7821 4328/9641/7809 +f 4332/9657/7821 7416/9658/7822 7417/9644/7812 +f 7420/9653/7820 7421/9659/7823 4332/9657/7821 +f 7421/9659/7823 7958/9660/7824 7416/9658/7822 +f 4337/9661/7825 4338/9662/7826 4334/9663/7827 +f 4338/9662/7826 7422/9664/7828 7423/9665/7829 +f 7414/9666/7806 7413/9667/7803 4338/9662/7826 +f 7413/9667/7803 7945/9668/7776 7422/9664/7828 +f 4333/9669/7830 4334/9663/7827 4339/9670/7831 +f 4335/9671/7832 4339/9670/7831 7352/9672/7643 +f 4334/9663/7827 7423/9665/7829 7424/9673/7833 +f 4339/9670/7831 7424/9673/7833 7953/9674/7644 +f 4335/9671/7832 4340/9675/7834 4336/9676/7835 +f 4340/9675/7834 7425/9677/7836 7426/9678/7837 +f 7351/9679/7642 7350/9680/7637 4340/9675/7834 +f 7350/9680/7637 7955/9681/7639 7425/9677/7836 +f 4333/9669/7830 4336/9676/7835 4341/9682/7838 +f 4337/9661/7825 4341/9682/7838 7415/9683/7807 +f 4336/9676/7835 7426/9678/7837 7427/9684/7839 +f 4341/9682/7838 7427/9684/7839 7957/9685/7808 +f 4346/9686/7840 4347/9687/7841 4343/9688/7842 +f 4347/9687/7841 7355/9689/7651 7354/9690/7650 +f 7429/9691/7843 7430/9692/7844 4347/9687/7841 +f 7430/9692/7844 7956/9693/7652 7355/9689/7651 +f 4342/9694/7845 4343/9688/7842 4348/9695/7846 +f 4344/9696/7847 4348/9695/7846 7431/9697/7848 +f 4343/9688/7842 7354/9690/7650 7353/9698/7661 +f 4348/9695/7846 7353/9698/7661 7954/9699/7664 +f 4344/9696/7847 4349/9700/7849 4345/9701/7850 +f 4349/9700/7849 7418/9645/7813 7417/9644/7812 +f 7432/9702/7851 7433/9703/7852 4349/9700/7849 +f 7433/9703/7852 7946/9615/7792 7418/9645/7813 +f 4342/9694/7845 4345/9701/7850 4350/9704/7853 +f 4346/9686/7840 4350/9704/7853 7428/9705/7854 +f 4345/9701/7850 7417/9644/7812 7416/9658/7822 +f 4350/9704/7853 7416/9658/7822 7958/9660/7824 +f 4351/9706/7855 4355/9707/7856 4356/9708/7857 +f 4352/9709/7858 4356/9708/7857 7403/9710/7775 +f 4355/9707/7856 7423/9665/7829 7422/9664/7828 +f 4356/9708/7857 7422/9664/7828 7945/9668/7776 +f 4352/9709/7858 4357/9711/7859 4353/9712/7860 +f 4357/9711/7859 7434/9713/7861 7435/9714/7862 +f 7402/9715/7772 7401/9716/7771 4357/9711/7859 +f 4357/9711/7859 7401/9716/7771 7947/9717/7744 +f 4351/9706/7855 4353/9712/7860 4358/9718/7863 +f 4354/9719/7864 4358/9718/7863 7367/9720/7679 +f 4353/9712/7860 7435/9714/7862 7436/9721/7865 +f 4358/9718/7863 7436/9721/7865 7951/9722/7680 +f 4354/9719/7864 4359/9723/7866 4355/9707/7856 +f 4359/9723/7866 7424/9673/7833 7423/9665/7829 +f 7366/9724/7678 7365/9725/7675 4359/9723/7866 +f 7365/9725/7675 7953/9674/7644 7424/9673/7833 +f 4360/9726/7867 4364/9727/7868 4365/9728/7869 +f 4361/9729/7870 4365/9728/7869 7437/9730/7871 +f 4364/9727/7868 7369/9731/7685 7368/9732/7693 +f 4365/9728/7869 7368/9732/7693 7952/9733/7696 +f 4361/9729/7870 4366/9734/7872 4362/9735/7873 +f 4366/9734/7872 7406/9736/7781 7405/9737/7780 +f 7438/9738/7874 7439/9739/7875 4366/9734/7872 +f 4366/9734/7872 7439/9739/7875 7948/9740/7760 +f 4360/9726/7867 4362/9735/7873 4367/9741/7876 +f 4363/9742/7877 4367/9741/7876 7433/9743/7852 +f 4362/9735/7873 7405/9737/7780 7404/9744/7790 +f 4367/9741/7876 7404/9744/7790 7946/9745/7792 +f 4363/9742/7877 4368/9746/7878 4364/9727/7868 +f 4368/9746/7878 7370/9747/7686 7369/9731/7685 +f 7432/9748/7851 7431/9749/7848 4368/9746/7878 +f 7431/9749/7848 7954/9750/7664 7370/9747/7686 +f 4372/9751/7879 4373/9752/7880 4370/9753/7881 +f 4373/9752/7880 7391/9544/7743 7390/9538/7740 +f 4372/9751/7879 7435/9754/7862 7434/9755/7861 +f 7434/9755/7861 7947/9545/7744 7391/9544/7743 +f 4369/9756/7882 4370/9753/7881 4374/9757/7883 +f 4371/9758/7884 4374/9757/7883 7379/9759/7711 +f 4370/9753/7881 7390/9538/7740 7389/9537/7739 +f 4374/9757/7883 7389/9537/7739 7949/9541/7712 +f 4371/9758/7884 4375/9760/7885 4372/9751/7879 +f 4375/9760/7885 7436/9761/7865 7435/9754/7862 +f 7378/9762/7710 7377/9763/7707 4375/9760/7885 +f 7377/9763/7707 7951/9764/7680 7436/9761/7865 +f 4379/9765/7886 4380/9766/7887 4377/9767/7888 +f 4380/9766/7887 7382/9768/7718 7381/9769/7717 +f 7438/9738/7874 7437/9730/7871 4380/9766/7887 +f 7437/9730/7871 7952/9733/7696 7382/9768/7718 +f 4376/9770/7889 4377/9767/7888 4381/9771/7890 +f 4378/9772/7891 4381/9771/7890 7394/9773/7749 +f 4377/9767/7888 7381/9769/7717 7380/9774/7726 +f 4381/9771/7890 7380/9774/7726 7950/9775/7728 +f 4378/9772/7891 4382/9776/7892 4379/9765/7886 +f 4379/9765/7886 4382/9776/7892 7439/9739/7875 +f 7393/9777/7748 7392/9778/7758 4382/9776/7892 +f 7392/9778/7758 7948/9740/7760 7439/9739/7875 +f 4387/9779/7893 4388/9780/7894 4384/9781/7895 +f 4384/9781/7895 4388/9780/7894 6614/9782/5649 +f 7441/9783/7896 7442/9784/7897 4388/9780/7894 +f 7442/9784/7897 7765/9785/5296 6614/9782/5649 +f 4383/9786/7898 4384/9781/7895 4389/9787/7899 +f 4389/9787/7899 6944/9788/6511 6943/9789/6510 +f 6613/9790/5648 6612/9791/5656 4389/9787/7899 +f 6612/9791/5656 7833/9792/5624 6944/9788/6511 +f 4385/9793/7900 4390/9794/7901 4386/9795/7902 +f 4390/9794/7901 7412/9622/7797 7411/9621/7796 +f 4385/9793/7900 6943/9789/6510 6942/9796/6523 +f 4390/9794/7901 6942/9796/6523 7839/9623/5974 +f 4383/9786/7898 4386/9795/7902 4391/9797/7903 +f 4387/9779/7893 4391/9797/7903 7440/9798/7904 +f 4386/9795/7902 7411/9621/7796 7410/9637/7805 +f 4391/9797/7903 7410/9637/7805 7957/9640/7808 +f 4396/9799/7905 4397/9800/7906 4393/9801/7907 +f 4393/9801/7907 4397/9800/7906 6959/9802/6542 +f 7420/9653/7820 7419/9652/7819 4397/9800/7906 +f 4397/9800/7906 7419/9652/7819 7840/9656/5999 +f 4392/9803/7908 4393/9801/7907 4398/9804/7909 +f 4398/9804/7909 6617/9805/5668 6616/9806/5666 +f 6958/9807/6541 6957/9808/6538 4398/9804/7909 +f 6957/9808/6538 7834/9809/5640 6617/9805/5668 +f 4394/9810/7910 4399/9811/7911 4395/9812/7912 +f 4399/9811/7911 7443/9813/7913 7444/9814/7914 +f 4394/9810/7910 6616/9806/5666 6615/9815/5665 +f 6615/9815/5665 7766/9816/5326 7443/9813/7913 +f 4392/9803/7908 4395/9812/7912 4400/9817/7915 +f 4396/9799/7905 4400/9817/7915 7421/9659/7823 +f 4395/9812/7912 7444/9814/7914 7445/9818/7916 +f 4400/9817/7915 7445/9818/7916 7958/9660/7824 +f 4401/9819/7917 4405/9820/7918 4406/9821/7919 +f 4402/9822/7920 4406/9821/7919 7442/9823/7897 +f 4405/9820/7918 6673/7143/5833 6672/7142/5832 +f 4406/9821/7919 6672/7142/5832 7765/7146/5296 +f 4402/9822/7920 4407/9824/7921 4403/9825/7922 +f 4407/9824/7921 7427/9826/7839 7426/9827/7837 +f 7441/9828/7896 7440/9829/7904 4407/9824/7921 +f 7440/9829/7904 7957/9830/7808 7427/9826/7839 +f 4401/9819/7917 4403/9825/7922 4408/9831/7923 +f 4408/9831/7923 7446/9832/7924 7447/9833/7925 +f 4403/9825/7922 7426/9827/7837 7425/9834/7836 +f 4408/9831/7923 7425/9834/7836 7955/9835/7639 +f 4404/9836/7926 4409/9837/7927 4405/9820/7918 +f 4409/9837/7927 6674/7151/5838 6673/7143/5833 +f 4404/9836/7926 7447/9833/7925 7448/9838/7928 +f 4409/9837/7927 7448/9838/7928 7773/7152/5839 +f 4410/9839/7929 4414/9840/7930 4415/9841/7931 +f 4411/9842/7932 4415/9841/7931 7430/9843/7844 +f 7450/9844/7933 7451/9845/7934 4415/9841/7931 +f 4415/9841/7931 7451/9845/7934 7956/9846/7652 +f 4411/9842/7932 4416/9847/7935 4412/9848/7936 +f 4416/9847/7935 7445/9849/7916 7444/9850/7914 +f 7429/9851/7843 7428/9852/7854 4416/9847/7935 +f 7428/9852/7854 7958/9853/7824 7445/9849/7916 +f 4410/9839/7929 4412/9848/7936 4417/9854/7937 +f 4413/9855/7938 4417/9854/7937 6689/7181/5866 +f 4412/9848/7936 7444/9850/7914 7443/9856/7913 +f 4417/9854/7937 7443/9856/7913 7766/6472/5326 +f 4413/9855/7938 4418/9857/7939 4414/9840/7930 +f 4414/9840/7930 4418/9857/7939 7449/9858/7940 +f 6688/7180/5865 6687/7174/5859 4418/9857/7939 +f 4418/9857/7939 6687/7174/5859 7774/7177/5862 +f 4419/9859/7941 4423/9860/7942 4424/9861/7943 +f 4420/9862/7944 4424/9861/7943 6698/9863/5897 +f 4423/9860/7942 6925/9864/6455 6924/9865/6454 +f 4424/9861/7943 6924/9865/6454 7771/9866/5727 +f 4420/9862/7944 4425/9867/7945 4421/9868/7946 +f 4425/9867/7945 7448/9869/7928 7447/9870/7925 +f 6697/9871/5896 6696/9872/5907 4425/9867/7945 +f 6696/9872/5907 7773/9873/5839 7448/9869/7928 +f 4419/9859/7941 4421/9868/7946 4426/9874/7947 +f 4426/9874/7947 7349/9875/7638 7348/9876/7634 +f 4421/9868/7946 7447/9870/7925 7446/9877/7924 +f 7446/9877/7924 7955/9878/7639 7349/9875/7638 +f 4422/9879/7948 4427/9880/7949 4423/9860/7942 +f 4427/9880/7949 6926/9881/6459 6925/9864/6455 +f 4422/9879/7948 7348/9876/7634 7347/9882/7633 +f 4427/9880/7949 7347/9882/7633 7841/9883/6190 +f 4428/9884/7950 4432/9885/7951 4433/9886/7952 +f 4429/9887/7953 4433/9886/7952 7451/9888/7934 +f 7357/9889/7655 7356/9890/7649 4433/9886/7952 +f 7356/9890/7649 7956/9891/7652 7451/9888/7934 +f 4429/9887/7953 4434/9892/7954 4430/9893/7955 +f 4434/9892/7954 6707/9894/5922 6706/9895/5920 +f 7450/9896/7933 7449/9897/7940 4434/9892/7954 +f 7449/9897/7940 7774/9898/5862 6707/9894/5922 +f 4428/9884/7950 4430/9893/7955 4435/9899/7956 +f 4431/9900/7957 4435/9899/7956 6935/9901/6478 +f 4430/9893/7955 6706/9895/5920 6705/9902/5919 +f 4435/9899/7956 6705/9902/5919 7772/9903/5746 +f 4431/9900/7957 4436/9904/7958 4432/9885/7951 +f 4432/9885/7951 4436/9904/7958 7358/9905/7656 +f 6934/9906/6477 6933/9907/6474 4436/9904/7958 +f 4436/9904/7958 6933/9907/6474 7842/9908/6210 +f 2/4/4 1/1/1 6/3/3 +f 4441/13/13 2/4/4 4440/5/5 +f 6/3/3 5/2/2 4439/7/7 +f 4440/5/5 6/3/3 7498/8/8 +f 1/1/1 2/4/4 3/10/10 +f 3/10/10 7/9/9 4444/12/12 +f 2/4/4 4441/13/13 7/9/9 +f 7/9/9 4442/14/14 4443/11/11 +f 4/17/17 1/1/1 8/16/16 +f 4447/23/23 4/17/17 4446/18/18 +f 8/16/16 3/10/10 4445/19/19 +f 4446/18/18 8/16/16 7454/20/20 +f 1/1/1 4/17/17 5/2/2 +f 5/2/2 9/21/21 4438/6/6 +f 4/17/17 4447/23/23 9/21/21 +f 9/21/21 4448/24/24 4437/22/22 +f 11/29/29 10/26/26 15/28/28 +f 4453/38/38 11/29/29 4452/30/30 +f 15/28/28 14/27/27 4451/32/32 +f 4452/30/30 15/28/28 7455/33/33 +f 10/26/26 11/29/29 12/35/35 +f 12/35/35 16/34/34 4456/37/37 +f 11/29/29 4453/38/38 16/34/34 +f 16/34/34 4454/39/39 4455/36/36 +f 13/42/42 10/26/26 17/41/41 +f 4459/48/48 13/42/42 4458/43/43 +f 17/41/41 12/35/35 4457/44/44 +f 4458/43/43 17/41/41 7499/45/45 +f 10/26/26 13/42/42 14/27/27 +f 14/27/27 18/46/46 4450/31/31 +f 13/42/42 4459/48/48 18/46/46 +f 18/46/46 4460/49/49 4449/47/47 +f 20/54/54 19/51/51 24/53/53 +f 4447/23/23 20/54/54 4448/24/24 +f 24/53/53 23/52/52 4463/56/56 +f 4448/24/24 24/53/53 7496/25/25 +f 19/51/51 20/54/54 21/58/58 +f 21/58/58 25/57/57 4465/60/60 +f 20/54/54 4447/23/23 25/57/57 +f 25/57/57 4446/18/18 4464/59/59 +f 22/62/62 19/51/51 26/61/61 +f 4468/68/68 22/62/62 4467/63/63 +f 26/61/61 21/58/58 4466/64/64 +f 4467/63/63 26/61/61 7456/65/65 +f 19/51/51 22/62/62 23/52/52 +f 23/52/52 27/66/66 4462/55/55 +f 22/62/62 4468/68/68 27/66/66 +f 27/66/66 4469/69/69 4461/67/67 +f 29/74/74 28/71/71 33/73/73 +f 4474/81/81 29/74/74 4473/75/75 +f 33/73/73 32/72/72 4472/77/77 +f 4473/75/75 33/73/73 7457/78/78 +f 28/71/71 29/74/74 30/80/80 +f 30/80/80 34/79/79 4450/31/31 +f 29/74/74 4474/81/81 34/79/79 +f 34/79/79 4475/82/82 4451/32/32 +f 31/84/84 28/71/71 35/83/83 +f 4477/88/88 31/84/84 4476/85/85 +f 35/83/83 30/80/80 4449/47/47 +f 4476/85/85 35/83/83 7497/50/50 +f 28/71/71 31/84/84 32/72/72 +f 32/72/72 36/86/86 4471/76/76 +f 31/84/84 4477/88/88 36/86/86 +f 36/86/86 4478/89/89 4470/87/87 +f 38/94/94 37/91/91 42/93/93 +f 4480/103/100 38/94/94 4479/95/95 +f 42/93/93 41/92/92 4464/97/59 +f 4479/95/95 42/93/93 7454/98/20 +f 37/91/91 38/94/94 39/100/97 +f 39/100/97 43/99/96 4483/102/99 +f 38/94/94 4480/103/100 43/99/96 +f 43/99/96 4481/104/101 4482/101/98 +f 40/107/104 37/91/91 44/106/103 +f 4486/113/109 40/107/104 4485/108/105 +f 44/106/103 39/100/97 4484/109/106 +f 4485/108/105 44/106/103 7458/110/107 +f 37/91/91 40/107/104 41/92/92 +f 41/92/92 45/111/108 4465/96/60 +f 40/107/104 4486/113/109 45/111/108 +f 45/111/108 4487/114/110 4466/112/64 +f 47/119/114 46/116/111 51/118/113 +f 4492/128/123 47/119/114 4491/120/115 +f 51/118/113 50/117/112 4490/122/117 +f 4491/120/115 51/118/113 7459/123/118 +f 46/116/111 47/119/114 48/125/120 +f 48/125/120 52/124/119 4495/127/122 +f 47/119/114 4492/128/123 52/124/119 +f 52/124/119 4493/129/124 4494/126/121 +f 49/132/127 46/116/111 53/131/126 +f 4474/81/81 49/132/127 4475/82/82 +f 53/131/126 48/125/120 4496/133/128 +f 4475/82/82 53/131/126 7455/33/33 +f 46/116/111 49/132/127 50/117/112 +f 50/117/112 54/134/129 4489/121/116 +f 49/132/127 4474/81/81 54/134/129 +f 54/134/129 4473/75/75 4488/135/130 +f 56/139/134 55/136/131 60/138/133 +f 4498/145/140 56/139/134 4497/140/135 +f 60/138/133 59/137/132 4443/11/11 +f 4497/140/135 60/138/133 7452/15/15 +f 55/136/131 56/139/134 57/142/137 +f 57/142/137 61/141/136 4501/144/139 +f 56/139/134 4498/145/140 61/141/136 +f 61/141/136 4499/146/141 4500/143/138 +f 58/149/144 55/136/131 62/148/143 +f 4480/154/100 58/149/144 4481/150/101 +f 62/148/143 57/142/137 4502/151/145 +f 4481/150/101 62/148/143 7460/152/102 +f 55/136/131 58/149/144 59/137/132 +f 59/137/132 63/153/146 4444/12/12 +f 58/149/144 4480/154/100 63/153/146 +f 63/153/146 4479/155/95 4445/19/19 +f 65/159/150 64/156/147 69/158/149 +f 4504/165/156 65/159/150 4503/160/151 +f 69/158/149 68/157/148 4494/126/121 +f 4503/160/151 69/158/149 7461/130/125 +f 64/156/147 65/159/150 66/162/153 +f 66/162/153 70/161/152 4507/164/155 +f 65/159/150 4504/165/156 70/161/152 +f 70/161/152 4505/166/157 4506/163/154 +f 67/169/160 64/156/147 71/168/159 +f 4453/38/38 67/169/160 4454/39/39 +f 71/168/159 66/162/153 4508/170/161 +f 4454/39/39 71/168/159 7453/40/40 +f 64/156/147 67/169/160 68/157/148 +f 68/157/148 72/171/162 4495/127/122 +f 67/169/160 4453/38/38 72/171/162 +f 72/171/162 4452/30/30 4496/133/128 +f 74/175/166 73/172/163 78/174/165 +f 4510/184/172 74/175/166 4509/176/167 +f 78/174/165 77/173/164 4500/178/138 +f 4509/176/167 78/174/165 7462/179/142 +f 73/172/163 74/175/166 75/181/169 +f 75/181/169 79/180/168 4513/183/171 +f 74/175/166 4510/184/172 79/180/168 +f 79/180/168 4511/185/173 4512/182/170 +f 76/188/176 73/172/163 80/187/175 +f 4516/194/181 76/188/176 4515/189/177 +f 80/187/175 75/181/169 4514/190/178 +f 4515/189/177 80/187/175 7466/191/179 +f 73/172/163 76/188/176 77/173/164 +f 77/173/164 81/192/180 4501/177/139 +f 76/188/176 4516/194/181 81/192/180 +f 81/192/180 4517/195/182 4502/193/145 +f 83/200/186 82/197/183 87/199/185 +f 4522/209/195 83/200/186 4521/201/187 +f 87/199/185 86/198/184 4520/203/189 +f 4521/201/187 87/199/185 7467/204/190 +f 82/197/183 83/200/186 84/206/192 +f 84/206/192 88/205/191 4525/208/194 +f 83/200/186 4522/209/195 88/205/191 +f 88/205/191 4523/210/196 4524/207/193 +f 85/213/199 82/197/183 89/212/198 +f 4504/165/156 85/213/199 4505/166/157 +f 89/212/198 84/206/192 4526/214/200 +f 4505/166/157 89/212/198 7463/167/158 +f 82/197/183 85/213/199 86/198/184 +f 86/198/184 90/215/201 4519/202/188 +f 85/213/199 4504/165/156 90/215/201 +f 90/215/201 4503/160/151 4518/216/202 +f 92/220/206 91/217/203 96/219/205 +f 4516/194/181 92/220/206 4517/195/182 +f 96/219/205 95/218/204 4482/222/98 +f 4517/195/182 96/219/205 7460/196/102 +f 91/217/203 92/220/206 93/224/208 +f 93/224/208 97/223/207 4528/226/210 +f 92/220/206 4516/194/181 97/223/207 +f 97/223/207 4515/189/177 4527/225/209 +f 94/228/212 91/217/203 98/227/211 +f 4531/234/217 94/228/212 4530/229/213 +f 98/227/211 93/224/208 4529/230/214 +f 4530/229/213 98/227/211 7468/231/215 +f 91/217/203 94/228/212 95/218/204 +f 95/218/204 99/232/216 4483/221/99 +f 94/228/212 4531/234/217 99/232/216 +f 99/232/216 4532/235/218 4484/233/106 +f 101/240/222 100/237/219 105/239/221 +f 4537/247/229 101/240/222 4536/241/223 +f 105/239/221 104/238/220 4535/243/225 +f 4536/241/223 105/239/221 7469/244/226 +f 100/237/219 101/240/222 102/246/228 +f 102/246/228 106/245/227 4519/202/188 +f 101/240/222 4537/247/229 106/245/227 +f 106/245/227 4538/248/230 4520/203/189 +f 103/250/232 100/237/219 107/249/231 +f 4492/128/123 103/250/232 4493/129/124 +f 107/249/231 102/246/228 4518/216/202 +f 4493/129/124 107/249/231 7461/130/125 +f 100/237/219 103/250/232 104/238/220 +f 104/238/220 108/251/233 4534/242/224 +f 103/250/232 4492/128/123 108/251/233 +f 108/251/233 4491/120/115 4533/252/234 +f 110/256/238 109/253/235 114/255/237 +f 4540/261/243 110/256/238 4539/257/239 +f 114/255/237 113/254/236 4527/225/209 +f 4539/257/239 114/255/237 7466/191/179 +f 111/259/241 109/253/235 115/258/240 +f 4543/267/249 111/259/241 4542/260/242 +f 115/258/240 110/256/238 4541/262/244 +f 4542/260/242 115/258/240 7472/263/245 +f 109/253/235 111/259/241 112/265/247 +f 4546/271/253 112/265/247 4545/266/248 +f 111/259/241 4543/267/249 116/264/246 +f 116/264/246 4544/268/250 4545/266/248 +f 109/253/235 112/265/247 113/254/236 +f 113/254/236 117/270/252 4528/226/210 +f 112/265/247 4546/271/253 117/270/252 +f 117/270/252 4547/272/254 4529/230/214 +f 118/281/263 122/273/255 119/275/257 +f 119/275/257 123/274/256 4552/277/259 +f 123/274/256 122/273/255 4550/279/261 +f 123/274/256 4550/279/261 4551/276/258 +f 120/283/265 118/281/263 124/282/264 +f 4555/290/271 120/283/265 4554/284/266 +f 124/282/264 119/275/257 4553/285/267 +f 4554/284/266 124/282/264 7473/286/268 +f 121/288/270 118/281/263 125/287/269 +f 4537/295/229 121/288/270 4538/289/230 +f 125/287/269 120/283/265 4556/291/272 +f 4538/289/230 125/287/269 7467/292/190 +f 118/281/263 121/288/270 122/273/255 +f 122/273/255 126/293/273 4549/278/260 +f 121/288/270 4537/295/229 126/293/273 +f 126/293/273 4536/296/223 4548/294/274 +f 128/301/278 127/298/275 132/300/277 +f 4558/307/284 128/301/278 4557/302/279 +f 132/300/277 131/299/276 4512/182/170 +f 4557/302/279 132/300/277 7464/186/174 +f 127/298/275 128/301/278 129/304/281 +f 129/304/281 133/303/280 4561/306/283 +f 128/301/278 4558/307/284 133/303/280 +f 133/303/280 4559/308/285 4560/305/282 +f 130/312/289 127/298/275 134/310/287 +f 130/312/289 134/310/287 4540/261/243 +f 129/304/281 4561/306/283 134/310/287 +f 134/310/287 4562/311/288 4541/262/244 +f 127/298/275 130/312/289 131/299/276 +f 131/299/276 135/313/290 4513/183/171 +f 130/312/289 4540/261/243 135/313/290 +f 135/313/290 4539/257/239 4514/190/178 +f 137/319/296 136/314/291 141/316/293 +f 137/319/296 141/316/293 4564/318/295 +f 140/315/292 4555/290/271 141/316/293 +f 141/316/293 4554/284/266 4563/317/294 +f 136/314/291 137/319/296 138/321/298 +f 138/321/298 142/320/297 4567/323/300 +f 137/319/296 4564/318/295 142/320/297 +f 142/320/297 4565/324/301 4566/322/299 +f 139/327/304 136/314/291 143/326/303 +f 4522/332/195 139/327/304 4523/328/196 +f 143/326/303 138/321/298 4568/329/305 +f 4523/328/196 143/326/303 7465/330/197 +f 136/314/291 139/327/304 140/315/292 +f 140/315/292 144/331/306 4555/290/271 +f 139/327/304 4522/332/195 144/331/306 +f 144/331/306 4521/333/187 4556/291/272 +f 146/337/310 145/334/307 150/336/309 +f 4570/345/315 146/337/310 4569/338/311 +f 150/336/309 149/335/308 4560/340/282 +f 4569/338/311 150/336/309 7474/341/286 +f 145/334/307 146/337/310 147/343/313 +f 4573/351/321 147/343/313 4572/344/314 +f 151/342/312 146/337/310 4571/346/316 +f 4572/344/314 151/342/312 7476/347/317 +f 148/354/324 145/334/307 152/348/318 +f 148/354/324 152/348/318 4576/350/320 +f 147/343/313 4573/351/321 152/348/318 +f 152/348/318 4574/352/322 4575/349/319 +f 145/334/307 148/354/324 149/335/308 +f 149/335/308 153/355/325 4561/339/283 +f 148/354/324 4576/350/320 153/355/325 +f 153/355/325 4577/357/326 4562/356/288 +f 155/367/335 154/359/327 159/361/329 +f 155/367/335 159/361/329 4582/363/331 +f 158/360/328 4579/364/332 159/361/329 +f 159/361/329 4580/365/333 4581/362/330 +f 154/359/327 155/367/335 156/369/337 +f 4585/375/343 156/369/337 4584/370/338 +f 160/368/336 155/367/335 4583/371/339 +f 4584/370/338 160/368/336 7477/372/340 +f 157/374/342 154/359/327 161/373/341 +f 4564/318/295 157/374/342 4565/324/301 +f 161/373/341 156/369/337 4586/376/344 +f 4565/324/301 161/373/341 7475/325/302 +f 154/359/327 157/374/342 158/360/328 +f 158/360/328 162/377/345 4579/364/332 +f 157/374/342 4564/318/295 162/377/345 +f 162/377/345 4563/317/294 4578/378/346 +f 163/384/350 167/379/347 164/381/349 +f 164/381/349 168/380/348 4576/350/320 +f 167/379/347 4543/382/249 168/380/348 +f 4577/357/326 168/380/348 7472/358/245 +f 165/386/352 163/384/350 169/385/351 +f 4588/392/358 165/386/352 4587/387/353 +f 169/385/351 164/381/349 4575/349/319 +f 4587/387/353 169/385/351 7478/353/323 +f 163/384/350 165/386/352 166/389/355 +f 166/389/355 170/388/354 4591/391/357 +f 165/386/352 4588/392/358 170/388/354 +f 170/388/354 4589/393/359 4590/390/356 +f 167/379/347 163/384/350 171/395/361 +f 4543/382/249 167/379/347 4544/396/250 +f 171/395/361 166/389/355 4592/397/362 +f 4544/396/250 171/395/361 7470/398/251 +f 172/407/371 176/399/363 173/401/365 +f 173/401/365 177/400/364 4597/403/367 +f 176/399/363 4594/404/368 177/400/364 +f 177/400/364 4595/405/369 4596/402/366 +f 174/409/373 172/407/371 178/408/372 +f 4579/364/332 174/409/373 4580/365/333 +f 178/408/372 173/401/365 4598/410/374 +f 4580/365/333 178/408/372 7479/366/334 +f 172/407/371 174/409/373 175/412/376 +f 175/412/376 179/411/375 4552/277/259 +f 174/409/373 4579/364/332 179/411/375 +f 4553/285/267 179/411/375 7473/286/268 +f 176/399/363 172/407/371 180/413/377 +f 4594/404/368 176/399/363 4593/414/378 +f 180/413/377 175/412/376 4551/276/258 +f 4593/414/378 180/413/377 7471/280/262 +f 181/420/384 185/415/379 182/417/381 +f 182/417/381 186/416/380 4600/419/383 +f 185/415/379 4588/392/358 186/416/380 +f 186/416/380 4587/387/353 4599/418/382 +f 183/422/386 181/420/384 187/421/385 +f 4603/430/394 183/422/386 4602/423/387 +f 187/421/385 182/417/381 4601/424/388 +f 4602/423/387 187/421/385 7484/425/389 +f 181/420/384 183/422/386 184/427/391 +f 184/427/391 188/426/390 4606/429/393 +f 183/422/386 4603/430/394 188/426/390 +f 188/426/390 4604/431/395 4605/428/392 +f 185/415/379 181/420/384 189/433/397 +f 4588/392/358 185/415/379 4589/393/359 +f 189/433/397 184/427/391 4607/434/398 +f 4589/393/359 189/433/397 7480/394/360 +f 190/443/407 194/435/399 191/437/401 +f 191/437/401 195/436/400 4612/439/403 +f 194/435/399 4609/440/404 195/436/400 +f 195/436/400 4610/441/405 4611/438/402 +f 192/445/409 190/443/407 196/444/408 +f 4615/451/415 192/445/409 4614/446/410 +f 196/444/408 191/437/401 4613/447/411 +f 4614/446/410 196/444/408 7485/448/412 +f 190/443/407 192/445/409 193/450/414 +f 193/450/414 197/449/413 4597/403/367 +f 192/445/409 4615/451/415 197/449/413 +f 197/449/413 4616/452/416 4598/410/374 +f 194/435/399 190/443/407 198/453/417 +f 4609/440/404 194/435/399 4608/454/418 +f 198/453/417 193/450/414 4596/402/366 +f 4608/454/418 198/453/417 7481/406/370 +f 199/463/424 203/455/419 200/457/421 +f 200/457/421 204/456/420 4618/459/423 +f 203/455/419 4573/460/321 204/456/420 +f 204/456/420 4572/461/314 4617/458/422 +f 201/465/426 199/463/424 205/464/425 +f 4621/473/432 201/465/426 4620/466/427 +f 205/464/425 200/457/421 4619/467/428 +f 4620/466/427 205/464/425 7486/468/429 +f 199/463/424 201/465/426 202/470/431 +f 202/470/431 206/469/430 4600/472/383 +f 201/465/426 4621/473/432 206/469/430 +f 206/469/430 4622/474/433 4601/471/388 +f 203/455/419 199/463/424 207/476/434 +f 4573/460/321 203/455/419 4574/477/322 +f 207/476/434 202/470/431 4599/478/382 +f 4574/477/322 207/476/434 7478/479/323 +f 208/485/440 212/480/435 209/482/437 +f 209/482/437 213/481/436 4624/484/439 +f 212/480/435 4615/451/415 213/481/436 +f 213/481/436 4614/446/410 4623/483/438 +f 210/487/442 208/485/440 214/486/441 +f 4627/493/448 210/487/442 4626/488/443 +f 214/486/441 209/482/437 4625/489/444 +f 4626/488/443 214/486/441 7487/490/445 +f 208/485/440 210/487/442 211/492/447 +f 211/492/447 215/491/446 4582/363/331 +f 210/487/442 4627/493/448 215/491/446 +f 215/491/446 4628/494/449 4583/371/339 +f 212/480/435 208/485/440 216/495/450 +f 4615/451/415 212/480/435 4616/452/416 +f 216/495/450 211/492/447 4581/362/330 +f 4616/452/416 216/495/450 7479/366/334 +f 217/501/456 221/496/451 218/498/453 +f 218/498/453 222/497/452 4630/500/455 +f 221/496/451 4621/473/432 222/497/452 +f 222/497/452 4620/466/427 4629/499/454 +f 219/503/458 217/501/456 223/502/457 +f 4633/511/466 219/503/458 4632/504/459 +f 223/502/457 218/498/453 4631/505/460 +f 4632/504/459 223/502/457 7488/506/461 +f 217/501/456 219/503/458 220/508/463 +f 220/508/463 224/507/462 4636/510/465 +f 219/503/458 4633/511/466 224/507/462 +f 224/507/462 4634/512/467 4635/509/464 +f 221/496/451 217/501/456 225/514/469 +f 4621/473/432 221/496/451 4622/474/433 +f 225/514/469 220/508/463 4637/515/470 +f 4622/474/433 225/514/469 7484/475/389 +f 226/524/479 230/516/471 227/518/473 +f 227/518/473 231/517/472 4642/520/475 +f 230/516/471 4639/521/476 231/517/472 +f 231/517/472 4640/522/477 4641/519/474 +f 228/526/481 226/524/479 232/525/480 +f 4645/534/487 228/526/481 4644/527/482 +f 232/525/480 227/518/473 4643/528/483 +f 4644/527/482 232/525/480 7489/529/484 +f 226/524/479 228/526/481 229/531/486 +f 229/531/486 233/530/485 4624/533/439 +f 228/526/481 4645/534/487 233/530/485 +f 233/530/485 4646/535/488 4625/532/444 +f 230/516/471 226/524/479 234/537/489 +f 4639/521/476 230/516/471 4638/538/490 +f 234/537/489 229/531/486 4623/539/438 +f 4638/538/490 234/537/489 7485/540/412 +f 235/546/494 239/541/491 236/543/493 +f 236/543/493 240/542/492 4636/510/465 +f 239/541/491 4603/544/394 240/542/492 +f 240/542/492 4602/545/387 4637/515/470 +f 237/548/496 235/546/494 241/547/495 +f 4648/554/502 237/548/496 4647/549/497 +f 241/547/495 236/543/493 4635/509/464 +f 4647/549/497 241/547/495 7490/513/468 +f 235/546/494 237/548/496 238/551/499 +f 238/551/499 242/550/498 4651/553/501 +f 237/548/496 4648/554/502 242/550/498 +f 242/550/498 4649/555/503 4650/552/500 +f 239/541/491 235/546/494 243/557/505 +f 4603/544/394 239/541/491 4604/558/395 +f 243/557/505 238/551/499 4652/559/506 +f 4604/558/395 243/557/505 7482/560/396 +f 244/569/515 248/561/507 245/563/509 +f 245/563/509 249/562/508 4657/565/511 +f 248/561/507 4654/566/512 249/562/508 +f 249/562/508 4655/567/513 4656/564/510 +f 246/571/517 244/569/515 250/570/516 +f 4639/521/476 246/571/517 4640/522/477 +f 250/570/516 245/563/509 4658/572/518 +f 4640/522/477 250/570/516 7491/523/478 +f 244/569/515 246/571/517 247/574/520 +f 247/574/520 251/573/519 4612/576/403 +f 246/571/517 4639/521/476 251/573/519 +f 251/573/519 4638/538/490 4613/575/411 +f 248/561/507 244/569/515 252/577/521 +f 4654/566/512 248/561/507 4653/578/522 +f 252/577/521 247/574/520 4611/579/402 +f 4653/578/522 252/577/521 7483/580/406 +f 253/586/528 257/581/523 254/583/525 +f 254/583/525 258/582/524 4660/585/527 +f 257/581/523 4648/554/502 258/582/524 +f 258/582/524 4647/549/497 4659/584/526 +f 255/588/530 253/586/528 259/587/529 +f 4462/55/55 255/588/530 4463/56/56 +f 259/587/529 254/583/525 4661/589/531 +f 4463/56/56 259/587/529 7496/25/25 +f 253/586/528 255/588/530 256/591/533 +f 256/591/533 260/590/532 4663/593/535 +f 255/588/530 4462/55/55 260/590/532 +f 260/590/532 4461/67/67 4662/592/534 +f 257/581/523 253/586/528 261/594/536 +f 4648/554/502 257/581/523 4649/555/503 +f 261/594/536 256/591/533 4664/595/537 +f 4649/555/503 261/594/536 7492/556/504 +f 262/604/543 266/596/538 263/598/540 +f 263/598/540 267/597/539 4477/600/88 +f 266/596/538 4666/601/541 267/597/539 +f 267/597/539 4667/602/542 4478/599/89 +f 264/606/545 262/604/543 268/605/544 +f 4669/612/549 264/606/545 4668/607/546 +f 268/605/544 263/598/540 4476/608/85 +f 4668/607/546 268/605/544 7497/609/50 +f 262/604/543 264/606/545 265/611/548 +f 265/611/548 269/610/547 4657/565/511 +f 264/606/545 4669/612/549 269/610/547 +f 269/610/547 4670/613/550 4658/572/518 +f 266/596/538 262/604/543 270/614/551 +f 4666/601/541 266/596/538 4665/615/552 +f 270/614/551 265/611/548 4656/564/510 +f 4665/615/552 270/614/551 7493/568/514 +f 271/621/558 275/616/553 272/618/555 +f 272/618/555 276/617/554 4672/620/557 +f 275/616/553 4633/511/466 276/617/554 +f 276/617/554 4632/504/459 4671/619/556 +f 273/623/560 271/621/558 277/622/559 +f 4438/6/6 273/623/560 4439/7/7 +f 277/622/559 272/618/555 4673/624/561 +f 4439/7/7 277/622/559 7498/8/8 +f 271/621/558 273/623/560 274/626/563 +f 274/626/563 278/625/562 4660/585/527 +f 273/623/560 4438/6/6 278/625/562 +f 278/625/562 4437/22/22 4661/589/531 +f 275/616/553 271/621/558 279/627/564 +f 4633/511/466 275/616/553 4634/512/467 +f 279/627/564 274/626/563 4659/584/526 +f 4634/512/467 279/627/564 7490/513/468 +f 280/633/568 284/628/565 281/630/567 +f 281/630/567 285/629/566 4459/632/48 +f 284/628/565 4669/612/549 285/629/566 +f 285/629/566 4668/607/546 4460/631/49 +f 282/635/570 280/633/568 286/634/569 +f 4675/641/574 282/635/570 4674/636/571 +f 286/634/569 281/630/567 4458/637/43 +f 4674/636/571 286/634/569 7499/638/45 +f 280/633/568 282/635/570 283/640/573 +f 283/640/573 287/639/572 4642/520/475 +f 282/635/570 4675/641/574 287/639/572 +f 287/639/572 4676/642/575 4643/528/483 +f 284/628/565 280/633/568 288/643/576 +f 4669/612/549 284/628/565 4670/613/550 +f 288/643/576 283/640/573 4641/519/474 +f 4670/613/550 288/643/576 7491/523/478 +f 289/652/582 293/644/577 290/646/579 +f 290/646/579 294/645/578 4672/648/557 +f 293/644/577 4678/649/580 294/645/578 +f 294/645/578 4679/650/581 4673/647/561 +f 291/654/584 289/652/582 295/653/583 +f 4681/662/590 291/654/584 4680/655/585 +f 295/653/583 290/646/579 4671/656/556 +f 4680/655/585 295/653/583 7488/657/461 +f 289/652/582 291/654/584 292/659/587 +f 292/659/587 296/658/586 4684/661/589 +f 291/654/584 4681/662/590 296/658/586 +f 296/658/586 4682/663/591 4683/660/588 +f 293/644/577 289/652/582 297/665/593 +f 4678/649/580 293/644/577 4677/666/594 +f 292/659/587 4684/661/589 297/665/593 +f 297/665/593 4685/667/595 4677/666/594 +f 298/677/605 302/669/597 299/671/599 +f 299/671/599 303/670/598 4690/673/601 +f 302/669/597 4687/674/602 303/670/598 +f 303/670/598 4688/675/603 4689/672/600 +f 300/679/607 298/677/605 304/678/606 +f 4675/641/574 300/679/607 4676/642/575 +f 304/678/606 299/671/599 4691/680/608 +f 4676/642/575 304/678/606 7489/529/484 +f 298/677/605 300/679/607 301/682/610 +f 301/682/610 305/681/609 4693/684/612 +f 300/679/607 4675/641/574 305/681/609 +f 305/681/609 4674/636/571 4692/683/611 +f 302/669/597 298/677/605 306/685/613 +f 302/669/597 306/685/613 4687/674/602 +f 306/685/613 301/682/610 4694/687/615 +f 306/685/613 4694/687/615 4686/686/614 +f 307/694/620 311/689/617 308/691/619 +f 308/691/619 312/690/618 4630/693/455 +f 311/689/617 4681/662/590 312/690/618 +f 312/690/618 4680/655/585 4631/692/460 +f 309/696/622 307/694/620 313/695/621 +f 4696/704/628 309/696/622 4695/697/623 +f 313/695/621 308/691/619 4629/698/454 +f 4695/697/623 313/695/621 7486/699/429 +f 307/694/620 309/696/622 310/701/625 +f 310/701/625 314/700/624 4699/703/627 +f 309/696/622 4696/704/628 314/700/624 +f 314/700/624 4697/705/629 4698/702/626 +f 311/689/617 307/694/620 315/707/631 +f 4681/662/590 311/689/617 4682/663/591 +f 315/707/631 310/701/625 4700/708/632 +f 4682/663/591 315/707/631 7502/664/592 +f 316/717/641 320/709/633 317/711/635 +f 317/711/635 321/710/634 4705/713/637 +f 320/709/633 4702/714/638 321/710/634 +f 321/710/634 4703/715/639 4704/712/636 +f 318/719/643 316/717/641 322/718/642 +f 4645/727/487 318/719/643 4646/720/488 +f 322/718/642 317/711/635 4706/721/644 +f 4646/720/488 322/718/642 7487/722/445 +f 316/717/641 318/719/643 319/724/646 +f 319/724/646 323/723/645 4690/726/601 +f 318/719/643 4645/727/487 323/723/645 +f 323/723/645 4644/728/482 4691/725/608 +f 320/709/633 316/717/641 324/730/647 +f 4702/714/638 320/709/633 4701/731/648 +f 324/730/647 319/724/646 4689/732/600 +f 4701/731/648 324/730/647 7503/733/604 +f 325/742/652 329/734/649 326/736/651 +f 326/736/651 330/735/650 4618/738/423 +f 329/734/649 4696/739/628 330/735/650 +f 330/735/650 4695/740/623 4619/737/428 +f 327/744/654 325/742/652 331/743/653 +f 4708/751/660 327/744/654 4707/745/655 +f 331/743/653 326/736/651 4617/746/422 +f 4707/745/655 331/743/653 7476/347/317 +f 325/742/652 327/744/654 328/748/657 +f 328/748/657 332/747/656 4711/750/659 +f 327/744/654 4708/751/660 332/747/656 +f 332/747/656 4709/752/661 4710/749/658 +f 329/734/649 325/742/652 333/754/663 +f 4696/739/628 329/734/649 4697/755/629 +f 333/754/663 328/748/657 4712/756/664 +f 4697/755/629 333/754/663 7504/757/630 +f 334/766/673 338/758/665 335/760/667 +f 335/760/667 339/759/666 4717/762/669 +f 338/758/665 4714/763/670 339/759/666 +f 339/759/666 4715/764/671 4716/761/668 +f 336/768/675 334/766/673 340/767/674 +f 4627/774/448 336/768/675 4628/769/449 +f 340/767/674 335/760/667 4718/770/676 +f 4628/769/449 340/767/674 7477/771/340 +f 334/766/673 336/768/675 337/773/678 +f 337/773/678 341/772/677 4705/713/637 +f 336/768/675 4627/774/448 341/772/677 +f 341/772/677 4626/775/443 4706/721/644 +f 338/758/665 334/766/673 342/776/679 +f 4714/763/670 338/758/665 4713/777/680 +f 342/776/679 337/773/678 4704/712/636 +f 4713/777/680 342/776/679 7505/716/640 +f 343/781/684 347/778/681 344/780/683 +f 344/780/683 348/779/682 4570/345/315 +f 347/778/681 4708/751/660 348/779/682 +f 348/779/682 4707/745/655 4571/346/316 +f 345/785/688 343/781/684 349/782/685 +f 345/785/688 349/782/685 4720/784/687 +f 344/780/683 4570/345/315 349/782/685 +f 349/782/685 4569/338/311 4719/783/686 +f 343/781/684 345/785/688 346/787/690 +f 346/787/690 350/786/689 4723/789/692 +f 345/785/688 4720/784/687 350/786/689 +f 350/786/689 4721/790/693 4722/788/691 +f 347/778/681 343/781/684 351/792/695 +f 4708/751/660 347/778/681 4709/752/661 +f 351/792/695 346/787/690 4724/793/696 +f 4709/752/661 351/792/695 7506/753/662 +f 352/802/705 356/794/697 353/796/699 +f 353/796/699 357/795/698 4729/798/701 +f 356/794/697 4726/799/702 357/795/698 +f 357/795/698 4727/800/703 4728/797/700 +f 354/808/708 352/802/705 358/803/706 +f 354/808/708 358/803/706 4585/805/343 +f 353/796/699 4729/798/701 358/803/706 +f 358/803/706 4730/806/707 4586/804/344 +f 352/802/705 354/808/708 355/810/710 +f 355/810/710 359/809/709 4717/762/669 +f 354/808/708 4585/805/343 359/809/709 +f 359/809/709 4584/811/338 4718/770/676 +f 356/794/697 352/802/705 360/812/711 +f 4726/799/702 356/794/697 4725/813/712 +f 360/812/711 355/810/710 4716/761/668 +f 4725/813/712 360/812/711 7507/765/672 +f 362/817/716 361/814/713 366/816/715 +f 4558/826/284 362/817/716 4559/818/285 +f 366/816/715 365/815/714 4719/820/686 +f 4559/818/285 366/816/715 7474/821/286 +f 361/814/713 362/817/716 363/823/718 +f 363/823/718 367/822/717 4732/825/720 +f 362/817/716 4558/826/284 367/822/717 +f 367/822/717 4557/827/279 4731/824/719 +f 364/830/722 361/814/713 368/829/721 +f 4735/836/727 364/830/722 4734/831/723 +f 368/829/721 363/823/718 4733/832/724 +f 4734/831/723 368/829/721 7510/833/725 +f 361/814/713 364/830/722 365/815/714 +f 4720/819/687 365/815/714 4721/835/693 +f 364/830/722 4735/836/727 369/834/726 +f 369/834/726 4736/837/728 4721/835/693 +f 371/842/732 370/839/729 375/841/731 +f 4741/851/739 371/842/732 4740/843/733 +f 375/841/731 374/840/730 4739/845/735 +f 4740/843/733 375/841/731 7511/846/736 +f 370/839/729 371/842/732 372/848/738 +f 372/848/738 376/847/737 4567/850/300 +f 371/842/732 4741/851/739 376/847/737 +f 376/847/737 4742/852/740 4568/849/305 +f 373/855/742 370/839/729 377/854/741 +f 4729/861/701 373/855/742 4730/856/707 +f 377/854/741 372/848/738 4566/857/299 +f 4730/856/707 377/854/741 7475/858/302 +f 370/839/729 373/855/742 374/840/730 +f 374/840/730 378/859/743 4738/844/734 +f 378/859/743 373/855/742 4728/862/700 +f 378/859/743 4728/862/700 4737/860/744 +f 380/867/748 379/864/745 384/866/747 +f 4510/873/172 380/867/748 4511/868/173 +f 384/866/747 383/865/746 4731/824/719 +f 4511/868/173 384/866/747 7464/828/174 +f 379/864/745 380/867/748 381/870/750 +f 381/870/750 385/869/749 4744/872/752 +f 380/867/748 4510/873/172 385/869/749 +f 385/869/749 4509/874/167 4743/871/751 +f 382/877/754 379/864/745 386/876/753 +f 4747/882/759 382/877/754 4746/878/755 +f 386/876/753 381/870/750 4745/879/756 +f 4746/878/755 386/876/753 7514/880/757 +f 379/864/745 382/877/754 383/865/746 +f 383/865/746 387/881/758 4732/825/720 +f 382/877/754 4747/882/759 387/881/758 +f 387/881/758 4748/883/760 4733/832/724 +f 389/887/764 388/884/761 393/886/763 +f 4753/896/771 389/887/764 4752/888/765 +f 393/886/763 392/885/762 4751/890/767 +f 4752/888/765 393/886/763 7515/891/768 +f 388/884/761 389/887/764 390/893/770 +f 390/893/770 394/892/769 4525/895/194 +f 389/887/764 4753/896/771 394/892/769 +f 394/892/769 4754/897/772 4526/894/200 +f 391/900/774 388/884/761 395/899/773 +f 4741/851/739 391/900/774 4742/852/740 +f 395/899/773 390/893/770 4524/901/193 +f 4742/852/740 395/899/773 7465/853/197 +f 388/884/761 391/900/774 392/885/762 +f 392/885/762 396/902/775 4750/889/766 +f 391/900/774 4741/851/739 396/902/775 +f 396/902/775 4740/843/733 4749/903/776 +f 398/907/780 397/904/777 402/906/779 +f 4498/916/140 398/907/780 4499/908/141 +f 402/906/779 401/905/778 4743/910/751 +f 4499/908/141 402/906/779 7462/911/142 +f 397/904/777 398/907/780 399/913/782 +f 399/913/782 403/912/781 4756/915/784 +f 398/907/780 4498/916/140 403/912/781 +f 403/912/781 4497/917/135 4755/914/783 +f 400/920/786 397/904/777 404/919/785 +f 4759/926/791 400/920/786 4758/921/787 +f 404/919/785 399/913/782 4757/922/788 +f 4758/921/787 404/919/785 7516/923/789 +f 397/904/777 400/920/786 401/905/778 +f 401/905/778 405/924/790 4744/909/752 +f 400/920/786 4759/926/791 405/924/790 +f 405/924/790 4760/927/792 4745/925/756 +f 407/932/796 406/929/793 411/931/795 +f 4765/941/803 407/932/796 4764/933/797 +f 411/931/795 410/930/794 4763/935/799 +f 4764/933/797 411/931/795 7517/936/800 +f 406/929/793 407/932/796 408/938/802 +f 408/938/802 412/937/801 4507/940/155 +f 407/932/796 4765/941/803 412/937/801 +f 412/937/801 4766/942/804 4508/939/161 +f 409/945/806 406/929/793 413/944/805 +f 4753/951/771 409/945/806 4754/946/772 +f 413/944/805 408/938/802 4506/947/154 +f 4754/946/772 413/944/805 7463/948/158 +f 406/929/793 409/945/806 410/930/794 +f 410/930/794 414/949/807 4762/934/798 +f 409/945/806 4753/951/771 414/949/807 +f 414/949/807 4752/952/765 4761/950/808 +f 416/957/812 415/954/809 420/956/811 +f 4441/963/13 416/957/812 4442/958/14 +f 420/956/811 419/955/810 4755/914/783 +f 4442/958/14 420/956/811 7452/918/15 +f 415/954/809 416/957/812 417/960/814 +f 417/960/814 421/959/813 4678/962/580 +f 416/957/812 4441/963/13 421/959/813 +f 421/959/813 4440/964/5 4679/961/581 +f 418/967/816 415/954/809 422/966/815 +f 4768/972/819 418/967/816 4767/968/817 +f 422/966/815 417/960/814 4677/969/594 +f 4767/968/817 422/966/815 7500/970/596 +f 415/954/809 418/967/816 419/955/810 +f 419/955/810 423/971/818 4756/915/784 +f 418/967/816 4768/972/819 423/971/818 +f 423/971/818 4769/973/820 4757/922/788 +f 425/977/824 424/974/821 429/976/823 +f 4693/986/612 425/977/824 4694/978/615 +f 429/976/823 428/975/822 4772/980/826 +f 4694/978/615 429/976/823 7501/981/616 +f 424/974/821 425/977/824 426/983/828 +f 426/983/828 430/982/827 4456/985/37 +f 425/977/824 4693/986/612 430/982/827 +f 430/982/827 4692/987/611 4457/984/44 +f 427/990/830 424/974/821 431/989/829 +f 4765/941/803 427/990/830 4766/942/804 +f 431/989/829 426/983/828 4455/991/36 +f 4766/942/804 431/989/829 7453/943/40 +f 424/974/821 427/990/830 428/975/822 +f 428/975/822 432/992/831 4771/979/825 +f 427/990/830 4765/941/803 432/992/831 +f 432/992/831 4764/933/797 4770/993/832 +f 433/1002/841 436/994/833 434/996/835 +f 434/996/835 437/995/834 4777/998/837 +f 436/994/833 4774/999/838 437/995/834 +f 437/995/834 4775/1000/839 4776/997/836 +f 435/1004/843 433/1002/841 438/1003/842 +f 4768/1010/819 435/1004/843 4769/1005/820 +f 438/1003/842 434/996/835 4778/1006/844 +f 4769/1005/820 438/1003/842 7516/1007/789 +f 436/994/833 433/1002/841 439/1008/845 +f 4774/999/838 436/994/833 4773/1009/846 +f 439/1008/845 435/1004/843 4767/1011/817 +f 4773/1009/846 439/1008/845 7500/1012/596 +f 441/1016/850 440/1013/847 444/1015/849 +f 4771/1024/825 441/1016/850 4772/1017/826 +f 444/1015/849 443/1014/848 4781/1019/852 +f 4772/1017/826 444/1015/849 7501/1020/616 +f 442/1022/854 440/1013/847 445/1021/853 +f 4783/1029/858 442/1022/854 4782/1023/855 +f 445/1021/853 441/1016/850 4770/1025/832 +f 4782/1023/855 445/1021/853 7517/1026/800 +f 440/1013/847 442/1022/854 443/1014/848 +f 443/1014/848 446/1027/856 4780/1018/851 +f 442/1022/854 4783/1029/858 446/1027/856 +f 446/1027/856 4784/1030/859 4779/1028/857 +f 448/1035/864 447/1032/861 451/1034/863 +f 4759/1041/791 448/1035/864 4760/1036/792 +f 451/1034/863 450/1033/862 4787/1038/866 +f 4760/1036/792 451/1034/863 7514/1039/757 +f 449/1043/868 447/1032/861 452/1040/867 +f 449/1043/868 452/1040/867 4777/998/837 +f 448/1035/864 4759/1041/791 452/1040/867 +f 452/1040/867 4758/1042/787 4778/1006/844 +f 447/1032/861 449/1043/868 450/1033/862 +f 450/1033/862 453/1044/869 4786/1037/865 +f 449/1043/868 4777/998/837 453/1044/869 +f 453/1044/869 4776/997/836 4785/1045/870 +f 454/1051/876 457/1046/871 455/1048/873 +f 455/1048/873 458/1047/872 4783/1029/858 +f 457/1046/871 4789/1049/874 458/1047/872 +f 458/1047/872 4790/1050/875 4784/1030/859 +f 456/1055/878 454/1051/876 459/1052/877 +f 456/1055/878 459/1052/877 4762/1054/798 +f 455/1048/873 4783/1029/858 459/1052/877 +f 459/1052/877 4782/1023/855 4763/1053/799 +f 457/1046/871 454/1051/876 460/1056/879 +f 4789/1049/874 457/1046/871 4788/1057/880 +f 460/1056/879 456/1055/878 4761/1058/808 +f 4788/1057/880 460/1056/879 7515/1059/768 +f 461/1065/886 464/1060/881 462/1062/883 +f 462/1062/883 465/1061/882 4792/1064/885 +f 464/1060/881 4786/1037/865 465/1061/882 +f 465/1061/882 4785/1045/870 4791/1063/884 +f 463/1067/888 461/1065/886 466/1066/887 +f 4747/1072/759 463/1067/888 4748/1068/760 +f 466/1066/887 462/1062/883 4793/1069/889 +f 4748/1068/760 466/1066/887 7510/1070/725 +f 464/1060/881 461/1065/886 467/1071/890 +f 4786/1037/865 464/1060/881 4787/1038/866 +f 463/1067/888 4747/1072/759 467/1071/890 +f 467/1071/890 4746/1073/755 4787/1038/866 +f 469/1082/894 468/1074/891 472/1076/893 +f 469/1082/894 472/1076/893 4750/1078/766 +f 472/1076/893 471/1075/892 4788/1080/880 +f 472/1076/893 4788/1080/880 4751/1077/767 +f 470/1084/896 468/1074/891 473/1083/895 +f 4795/1090/899 470/1084/896 4794/1085/897 +f 473/1083/895 469/1082/894 4749/1086/776 +f 4794/1085/897 473/1083/895 7511/1087/736 +f 468/1074/891 470/1084/896 471/1075/892 +f 471/1075/892 474/1088/898 4789/1079/874 +f 470/1084/896 4795/1090/899 474/1088/898 +f 474/1088/898 4796/1091/900 4790/1089/875 +f 475/1098/906 478/1093/901 476/1095/903 +f 476/1095/903 479/1094/902 4798/1097/905 +f 478/1093/901 4792/1064/885 479/1094/902 +f 479/1094/902 4791/1063/884 4797/1096/904 +f 477/1100/908 475/1098/906 480/1099/907 +f 4735/1105/727 477/1100/908 4736/1101/728 +f 480/1099/907 476/1095/903 4799/1102/909 +f 4736/1101/728 480/1099/907 7508/1103/694 +f 478/1093/901 475/1098/906 481/1104/910 +f 4792/1064/885 478/1093/901 4793/1069/889 +f 481/1104/910 477/1100/908 4734/1106/723 +f 4793/1069/889 481/1104/910 7510/1070/725 +f 483/1110/914 482/1107/911 486/1109/913 +f 4738/1115/734 483/1110/914 4739/1111/735 +f 486/1109/913 485/1108/912 4794/1085/897 +f 4739/1111/735 486/1109/913 7511/1087/736 +f 484/1113/916 482/1107/911 487/1112/915 +f 4801/1119/919 484/1113/916 4800/1114/917 +f 487/1112/915 483/1110/914 4737/1116/744 +f 4800/1114/917 487/1112/915 7509/1117/704 +f 482/1107/911 484/1113/916 485/1108/912 +f 485/1108/912 488/1118/918 4795/1090/899 +f 484/1113/916 4801/1119/919 488/1118/918 +f 488/1118/918 4802/1120/920 4796/1091/900 +f 489/1126/926 492/1121/921 490/1123/923 +f 490/1123/923 493/1122/922 4804/1125/925 +f 492/1121/921 4798/1097/905 493/1122/922 +f 493/1122/922 4797/1096/904 4803/1124/924 +f 491/1132/929 489/1126/926 494/1127/927 +f 491/1132/929 494/1127/927 4723/1129/692 +f 494/1127/927 490/1123/923 4805/1130/928 +f 494/1127/927 4805/1130/928 4724/1128/696 +f 492/1121/921 489/1126/926 495/1133/930 +f 4798/1097/905 492/1121/921 4799/1102/909 +f 495/1133/930 491/1132/929 4722/1134/691 +f 4799/1102/909 495/1133/930 7508/1103/694 +f 497/1138/934 496/1135/931 500/1137/933 +f 4726/1143/702 497/1138/934 4727/1139/703 +f 500/1137/933 499/1136/932 4800/1114/917 +f 4727/1139/703 500/1137/933 7509/1117/704 +f 498/1141/936 496/1135/931 501/1140/935 +f 4807/1147/939 498/1141/936 4806/1142/937 +f 497/1138/934 4726/1143/702 501/1140/935 +f 501/1140/935 4725/1144/712 4806/1142/937 +f 496/1135/931 498/1141/936 499/1136/932 +f 499/1136/932 502/1146/938 4801/1119/919 +f 498/1141/936 4807/1147/939 502/1146/938 +f 502/1146/938 4808/1148/940 4802/1120/920 +f 503/1154/946 506/1149/941 504/1151/943 +f 504/1151/943 507/1150/942 4810/1153/945 +f 506/1149/941 4804/1125/925 507/1150/942 +f 507/1150/942 4803/1124/924 4809/1152/944 +f 505/1160/949 503/1154/946 508/1155/947 +f 505/1160/949 508/1155/947 4711/1157/659 +f 508/1155/947 504/1151/943 4811/1158/948 +f 508/1155/947 4811/1158/948 4712/1156/664 +f 506/1149/941 503/1154/946 509/1161/950 +f 4804/1125/925 506/1149/941 4805/1130/928 +f 509/1161/950 505/1160/949 4710/1162/658 +f 4805/1130/928 509/1161/950 7506/1131/662 +f 511/1166/954 510/1163/951 514/1165/953 +f 4714/1171/670 511/1166/954 4715/1167/671 +f 514/1165/953 513/1164/952 4806/1142/937 +f 4715/1167/671 514/1165/953 7507/1145/672 +f 512/1169/956 510/1163/951 515/1168/955 +f 4813/1175/959 512/1169/956 4812/1170/957 +f 511/1166/954 4714/1171/670 515/1168/955 +f 515/1168/955 4713/1172/680 4812/1170/957 +f 510/1163/951 512/1169/956 513/1164/952 +f 513/1164/952 516/1174/958 4807/1147/939 +f 512/1169/956 4813/1175/959 516/1174/958 +f 516/1174/958 4814/1176/960 4808/1148/940 +f 517/1182/966 520/1177/961 518/1179/963 +f 518/1179/963 521/1178/962 4816/1181/965 +f 520/1177/961 4810/1153/945 521/1178/962 +f 521/1178/962 4809/1152/944 4815/1180/964 +f 519/1188/969 517/1182/966 522/1183/967 +f 519/1188/969 522/1183/967 4699/1185/627 +f 522/1183/967 518/1179/963 4817/1186/968 +f 522/1183/967 4817/1186/968 4700/1184/632 +f 520/1177/961 517/1182/966 523/1189/970 +f 4810/1153/945 520/1177/961 4811/1158/948 +f 523/1189/970 519/1188/969 4698/1190/626 +f 4811/1158/948 523/1189/970 7504/1159/630 +f 525/1194/974 524/1191/971 528/1193/973 +f 4702/1199/638 525/1194/974 4703/1195/639 +f 528/1193/973 527/1192/972 4812/1170/957 +f 4703/1195/639 528/1193/973 7505/1173/640 +f 526/1197/976 524/1191/971 529/1196/975 +f 4819/1203/979 526/1197/976 4818/1198/977 +f 525/1194/974 4702/1199/638 529/1196/975 +f 529/1196/975 4701/1200/648 4818/1198/977 +f 524/1191/971 526/1197/976 527/1192/972 +f 527/1192/972 530/1202/978 4813/1175/959 +f 526/1197/976 4819/1203/979 530/1202/978 +f 530/1202/978 4820/1204/980 4814/1176/960 +f 531/1208/984 534/1205/981 532/1207/983 +f 532/1207/983 535/1206/982 4774/999/838 +f 534/1205/981 4816/1181/965 535/1206/982 +f 535/1206/982 4815/1180/964 4775/1000/839 +f 533/1210/986 531/1208/984 536/1209/985 +f 4684/1213/589 533/1210/986 4685/1211/595 +f 536/1209/985 532/1207/983 4773/1009/846 +f 4685/1211/595 536/1209/985 7500/1012/596 +f 534/1205/981 531/1208/984 537/1212/987 +f 4816/1181/965 534/1205/981 4817/1186/968 +f 537/1212/987 533/1210/986 4683/1214/588 +f 4817/1186/968 537/1212/987 7502/1187/592 +f 539/1218/991 538/1215/988 542/1217/990 +f 4687/1225/602 539/1218/991 4688/1219/603 +f 542/1217/990 541/1216/989 4818/1221/977 +f 4688/1219/603 542/1217/990 7503/1222/604 +f 540/1224/993 538/1215/988 543/1223/992 +f 4780/1018/851 540/1224/993 4781/1019/852 +f 543/1223/992 539/1218/991 4686/1226/614 +f 4781/1019/852 543/1223/992 7501/1020/616 +f 538/1215/988 540/1224/993 541/1216/989 +f 541/1216/989 544/1227/994 4819/1220/979 +f 540/1224/993 4780/1018/851 544/1227/994 +f 544/1227/994 4779/1028/857 4820/1228/980 +f 545/1237/1003 549/1229/995 546/1231/997 +f 546/1231/997 550/1230/996 4825/1233/999 +f 549/1229/995 4822/1234/1000 550/1230/996 +f 550/1230/996 4823/1235/1001 4824/1232/998 +f 547/1239/1005 545/1237/1003 551/1238/1004 +f 4828/1247/1013 547/1239/1005 4827/1240/1006 +f 551/1238/1004 546/1231/997 4826/1241/1007 +f 4827/1240/1006 551/1238/1004 7625/1242/1008 +f 545/1237/1003 547/1239/1005 548/1244/1010 +f 548/1244/1010 552/1243/1009 4831/1246/1012 +f 547/1239/1005 4828/1247/1013 552/1243/1009 +f 552/1243/1009 4829/1248/1014 4830/1245/1011 +f 549/1229/995 545/1237/1003 553/1250/1016 +f 4822/1234/1000 549/1229/995 4821/1251/1017 +f 553/1250/1016 548/1244/1010 4832/1252/1018 +f 4821/1251/1017 553/1250/1016 7542/1253/1019 +f 554/1259/1025 558/1254/1020 555/1256/1022 +f 555/1256/1022 559/1255/1021 4834/1258/1024 +f 558/1254/1020 4831/1246/1012 559/1255/1021 +f 559/1255/1021 4830/1245/1011 4833/1257/1023 +f 556/1261/1027 554/1259/1025 560/1260/1026 +f 4837/1269/1035 556/1261/1027 4836/1262/1028 +f 560/1260/1026 555/1256/1022 4835/1263/1029 +f 4836/1262/1028 560/1260/1026 7626/1264/1030 +f 554/1259/1025 556/1261/1027 557/1266/1032 +f 557/1266/1032 561/1265/1031 4840/1268/1034 +f 556/1261/1027 4837/1269/1035 561/1265/1031 +f 561/1265/1031 4838/1270/1036 4839/1267/1033 +f 558/1254/1020 554/1259/1025 562/1272/1038 +f 4831/1246/1012 558/1254/1020 4832/1252/1018 +f 562/1272/1038 557/1266/1032 4841/1273/1039 +f 4832/1252/1018 562/1272/1038 7542/1253/1019 +f 563/1282/1048 567/1274/1040 564/1276/1042 +f 564/1276/1042 568/1275/1041 4846/1278/1044 +f 567/1274/1040 4843/1279/1045 568/1275/1041 +f 568/1275/1041 4844/1280/1046 4845/1277/1043 +f 565/1284/1050 563/1282/1048 569/1283/1049 +f 4849/1290/1056 565/1284/1050 4848/1285/1051 +f 569/1283/1049 564/1276/1042 4847/1286/1052 +f 4848/1285/1051 569/1283/1049 7623/1287/1053 +f 563/1282/1048 565/1284/1050 566/1289/1055 +f 566/1289/1055 570/1288/1054 4825/1233/999 +f 565/1284/1050 4849/1290/1056 570/1288/1054 +f 570/1288/1054 4850/1291/1057 4826/1241/1007 +f 567/1274/1040 563/1282/1048 571/1292/1058 +f 4843/1279/1045 567/1274/1040 4842/1293/1059 +f 571/1292/1058 566/1289/1055 4824/1232/998 +f 4842/1293/1059 571/1292/1058 7540/1236/1002 +f 572/1299/1065 576/1294/1060 573/1296/1062 +f 573/1296/1062 577/1295/1061 4852/1298/1064 +f 576/1294/1060 4837/1269/1035 577/1295/1061 +f 577/1295/1061 4836/1262/1028 4851/1297/1063 +f 574/1301/1067 572/1299/1065 578/1300/1066 +f 4855/1309/1075 574/1301/1067 4854/1302/1068 +f 578/1300/1066 573/1296/1062 4853/1303/1069 +f 4854/1302/1068 578/1300/1066 7624/1304/1070 +f 572/1299/1065 574/1301/1067 575/1306/1072 +f 575/1306/1072 579/1305/1071 4858/1308/1074 +f 574/1301/1067 4855/1309/1075 579/1305/1071 +f 579/1305/1071 4856/1310/1076 4857/1307/1073 +f 576/1294/1060 572/1299/1065 580/1312/1078 +f 4837/1269/1035 576/1294/1060 4838/1270/1036 +f 580/1312/1078 575/1306/1072 4859/1313/1079 +f 4838/1270/1036 580/1312/1078 7541/1271/1037 +f 582/1317/1083 581/1314/1080 586/1316/1082 +f 4864/1326/1092 582/1317/1083 4863/1318/1084 +f 585/1315/1081 4861/1319/1085 586/1316/1082 +f 586/1316/1082 4862/1320/1086 4863/1318/1084 +f 581/1314/1080 582/1317/1083 583/1323/1089 +f 583/1323/1089 587/1322/1088 4867/1325/1091 +f 582/1317/1083 4864/1326/1092 587/1322/1088 +f 587/1322/1088 4865/1327/1093 4866/1324/1090 +f 584/1334/1097 581/1314/1080 588/1329/1095 +f 584/1334/1097 588/1329/1095 4846/1331/1044 +f 588/1329/1095 583/1323/1089 4868/1332/1096 +f 588/1329/1095 4868/1332/1096 4847/1330/1052 +f 585/1315/1081 581/1314/1080 589/1335/1098 +f 4861/1319/1085 585/1315/1081 4860/1336/1099 +f 589/1335/1098 584/1334/1097 4845/1337/1043 +f 4860/1336/1099 589/1335/1098 7538/1338/1047 +f 591/1342/1103 590/1339/1100 595/1341/1102 +f 4870/1348/1109 591/1342/1103 4869/1343/1104 +f 594/1340/1101 4855/1309/1075 595/1341/1102 +f 595/1341/1102 4854/1302/1068 4869/1343/1104 +f 590/1339/1100 591/1342/1103 592/1345/1106 +f 592/1345/1106 596/1344/1105 4873/1347/1108 +f 591/1342/1103 4870/1348/1109 596/1344/1105 +f 596/1344/1105 4871/1349/1110 4872/1346/1107 +f 593/1356/1117 590/1339/1100 597/1351/1112 +f 593/1356/1117 597/1351/1112 4876/1353/1114 +f 597/1351/1112 592/1345/1106 4874/1354/1115 +f 597/1351/1112 4874/1354/1115 4875/1352/1113 +f 594/1340/1101 590/1339/1100 598/1357/1118 +f 4855/1309/1075 594/1340/1101 4856/1310/1076 +f 598/1357/1118 593/1356/1117 4877/1358/1119 +f 4856/1310/1076 598/1357/1118 7539/1311/1077 +f 600/1367/1128 599/1359/1120 604/1361/1122 +f 600/1367/1128 604/1361/1122 4882/1363/1124 +f 603/1360/1121 4879/1364/1125 604/1361/1122 +f 604/1361/1122 4880/1365/1126 4881/1362/1123 +f 599/1359/1120 600/1367/1128 601/1369/1130 +f 601/1369/1130 605/1368/1129 4885/1371/1132 +f 600/1367/1128 4882/1363/1124 605/1368/1129 +f 605/1368/1129 4883/1372/1133 4884/1370/1131 +f 602/1375/1136 599/1359/1120 606/1374/1135 +f 4864/1326/1092 602/1375/1136 4865/1327/1093 +f 606/1374/1135 601/1369/1130 4886/1376/1137 +f 4865/1327/1093 606/1374/1135 7621/1328/1094 +f 599/1359/1120 602/1375/1136 603/1360/1121 +f 4879/1364/1125 603/1360/1121 4878/1378/1139 +f 602/1375/1136 4864/1326/1092 607/1377/1138 +f 4878/1378/1139 607/1377/1138 7536/1321/1087 +f 609/1382/1143 608/1379/1140 613/1381/1142 +f 4888/1388/1149 609/1382/1143 4887/1383/1144 +f 613/1381/1142 612/1380/1141 4872/1346/1107 +f 4887/1383/1144 613/1381/1142 7622/1350/1111 +f 608/1379/1140 609/1382/1143 610/1385/1146 +f 610/1385/1146 614/1384/1145 4891/1387/1148 +f 609/1382/1143 4888/1388/1149 614/1384/1145 +f 614/1384/1145 4889/1389/1150 4890/1386/1147 +f 611/1396/1157 608/1379/1140 615/1391/1152 +f 611/1396/1157 615/1391/1152 4894/1393/1154 +f 610/1385/1146 4891/1387/1148 615/1391/1152 +f 615/1391/1152 4892/1394/1155 4893/1392/1153 +f 608/1379/1140 611/1396/1157 612/1380/1141 +f 612/1380/1141 616/1397/1158 4873/1347/1108 +f 616/1397/1158 611/1396/1157 4895/1398/1159 +f 4874/1354/1115 616/1397/1158 7537/1355/1116 +f 617/1413/1174 621/1399/1160 618/1401/1162 +f 618/1401/1162 622/1400/1161 4900/1403/1164 +f 621/1399/1160 4897/1404/1165 622/1400/1161 +f 622/1400/1161 4898/1405/1166 4899/1402/1163 +f 617/1413/1174 618/1401/1162 619/1408/1169 +f 619/1408/1169 623/1407/1168 4903/1410/1171 +f 618/1401/1162 4900/1403/1164 623/1407/1168 +f 623/1407/1168 4901/1411/1172 4902/1409/1170 +f 620/1415/1176 617/1413/1174 624/1414/1175 +f 4882/1363/1124 620/1415/1176 4883/1372/1133 +f 624/1414/1175 619/1408/1169 4904/1416/1177 +f 4883/1372/1133 624/1414/1175 7619/1373/1134 +f 621/1399/1160 617/1413/1174 625/1417/1178 +f 4897/1404/1165 621/1399/1160 4896/1418/1179 +f 625/1417/1178 620/1415/1176 4881/1362/1123 +f 4896/1418/1179 625/1417/1178 7534/1366/1127 +f 627/1422/1183 626/1419/1180 631/1421/1182 +f 4906/1431/1189 627/1422/1183 4905/1423/1184 +f 631/1421/1182 630/1420/1181 4890/1425/1147 +f 4905/1423/1184 631/1421/1182 7620/1426/1151 +f 626/1419/1180 627/1422/1183 628/1428/1186 +f 628/1428/1186 632/1427/1185 4909/1430/1188 +f 627/1422/1183 4906/1431/1189 632/1427/1185 +f 632/1427/1185 4907/1432/1190 4908/1429/1187 +f 626/1419/1180 628/1428/1186 629/1435/1193 +f 629/1435/1193 633/1434/1192 4912/1437/1195 +f 628/1428/1186 4909/1430/1188 633/1434/1192 +f 633/1434/1192 4910/1438/1196 4911/1436/1194 +f 630/1420/1181 626/1419/1180 634/1440/1198 +f 4891/1424/1148 630/1420/1181 4892/1441/1155 +f 634/1440/1198 629/1435/1193 4913/1442/1199 +f 4892/1441/1155 634/1440/1198 7535/1443/1156 +f 636/1447/1203 635/1444/1200 640/1446/1202 +f 4918/1456/1212 636/1447/1203 4917/1448/1204 +f 640/1446/1202 639/1445/1201 4916/1450/1206 +f 4917/1448/1204 640/1446/1202 7530/1451/1207 +f 635/1444/1200 636/1447/1203 637/1453/1209 +f 637/1453/1209 641/1452/1208 4921/1455/1211 +f 636/1447/1203 4918/1456/1212 641/1452/1208 +f 641/1452/1208 4919/1457/1213 4920/1454/1210 +f 638/1460/1216 635/1444/1200 642/1459/1215 +f 4924/1466/1222 638/1460/1216 4923/1461/1217 +f 642/1459/1215 637/1453/1209 4922/1462/1218 +f 4923/1461/1217 642/1459/1215 7597/1463/1219 +f 635/1444/1200 638/1460/1216 639/1445/1201 +f 639/1445/1201 643/1464/1220 4915/1449/1205 +f 638/1460/1216 4924/1466/1222 643/1464/1220 +f 643/1464/1220 4925/1467/1223 4914/1465/1221 +f 645/1472/1228 644/1469/1225 649/1471/1227 +f 4930/1481/1237 645/1472/1228 4929/1473/1229 +f 649/1471/1227 648/1470/1226 4928/1475/1231 +f 4929/1473/1229 649/1471/1227 7598/1476/1232 +f 644/1469/1225 645/1472/1228 646/1478/1234 +f 646/1478/1234 650/1477/1233 4933/1480/1236 +f 645/1472/1228 4930/1481/1237 650/1477/1233 +f 650/1477/1233 4931/1482/1238 4932/1479/1235 +f 647/1485/1241 644/1469/1225 651/1484/1240 +f 4936/1491/1247 647/1485/1241 4935/1486/1242 +f 651/1484/1240 646/1478/1234 4934/1487/1243 +f 4935/1486/1242 651/1484/1240 7531/1488/1244 +f 644/1469/1225 647/1485/1241 648/1470/1226 +f 648/1470/1226 652/1489/1245 4927/1474/1230 +f 647/1485/1241 4936/1491/1247 652/1489/1245 +f 652/1489/1245 4937/1492/1248 4926/1490/1246 +f 654/1497/1253 653/1494/1250 658/1496/1252 +f 4939/1506/1259 654/1497/1253 4938/1498/1254 +f 658/1496/1252 657/1495/1251 4920/1500/1210 +f 4938/1498/1254 658/1496/1252 7543/1501/1214 +f 653/1494/1250 654/1497/1253 655/1503/1256 +f 655/1503/1256 659/1502/1255 4942/1505/1258 +f 654/1497/1253 4939/1506/1259 659/1502/1255 +f 4941/1504/1257 659/1502/1255 7545/1508/1261 +f 656/1510/1263 653/1494/1250 660/1509/1262 +f 4945/1516/1268 656/1510/1263 4944/1511/1264 +f 660/1509/1262 655/1503/1256 4943/1512/1265 +f 4944/1511/1264 660/1509/1262 7599/1513/1266 +f 653/1494/1250 656/1510/1263 657/1495/1251 +f 657/1495/1251 661/1514/1267 4921/1499/1211 +f 656/1510/1263 4945/1516/1268 661/1514/1267 +f 661/1514/1267 4946/1517/1269 4922/1515/1218 +f 663/1522/1273 662/1519/1270 667/1521/1272 +f 4951/1531/1282 663/1522/1273 4950/1523/1274 +f 667/1521/1272 666/1520/1271 4949/1525/1276 +f 4950/1523/1274 667/1521/1272 7600/1526/1277 +f 662/1519/1270 663/1522/1273 664/1528/1279 +f 664/1528/1279 668/1527/1278 4954/1530/1281 +f 663/1522/1273 4951/1531/1282 668/1527/1278 +f 4953/1529/1280 668/1527/1278 7546/1533/1284 +f 665/1535/1286 662/1519/1270 669/1534/1285 +f 4930/1481/1237 665/1535/1286 4931/1482/1238 +f 669/1534/1285 664/1528/1279 4955/1536/1287 +f 4931/1482/1238 669/1534/1285 7544/1483/1239 +f 662/1519/1270 665/1535/1286 666/1520/1271 +f 666/1520/1271 670/1537/1288 4948/1524/1275 +f 665/1535/1286 4930/1481/1237 670/1537/1288 +f 670/1537/1288 4929/1473/1229 4947/1538/1289 +f 672/1542/1293 671/1539/1290 676/1541/1292 +f 4957/1550/1298 672/1542/1293 4956/1543/1294 +f 676/1541/1292 675/1540/1291 4941/1545/1257 +f 4956/1543/1294 676/1541/1292 7545/1546/1261 +f 671/1539/1290 672/1542/1293 673/1548/1296 +f 4960/1556/1304 673/1548/1296 4959/1549/1297 +f 672/1542/1293 4957/1550/1298 677/1547/1295 +f 4959/1549/1297 677/1547/1295 7547/1552/1300 +f 674/1554/1302 671/1539/1290 678/1553/1301 +f 4963/1561/1308 674/1554/1302 4962/1555/1303 +f 678/1553/1301 673/1548/1296 4961/1557/1305 +f 4962/1555/1303 678/1553/1301 7601/1558/1306 +f 671/1539/1290 674/1554/1302 675/1540/1291 +f 675/1540/1291 679/1559/1307 4942/1544/1258 +f 674/1554/1302 4963/1561/1308 679/1559/1307 +f 679/1559/1307 4964/1562/1309 4943/1560/1265 +f 681/1567/1313 680/1564/1310 685/1566/1312 +f 4969/1576/1322 681/1567/1313 4968/1568/1314 +f 685/1566/1312 684/1565/1311 4967/1570/1316 +f 4968/1568/1314 685/1566/1312 7602/1571/1317 +f 680/1564/1310 681/1567/1313 682/1573/1319 +f 682/1573/1319 686/1572/1318 4972/1575/1321 +f 686/1572/1318 681/1567/1313 4970/1577/1323 +f 4971/1574/1320 686/1572/1318 7548/1578/1324 +f 683/1580/1326 680/1564/1310 687/1579/1325 +f 4951/1531/1282 683/1580/1326 4952/1532/1283 +f 687/1579/1325 682/1573/1319 4973/1581/1327 +f 4952/1532/1283 687/1579/1325 7546/1533/1284 +f 680/1564/1310 683/1580/1326 684/1565/1311 +f 684/1565/1311 688/1582/1328 4966/1569/1315 +f 683/1580/1326 4951/1531/1282 688/1582/1328 +f 688/1582/1328 4950/1523/1274 4965/1583/1329 +f 689/1589/1335 693/1584/1330 690/1586/1332 +f 690/1586/1332 694/1585/1331 4975/1588/1334 +f 693/1584/1330 4960/1556/1304 694/1585/1331 +f 694/1585/1331 4959/1549/1297 4974/1587/1333 +f 691/1591/1337 689/1589/1335 695/1590/1336 +f 4978/1598/1344 691/1591/1337 4977/1592/1338 +f 695/1590/1336 690/1586/1332 4976/1593/1339 +f 4977/1592/1338 695/1590/1336 7549/1594/1340 +f 689/1589/1335 691/1591/1337 692/1596/1342 +f 4981/1602/1348 692/1596/1342 4980/1597/1343 +f 691/1591/1337 4978/1598/1344 696/1595/1341 +f 4980/1597/1343 696/1595/1341 7603/1600/1346 +f 693/1584/1330 689/1589/1335 697/1601/1347 +f 4960/1556/1304 693/1584/1330 4961/1557/1305 +f 692/1596/1342 4981/1602/1348 697/1601/1347 +f 697/1601/1347 4982/1603/1349 4961/1557/1305 +f 698/1612/1358 702/1604/1350 699/1606/1352 +f 699/1606/1352 703/1605/1351 4987/1608/1354 +f 703/1605/1351 702/1604/1350 4985/1610/1356 +f 4986/1607/1353 703/1605/1351 7604/1611/1357 +f 700/1614/1360 698/1612/1358 704/1613/1359 +f 4990/1620/1366 700/1614/1360 4989/1615/1361 +f 704/1613/1359 699/1606/1352 4988/1616/1362 +f 4989/1615/1361 704/1613/1359 7550/1617/1363 +f 698/1612/1358 700/1614/1360 701/1619/1365 +f 701/1619/1365 705/1618/1364 4969/1576/1322 +f 700/1614/1360 4990/1620/1366 705/1618/1364 +f 705/1618/1364 4991/1621/1367 4970/1577/1323 +f 702/1604/1350 698/1612/1358 706/1622/1368 +f 702/1604/1350 706/1622/1368 4984/1609/1355 +f 706/1622/1368 701/1619/1365 4968/1568/1314 +f 706/1622/1368 4968/1568/1314 4983/1623/1369 +f 707/1629/1375 711/1624/1370 708/1626/1372 +f 708/1626/1372 712/1625/1371 4993/1628/1374 +f 711/1624/1370 4978/1598/1344 712/1625/1371 +f 712/1625/1371 4977/1592/1338 4992/1627/1373 +f 709/1631/1377 707/1629/1375 713/1630/1376 +f 4996/1639/1385 709/1631/1377 4995/1632/1378 +f 713/1630/1376 708/1626/1372 4994/1633/1379 +f 4995/1632/1378 713/1630/1376 7551/1634/1380 +f 707/1629/1375 709/1631/1377 710/1636/1382 +f 710/1636/1382 714/1635/1381 4999/1638/1384 +f 709/1631/1377 4996/1639/1385 714/1635/1381 +f 714/1635/1381 4997/1640/1386 4998/1637/1383 +f 711/1624/1370 707/1629/1375 715/1642/1388 +f 4978/1598/1344 711/1624/1370 4979/1599/1345 +f 715/1642/1388 710/1636/1382 5000/1643/1389 +f 4979/1599/1345 715/1642/1388 7603/1600/1346 +f 716/1652/1398 720/1644/1390 717/1646/1392 +f 717/1646/1392 721/1645/1391 5005/1648/1394 +f 720/1644/1390 5002/1649/1395 721/1645/1391 +f 721/1645/1391 5003/1650/1396 5004/1647/1393 +f 718/1654/1400 716/1652/1398 722/1653/1399 +f 5008/1662/1406 718/1654/1400 5007/1655/1401 +f 722/1653/1399 717/1646/1392 5006/1656/1402 +f 5007/1655/1401 722/1653/1399 7552/1657/1403 +f 716/1652/1398 718/1654/1400 719/1659/1405 +f 719/1659/1405 723/1658/1404 4987/1661/1354 +f 718/1654/1400 5008/1662/1406 723/1658/1404 +f 723/1658/1404 5009/1663/1407 4988/1660/1362 +f 720/1644/1390 716/1652/1398 724/1665/1408 +f 5002/1649/1395 720/1644/1390 5001/1666/1409 +f 724/1665/1408 719/1659/1405 4986/1667/1353 +f 5001/1666/1409 724/1665/1408 7604/1668/1357 +f 725/1674/1415 729/1669/1410 726/1671/1412 +f 726/1671/1412 730/1670/1411 5011/1673/1414 +f 729/1669/1410 4996/1639/1385 730/1670/1411 +f 730/1670/1411 4995/1632/1378 5010/1672/1413 +f 727/1676/1417 725/1674/1415 731/1675/1416 +f 5014/1684/1425 727/1676/1417 5013/1677/1418 +f 731/1675/1416 726/1671/1412 5012/1678/1419 +f 5013/1677/1418 731/1675/1416 7553/1679/1420 +f 725/1674/1415 727/1676/1417 728/1681/1422 +f 728/1681/1422 732/1680/1421 5017/1683/1424 +f 727/1676/1417 5014/1684/1425 732/1680/1421 +f 732/1680/1421 5015/1685/1426 5016/1682/1423 +f 729/1669/1410 725/1674/1415 733/1687/1428 +f 4996/1639/1385 729/1669/1410 4997/1640/1386 +f 733/1687/1428 728/1681/1422 5018/1688/1429 +f 4997/1640/1386 733/1687/1428 7605/1641/1387 +f 734/1697/1438 738/1689/1430 735/1691/1432 +f 735/1691/1432 739/1690/1431 5023/1693/1434 +f 738/1689/1430 5020/1694/1435 739/1690/1431 +f 739/1690/1431 5021/1695/1436 5022/1692/1433 +f 736/1699/1440 734/1697/1438 740/1698/1439 +f 5026/1705/1446 736/1699/1440 5025/1700/1441 +f 740/1698/1439 735/1691/1432 5024/1701/1442 +f 5025/1700/1441 740/1698/1439 7554/1702/1443 +f 734/1697/1438 736/1699/1440 737/1704/1445 +f 737/1704/1445 741/1703/1444 5005/1648/1394 +f 736/1699/1440 5026/1705/1446 741/1703/1444 +f 741/1703/1444 5027/1706/1447 5006/1656/1402 +f 738/1689/1430 734/1697/1438 742/1707/1448 +f 5020/1694/1435 738/1689/1430 5019/1708/1449 +f 742/1707/1448 737/1704/1445 5004/1647/1393 +f 5019/1708/1449 742/1707/1448 7606/1651/1397 +f 743/1714/1455 747/1709/1450 744/1711/1452 +f 744/1711/1452 748/1710/1451 5029/1713/1454 +f 747/1709/1450 5014/1684/1425 748/1710/1451 +f 748/1710/1451 5013/1677/1418 5028/1712/1453 +f 745/1720/1461 743/1714/1455 749/1715/1456 +f 745/1720/1461 749/1715/1456 5032/1717/1458 +f 749/1715/1456 744/1711/1452 5030/1718/1459 +f 5031/1716/1457 749/1715/1456 7555/1719/1460 +f 746/1722/1463 743/1714/1455 750/1721/1462 +f 5035/1727/1468 746/1722/1463 5034/1723/1464 +f 750/1721/1462 745/1720/1461 5033/1724/1465 +f 5034/1723/1464 750/1721/1462 7609/1725/1466 +f 743/1714/1455 746/1722/1463 747/1709/1450 +f 5014/1684/1425 747/1709/1450 5015/1685/1426 +f 746/1722/1463 5035/1727/1468 751/1726/1467 +f 751/1726/1467 5036/1728/1469 5015/1685/1426 +f 753/1732/1473 752/1729/1470 757/1731/1472 +f 5041/1740/1481 753/1732/1473 5040/1733/1474 +f 757/1731/1472 756/1730/1471 5039/1735/1476 +f 5040/1733/1474 757/1731/1472 7610/1736/1477 +f 754/1738/1479 752/1729/1470 758/1737/1478 +f 5044/1745/1486 754/1738/1479 5043/1739/1480 +f 753/1732/1473 5041/1740/1481 758/1737/1478 +f 5043/1739/1480 758/1737/1478 7556/1742/1483 +f 752/1729/1470 754/1738/1479 755/1744/1485 +f 755/1744/1485 759/1743/1484 5023/1693/1434 +f 754/1738/1479 5044/1745/1486 759/1743/1484 +f 759/1743/1484 5045/1746/1487 5024/1701/1442 +f 752/1729/1470 755/1744/1485 756/1730/1471 +f 756/1730/1471 760/1747/1488 5038/1734/1475 +f 760/1747/1488 755/1744/1485 5022/1692/1433 +f 760/1747/1488 5022/1692/1433 5037/1748/1489 +f 761/1754/1495 765/1749/1490 762/1751/1492 +f 762/1751/1492 766/1750/1491 5047/1753/1494 +f 765/1749/1490 5032/1717/1458 766/1750/1491 +f 766/1750/1491 5031/1716/1457 5046/1752/1493 +f 763/1756/1497 761/1754/1495 767/1755/1496 +f 5050/1764/1505 763/1756/1497 5049/1757/1498 +f 767/1755/1496 762/1751/1492 5048/1758/1499 +f 5049/1757/1498 767/1755/1496 7557/1759/1500 +f 761/1754/1495 763/1756/1497 764/1761/1502 +f 764/1761/1502 768/1760/1501 5053/1763/1504 +f 763/1756/1497 5050/1764/1505 768/1760/1501 +f 768/1760/1501 5051/1765/1506 5052/1762/1503 +f 765/1749/1490 761/1754/1495 769/1767/1508 +f 765/1749/1490 769/1767/1508 5032/1717/1458 +f 764/1761/1502 5053/1763/1504 769/1767/1508 +f 769/1767/1508 5054/1768/1509 5033/1724/1465 +f 770/1777/1518 774/1769/1510 771/1771/1512 +f 771/1771/1512 775/1770/1511 5059/1773/1514 +f 774/1769/1510 5056/1774/1515 775/1770/1511 +f 775/1770/1511 5057/1775/1516 5058/1772/1513 +f 772/1779/1520 770/1777/1518 776/1778/1519 +f 5062/1787/1526 772/1779/1520 5061/1780/1521 +f 776/1778/1519 771/1771/1512 5060/1781/1522 +f 5061/1780/1521 776/1778/1519 7558/1782/1523 +f 770/1777/1518 772/1779/1520 773/1784/1525 +f 773/1784/1525 777/1783/1524 5041/1786/1481 +f 772/1779/1520 5062/1787/1526 777/1783/1524 +f 777/1783/1524 5063/1788/1527 5042/1785/1482 +f 774/1769/1510 770/1777/1518 778/1790/1528 +f 774/1769/1510 778/1790/1528 5056/1774/1515 +f 773/1784/1525 5041/1786/1481 778/1790/1528 +f 778/1790/1528 5040/1792/1474 5055/1791/1529 +f 779/1802/1535 783/1794/1530 780/1796/1532 +f 780/1796/1532 784/1795/1531 5065/1798/1534 +f 783/1794/1530 5050/1799/1505 784/1795/1531 +f 784/1795/1531 5049/1800/1498 5064/1797/1533 +f 781/1804/1537 779/1802/1535 785/1803/1536 +f 5068/1812/1545 781/1804/1537 5067/1805/1538 +f 785/1803/1536 780/1796/1532 5066/1806/1539 +f 5067/1805/1538 785/1803/1536 7559/1807/1540 +f 779/1802/1535 781/1804/1537 782/1809/1542 +f 782/1809/1542 786/1808/1541 5071/1811/1544 +f 781/1804/1537 5068/1812/1545 786/1808/1541 +f 786/1808/1541 5069/1813/1546 5070/1810/1543 +f 783/1794/1530 779/1802/1535 787/1815/1548 +f 5050/1799/1505 783/1794/1530 5051/1816/1506 +f 787/1815/1548 782/1809/1542 5072/1817/1549 +f 5051/1816/1506 787/1815/1548 7611/1818/1507 +f 788/1827/1558 792/1819/1550 789/1821/1552 +f 789/1821/1552 793/1820/1551 5077/1823/1554 +f 792/1819/1550 5074/1824/1555 793/1820/1551 +f 793/1820/1551 5075/1825/1556 5076/1822/1553 +f 790/1829/1560 788/1827/1558 794/1828/1559 +f 5080/1835/1566 790/1829/1560 5079/1830/1561 +f 794/1828/1559 789/1821/1552 5078/1831/1562 +f 5079/1830/1561 794/1828/1559 7560/1832/1563 +f 788/1827/1558 790/1829/1560 791/1834/1565 +f 791/1834/1565 795/1833/1564 5059/1773/1514 +f 790/1829/1560 5080/1835/1566 795/1833/1564 +f 795/1833/1564 5081/1836/1567 5060/1781/1522 +f 792/1819/1550 788/1827/1558 796/1837/1568 +f 5074/1824/1555 792/1819/1550 5073/1838/1569 +f 796/1837/1568 791/1834/1565 5058/1772/1513 +f 5073/1838/1569 796/1837/1568 7612/1776/1517 +f 797/1844/1575 801/1839/1570 798/1841/1572 +f 798/1841/1572 802/1840/1571 5083/1843/1574 +f 801/1839/1570 5068/1812/1545 802/1840/1571 +f 802/1840/1571 5067/1805/1538 5082/1842/1573 +f 799/1846/1577 797/1844/1575 803/1845/1576 +f 5086/1854/1585 799/1846/1577 5085/1847/1578 +f 803/1845/1576 798/1841/1572 5084/1848/1579 +f 5085/1847/1578 803/1845/1576 7518/1849/1580 +f 797/1844/1575 799/1846/1577 800/1851/1582 +f 800/1851/1582 804/1850/1581 5089/1853/1584 +f 799/1846/1577 5086/1854/1585 804/1850/1581 +f 804/1850/1581 5087/1855/1586 5088/1852/1583 +f 801/1839/1570 797/1844/1575 805/1857/1588 +f 5068/1812/1545 801/1839/1570 5069/1813/1546 +f 805/1857/1588 800/1851/1582 5090/1858/1589 +f 5069/1813/1546 805/1857/1588 7613/1814/1547 +f 806/1867/1595 810/1859/1590 807/1861/1592 +f 807/1861/1592 811/1860/1591 5086/1863/1585 +f 810/1859/1590 5092/1864/1593 811/1860/1591 +f 811/1860/1591 5093/1865/1594 5087/1862/1586 +f 808/1869/1597 806/1867/1595 812/1868/1596 +f 5095/1875/1601 808/1869/1597 5094/1870/1598 +f 812/1868/1596 807/1861/1592 5085/1871/1578 +f 5094/1870/1598 812/1868/1596 7518/1872/1580 +f 806/1867/1595 808/1869/1597 809/1874/1600 +f 809/1874/1600 813/1873/1599 5077/1823/1554 +f 808/1869/1597 5095/1875/1601 813/1873/1599 +f 813/1873/1599 5096/1876/1602 5078/1831/1562 +f 810/1859/1590 806/1867/1595 814/1877/1603 +f 5092/1864/1593 810/1859/1590 5091/1878/1604 +f 814/1877/1603 809/1874/1600 5076/1822/1553 +f 5091/1878/1604 814/1877/1603 7614/1826/1557 +f 815/1887/1613 819/1879/1605 816/1881/1607 +f 816/1881/1607 820/1880/1606 5101/1883/1609 +f 819/1879/1605 5098/1884/1610 820/1880/1606 +f 5100/1882/1608 820/1880/1606 7561/1886/1612 +f 817/1889/1615 815/1887/1613 821/1888/1614 +f 5104/1897/1621 817/1889/1615 5103/1890/1616 +f 821/1888/1614 816/1881/1607 5102/1891/1617 +f 5103/1890/1616 821/1888/1614 7579/1892/1618 +f 815/1887/1613 817/1889/1615 818/1894/1620 +f 818/1894/1620 822/1893/1619 5071/1896/1544 +f 817/1889/1615 5104/1897/1621 822/1893/1619 +f 822/1893/1619 5105/1898/1622 5072/1895/1549 +f 819/1879/1605 815/1887/1613 823/1899/1623 +f 5098/1884/1610 819/1879/1605 5097/1900/1624 +f 823/1899/1623 818/1894/1620 5070/1901/1543 +f 5097/1900/1624 823/1899/1623 7613/1902/1547 +f 824/1911/1630 828/1903/1625 825/1905/1627 +f 825/1905/1627 829/1904/1626 5107/1907/1629 +f 828/1903/1625 5074/1908/1555 829/1904/1626 +f 829/1904/1626 5073/1909/1569 5106/1906/1628 +f 826/1913/1632 824/1911/1630 830/1912/1631 +f 5110/1921/1640 826/1913/1632 5109/1914/1633 +f 830/1912/1631 825/1905/1627 5108/1915/1634 +f 5109/1914/1633 830/1912/1631 7580/1916/1635 +f 824/1911/1630 826/1913/1632 827/1918/1637 +f 827/1918/1637 831/1917/1636 5113/1920/1639 +f 826/1913/1632 5110/1921/1640 831/1917/1636 +f 5112/1919/1638 831/1917/1636 7562/1923/1642 +f 828/1903/1625 824/1911/1630 832/1924/1643 +f 5074/1908/1555 828/1903/1625 5075/1925/1556 +f 832/1924/1643 827/1918/1637 5114/1926/1644 +f 5075/1925/1556 832/1924/1643 7614/1927/1557 +f 834/1931/1648 833/1928/1645 838/1930/1647 +f 5116/1937/1654 834/1931/1648 5115/1932/1649 +f 837/1929/1646 5104/1897/1621 838/1930/1647 +f 838/1930/1647 5103/1890/1616 5115/1932/1649 +f 833/1928/1645 834/1931/1648 835/1934/1651 +f 835/1934/1651 839/1933/1650 5119/1936/1653 +f 834/1931/1648 5116/1937/1654 839/1933/1650 +f 839/1933/1650 5117/1938/1655 5118/1935/1652 +f 836/1941/1658 833/1928/1645 840/1940/1657 +f 5053/1763/1504 836/1941/1658 5054/1768/1509 +f 840/1940/1657 835/1934/1651 5120/1942/1659 +f 5054/1768/1509 840/1940/1657 7609/1725/1466 +f 837/1929/1646 833/1928/1645 841/1943/1660 +f 5104/1897/1621 837/1929/1646 5105/1898/1622 +f 841/1943/1660 836/1941/1658 5052/1762/1503 +f 5105/1898/1622 841/1943/1660 7611/1766/1507 +f 843/1947/1664 842/1944/1661 847/1946/1663 +f 5122/1956/1670 843/1947/1664 5121/1948/1665 +f 847/1946/1663 846/1945/1662 5055/1950/1529 +f 5121/1948/1665 847/1946/1663 7610/1951/1477 +f 842/1944/1661 843/1947/1664 844/1953/1667 +f 844/1953/1667 848/1952/1666 5125/1955/1669 +f 843/1947/1664 5122/1956/1670 848/1952/1666 +f 848/1952/1666 5123/1957/1671 5124/1954/1668 +f 845/1961/1675 842/1944/1661 849/1959/1673 +f 845/1961/1675 849/1959/1673 5107/1907/1629 +f 849/1959/1673 844/1953/1667 5126/1960/1674 +f 849/1959/1673 5126/1960/1674 5108/1915/1634 +f 846/1945/1662 842/1944/1661 850/1962/1676 +f 5056/1949/1515 846/1945/1662 5057/1963/1516 +f 850/1962/1676 845/1961/1675 5106/1906/1628 +f 5057/1963/1516 850/1962/1676 7612/1910/1517 +f 851/1972/1685 855/1964/1677 852/1966/1679 +f 852/1966/1679 856/1965/1678 5131/1968/1681 +f 855/1964/1677 5128/1969/1682 856/1965/1678 +f 856/1965/1678 5129/1970/1683 5130/1967/1680 +f 853/1974/1687 851/1972/1685 857/1973/1686 +f 5035/1982/1468 853/1974/1687 5036/1975/1469 +f 857/1973/1686 852/1966/1679 5132/1976/1688 +f 5036/1975/1469 857/1973/1686 7607/1977/1427 +f 851/1972/1685 853/1974/1687 854/1979/1690 +f 854/1979/1690 858/1978/1689 5119/1981/1653 +f 853/1974/1687 5035/1982/1468 858/1978/1689 +f 858/1978/1689 5034/1983/1464 5120/1980/1659 +f 855/1964/1677 851/1972/1685 859/1985/1691 +f 5128/1969/1682 855/1964/1677 5127/1986/1692 +f 859/1985/1691 854/1979/1690 5118/1987/1652 +f 5127/1986/1692 859/1985/1691 7630/1988/1656 +f 860/1994/1696 864/1989/1693 861/1991/1695 +f 861/1991/1695 865/1990/1694 5038/1993/1475 +f 864/1989/1693 5122/1956/1670 865/1990/1694 +f 865/1990/1694 5121/1948/1665 5039/1992/1476 +f 862/1996/1698 860/1994/1696 866/1995/1697 +f 5134/2004/1704 862/1996/1698 5133/1997/1699 +f 866/1995/1697 861/1991/1695 5037/1998/1489 +f 5133/1997/1699 866/1995/1697 7608/1999/1437 +f 860/1994/1696 862/1996/1698 863/2001/1701 +f 863/2001/1701 867/2000/1700 5137/2003/1703 +f 862/1996/1698 5134/2004/1704 867/2000/1700 +f 867/2000/1700 5135/2005/1705 5136/2002/1702 +f 864/1989/1693 860/1994/1696 868/2007/1707 +f 5122/1956/1670 864/1989/1693 5123/1957/1671 +f 868/2007/1707 863/2001/1701 5138/2008/1708 +f 5123/1957/1671 868/2007/1707 7631/1958/1672 +f 870/2012/1712 869/2009/1709 874/2011/1711 +f 5143/2021/1719 870/2012/1712 5142/2013/1713 +f 874/2011/1711 873/2010/1710 5141/2015/1715 +f 5142/2013/1713 874/2011/1711 7575/2016/1716 +f 869/2009/1709 870/2012/1712 871/2018/1718 +f 871/2018/1718 875/2017/1717 5017/2020/1424 +f 870/2012/1712 5143/2021/1719 875/2017/1717 +f 875/2017/1717 5144/2022/1720 5018/2019/1429 +f 869/2009/1709 871/2018/1718 872/2025/1722 +f 872/2025/1722 876/2024/1721 5131/1968/1681 +f 871/2018/1718 5017/2020/1424 876/2024/1721 +f 876/2024/1721 5016/2026/1423 5132/1976/1688 +f 873/2010/1710 869/2009/1709 877/2027/1723 +f 5140/2014/1714 873/2010/1710 5139/2028/1724 +f 877/2027/1723 872/2025/1722 5130/1967/1680 +f 5139/2028/1724 877/2027/1723 7577/1971/1684 +f 878/2040/1732 882/2029/1725 879/2031/1727 +f 879/2031/1727 883/2030/1726 5020/2033/1435 +f 882/2029/1725 5134/2004/1704 883/2030/1726 +f 883/2030/1726 5133/1997/1699 5021/2032/1436 +f 878/2040/1732 879/2031/1727 880/2035/1729 +f 880/2035/1729 884/2034/1728 5146/2037/1731 +f 879/2031/1727 5020/2033/1435 884/2034/1728 +f 884/2034/1728 5019/2038/1449 5145/2036/1730 +f 881/2042/1734 878/2040/1732 885/2041/1733 +f 5149/2047/1739 881/2042/1734 5148/2043/1735 +f 885/2041/1733 880/2035/1729 5147/2044/1736 +f 5148/2043/1735 885/2041/1733 7576/2045/1737 +f 882/2029/1725 878/2040/1732 886/2046/1738 +f 5134/2004/1704 882/2029/1725 5135/2005/1705 +f 886/2046/1738 881/2042/1734 5150/2048/1740 +f 5135/2005/1705 886/2046/1738 7578/2006/1706 +f 887/2057/1749 891/2049/1741 888/2051/1743 +f 888/2051/1743 892/2050/1742 5155/2053/1745 +f 891/2049/1741 5152/2054/1746 892/2050/1742 +f 892/2050/1742 5153/2055/1747 5154/2052/1744 +f 889/2063/1752 887/2057/1749 893/2058/1750 +f 889/2063/1752 893/2058/1750 4999/2060/1384 +f 893/2058/1750 888/2051/1743 5156/2061/1751 +f 5000/2059/1389 893/2058/1750 7603/2062/1346 +f 890/2065/1754 887/2057/1749 894/2064/1753 +f 5143/2021/1719 890/2065/1754 5144/2022/1720 +f 894/2064/1753 889/2063/1752 4998/2066/1383 +f 5144/2022/1720 894/2064/1753 7605/2023/1387 +f 887/2057/1749 890/2065/1754 891/2049/1741 +f 891/2049/1741 895/2067/1755 5152/2054/1746 +f 890/2065/1754 5143/2021/1719 895/2067/1755 +f 895/2067/1755 5142/2013/1713 5151/2068/1756 +f 897/2072/1760 896/2069/1757 901/2071/1759 +f 5002/2077/1395 897/2072/1760 5003/2073/1396 +f 901/2071/1759 900/2070/1758 5145/2036/1730 +f 5003/2073/1396 901/2071/1759 7606/2039/1397 +f 898/2075/1762 896/2069/1757 902/2074/1761 +f 5158/2083/1768 898/2075/1762 5157/2076/1763 +f 897/2072/1760 5002/2077/1395 902/2074/1761 +f 5157/2076/1763 902/2074/1761 7604/1611/1357 +f 896/2069/1757 898/2075/1762 899/2080/1765 +f 899/2080/1765 903/2079/1764 5161/2082/1767 +f 898/2075/1762 5158/2083/1768 903/2079/1764 +f 903/2079/1764 5159/2084/1769 5160/2081/1766 +f 896/2069/1757 899/2080/1765 900/2070/1758 +f 900/2070/1758 904/2086/1771 5146/2037/1731 +f 899/2080/1765 5161/2082/1767 904/2086/1771 +f 904/2086/1771 5162/2087/1772 5147/2044/1736 +f 905/2096/1781 909/2088/1773 906/2090/1775 +f 906/2090/1775 910/2089/1774 5167/2092/1777 +f 909/2088/1773 5164/2093/1778 910/2089/1774 +f 910/2089/1774 5165/2094/1779 5166/2091/1776 +f 907/2098/1783 905/2096/1781 911/2097/1782 +f 4981/1602/1348 907/2098/1783 4982/1603/1349 +f 911/2097/1782 906/2090/1775 5168/2099/1784 +f 4982/1603/1349 911/2097/1782 7601/1558/1306 +f 905/2096/1781 907/2098/1783 908/2101/1786 +f 908/2101/1786 912/2100/1785 5155/2103/1745 +f 907/2098/1783 4981/1602/1348 912/2100/1785 +f 912/2100/1785 4980/1597/1343 5156/2102/1751 +f 909/2088/1773 905/2096/1781 913/2104/1787 +f 5164/2093/1778 909/2088/1773 5163/2105/1788 +f 913/2104/1787 908/2101/1786 5154/2106/1744 +f 5163/2105/1788 913/2104/1787 7573/2107/1748 +f 914/2111/1792 918/2108/1789 915/2110/1791 +f 915/2110/1791 919/2109/1790 4984/1609/1355 +f 918/2108/1789 5158/2083/1768 919/2109/1790 +f 919/2109/1790 5157/2076/1763 4985/1610/1356 +f 916/2113/1794 914/2111/1792 920/2112/1793 +f 5170/2119/1800 916/2113/1794 5169/2114/1795 +f 920/2112/1793 915/2110/1791 4983/1623/1369 +f 5169/2114/1795 920/2112/1793 7602/1571/1317 +f 914/2111/1792 916/2113/1794 917/2116/1797 +f 917/2116/1797 921/2115/1796 5173/2118/1799 +f 916/2113/1794 5170/2119/1800 921/2115/1796 +f 921/2115/1796 5171/2120/1801 5172/2117/1798 +f 918/2108/1789 914/2111/1792 922/2122/1803 +f 5158/2083/1768 918/2108/1789 5159/2084/1769 +f 922/2122/1803 917/2116/1797 5174/2123/1804 +f 5159/2084/1769 922/2122/1803 7574/2085/1770 +f 923/2132/1813 927/2124/1805 924/2126/1807 +f 924/2126/1807 928/2125/1806 5179/2128/1809 +f 927/2124/1805 5176/2129/1810 928/2125/1806 +f 928/2125/1806 5177/2130/1811 5178/2127/1808 +f 925/2134/1815 923/2132/1813 929/2133/1814 +f 4963/1561/1308 925/2134/1815 4964/1562/1309 +f 929/2133/1814 924/2126/1807 5180/2135/1816 +f 4964/1562/1309 929/2133/1814 7599/1563/1266 +f 923/2132/1813 925/2134/1815 926/2137/1818 +f 926/2137/1818 930/2136/1817 5167/2092/1777 +f 925/2134/1815 4963/1561/1308 930/2136/1817 +f 930/2136/1817 4962/1555/1303 5168/2099/1784 +f 927/2124/1805 923/2132/1813 931/2138/1819 +f 5176/2129/1810 927/2124/1805 5175/2139/1820 +f 931/2138/1819 926/2137/1818 5166/2091/1776 +f 5175/2139/1820 931/2138/1819 7571/2095/1780 +f 932/2143/1824 936/2140/1821 933/2142/1823 +f 933/2142/1823 937/2141/1822 4966/1569/1315 +f 936/2140/1821 5170/2119/1800 937/2141/1822 +f 937/2141/1822 5169/2114/1795 4967/1570/1316 +f 934/2145/1826 932/2143/1824 938/2144/1825 +f 5182/2151/1832 934/2145/1826 5181/2146/1827 +f 938/2144/1825 933/2142/1823 4965/1583/1329 +f 5181/2146/1827 938/2144/1825 7600/1526/1277 +f 932/2143/1824 934/2145/1826 935/2148/1829 +f 935/2148/1829 939/2147/1828 5185/2150/1831 +f 934/2145/1826 5182/2151/1832 939/2147/1828 +f 939/2147/1828 5183/2152/1833 5184/2149/1830 +f 936/2140/1821 932/2143/1824 940/2154/1835 +f 5170/2119/1800 936/2140/1821 5171/2120/1801 +f 940/2154/1835 935/2148/1829 5186/2155/1836 +f 5171/2120/1801 940/2154/1835 7572/2121/1802 +f 941/2164/1845 945/2156/1837 942/2158/1839 +f 942/2158/1839 946/2157/1838 5191/2160/1841 +f 945/2156/1837 5188/2161/1842 946/2157/1838 +f 946/2157/1838 5189/2162/1843 5190/2159/1840 +f 943/2166/1847 941/2164/1845 947/2165/1846 +f 4945/2172/1268 943/2166/1847 4946/2167/1269 +f 947/2165/1846 942/2158/1839 5192/2168/1848 +f 4946/2167/1269 947/2165/1846 7597/2169/1219 +f 941/2164/1845 943/2166/1847 944/2171/1850 +f 944/2171/1850 948/2170/1849 5179/2128/1809 +f 943/2166/1847 4945/2172/1268 948/2170/1849 +f 948/2170/1849 4944/2173/1264 5180/2135/1816 +f 945/2156/1837 941/2164/1845 949/2174/1851 +f 5188/2161/1842 945/2156/1837 5187/2175/1852 +f 949/2174/1851 944/2171/1850 5178/2127/1808 +f 5187/2175/1852 949/2174/1851 7569/2131/1812 +f 950/2179/1856 954/2176/1853 951/2178/1855 +f 951/2178/1855 955/2177/1854 4948/1524/1275 +f 954/2176/1853 5182/2151/1832 955/2177/1854 +f 955/2177/1854 5181/2146/1827 4949/1525/1276 +f 952/2181/1858 950/2179/1856 956/2180/1857 +f 5194/2187/1864 952/2181/1858 5193/2182/1859 +f 956/2180/1857 951/2178/1855 4947/1538/1289 +f 5193/2182/1859 956/2180/1857 7598/1476/1232 +f 950/2179/1856 952/2181/1858 953/2184/1861 +f 953/2184/1861 957/2183/1860 5197/2186/1863 +f 952/2181/1858 5194/2187/1864 957/2183/1860 +f 957/2183/1860 5195/2188/1865 5196/2185/1862 +f 954/2176/1853 950/2179/1856 958/2190/1867 +f 5182/2151/1832 954/2176/1853 5183/2152/1833 +f 958/2190/1867 953/2184/1861 5198/2191/1868 +f 5183/2152/1833 958/2190/1867 7570/2153/1834 +f 960/2195/1872 959/2192/1869 964/2194/1871 +f 5203/2203/1879 960/2195/1872 5202/2196/1873 +f 964/2194/1871 963/2193/1870 5201/2198/1875 +f 5202/2196/1873 964/2194/1871 7565/2199/1876 +f 959/2192/1869 960/2195/1872 961/2201/1878 +f 4924/2208/1222 961/2201/1878 4925/2202/1223 +f 960/2195/1872 5203/2203/1879 965/2200/1877 +f 965/2200/1877 5204/2204/1880 4925/2202/1223 +f 959/2192/1869 961/2201/1878 962/2207/1882 +f 962/2207/1882 966/2206/1881 5191/2160/1841 +f 961/2201/1878 4924/2208/1222 966/2206/1881 +f 966/2206/1881 4923/2209/1217 5192/2168/1848 +f 963/2193/1870 959/2192/1869 967/2210/1883 +f 5200/2197/1874 963/2193/1870 5199/2211/1884 +f 967/2210/1883 962/2207/1882 5190/2159/1840 +f 5199/2211/1884 967/2210/1883 7567/2163/1844 +f 968/2226/1892 972/2212/1885 969/2214/1887 +f 969/2214/1887 973/2213/1886 4927/2216/1230 +f 972/2212/1885 5194/2217/1864 973/2213/1886 +f 973/2213/1886 5193/2218/1859 4928/2215/1231 +f 968/2226/1892 969/2214/1887 970/2221/1889 +f 970/2221/1889 974/2220/1888 5206/2223/1891 +f 974/2220/1888 969/2214/1887 4926/2224/1246 +f 974/2220/1888 4926/2224/1246 5205/2222/1890 +f 971/2228/1894 968/2226/1892 975/2227/1893 +f 5209/2234/1899 971/2228/1894 5208/2229/1895 +f 975/2227/1893 970/2221/1889 5207/2230/1896 +f 5208/2229/1895 975/2227/1893 7566/2231/1897 +f 972/2212/1885 968/2226/1892 976/2232/1898 +f 5194/2217/1864 972/2212/1885 5195/2233/1865 +f 976/2232/1898 971/2228/1894 5210/2235/1900 +f 5195/2233/1865 976/2232/1898 7568/2236/1866 +f 978/2240/1904 977/2237/1901 982/2239/1903 +f 5212/2246/1910 978/2240/1904 5211/2241/1905 +f 981/2238/1902 5203/2203/1879 982/2239/1903 +f 982/2239/1903 5202/2196/1873 5211/2241/1905 +f 977/2237/1901 978/2240/1904 979/2243/1907 +f 979/2243/1907 983/2242/1906 5215/2245/1909 +f 978/2240/1904 5212/2246/1910 983/2242/1906 +f 983/2242/1906 5213/2247/1911 5214/2244/1908 +f 980/2250/1914 977/2237/1901 984/2249/1913 +f 5218/2255/1919 980/2250/1914 5217/2251/1915 +f 984/2249/1913 979/2243/1907 5216/2252/1916 +f 984/2249/1913 5216/2252/1916 5217/2251/1915 +f 977/2237/1901 980/2250/1914 981/2238/1902 +f 981/2238/1902 985/2254/1918 5203/2203/1879 +f 980/2250/1914 5218/2255/1919 985/2254/1918 +f 985/2254/1918 5219/2256/1920 5204/2204/1880 +f 987/2260/1924 986/2257/1921 991/2259/1923 +f 5224/2268/1932 987/2260/1924 5223/2261/1925 +f 991/2259/1923 990/2258/1922 5222/2263/1927 +f 991/2259/1923 5222/2263/1927 5223/2261/1925 +f 986/2257/1921 987/2260/1924 988/2265/1929 +f 988/2265/1929 992/2264/1928 5227/2267/1931 +f 987/2260/1924 5224/2268/1932 992/2264/1928 +f 992/2264/1928 5225/2269/1933 5226/2266/1930 +f 989/2273/1937 986/2257/1921 993/2271/1935 +f 989/2273/1937 993/2271/1935 5206/2223/1891 +f 993/2271/1935 988/2265/1929 5228/2272/1936 +f 993/2271/1935 5228/2272/1936 5207/2230/1896 +f 986/2257/1921 989/2273/1937 990/2258/1922 +f 990/2258/1922 994/2274/1938 5221/2262/1926 +f 989/2273/1937 5206/2223/1891 994/2274/1938 +f 994/2274/1938 5205/2222/1890 5220/2275/1939 +f 995/2281/1945 999/2276/1940 996/2278/1942 +f 996/2278/1942 1000/2277/1941 5098/1884/1610 +f 999/2276/1940 5230/2279/1943 1000/2277/1941 +f 1000/2277/1941 5231/2280/1944 5099/1885/1611 +f 997/2283/1947 995/2281/1945 1001/2282/1946 +f 5089/2289/1584 997/2283/1947 5090/2284/1589 +f 1001/2282/1946 996/2278/1942 5097/1900/1624 +f 1001/2282/1946 5097/1900/1624 5090/2284/1589 +f 995/2281/1945 997/2283/1947 998/2286/1949 +f 998/2286/1949 1002/2285/1948 5233/2288/1951 +f 997/2283/1947 5089/2289/1584 1002/2285/1948 +f 1002/2285/1948 5088/2290/1583 5232/2287/1950 +f 999/2276/1940 995/2281/1945 1003/2292/1952 +f 5230/2279/1943 999/2276/1940 5229/2293/1953 +f 1003/2292/1952 998/2286/1949 5234/2294/1954 +f 1003/2292/1952 5234/2294/1954 5229/2293/1953 +f 1004/2304/1961 1008/2296/1956 1005/2298/1958 +f 1005/2298/1958 1009/2297/1957 5092/2300/1593 +f 1008/2296/1956 5236/2301/1959 1009/2297/1957 +f 1009/2297/1957 5237/2302/1960 5093/2299/1594 +f 1006/2306/1963 1004/2304/1961 1010/2305/1962 +f 5113/1920/1639 1006/2306/1963 5114/1926/1644 +f 1010/2305/1962 1005/2298/1958 5091/2307/1604 +f 1010/2305/1962 5091/2307/1604 5114/1926/1644 +f 1004/2304/1961 1006/2306/1963 1007/2309/1965 +f 1007/2309/1965 1011/2308/1964 5239/2311/1967 +f 1006/2306/1963 5113/1920/1639 1011/2308/1964 +f 1011/2308/1964 5112/1919/1638 5238/2310/1966 +f 1008/2296/1956 1004/2304/1961 1012/2312/1968 +f 5236/2301/1959 1008/2296/1956 5235/2313/1969 +f 1012/2312/1968 1007/2309/1965 5240/2314/1970 +f 1012/2312/1968 5240/2314/1970 5235/2313/1969 +f 1014/2319/1975 1013/2316/1972 1018/2318/1974 +f 5233/2288/1951 1014/2319/1975 5234/2294/1954 +f 1018/2318/1974 1017/2317/1973 5243/2321/1977 +f 5234/2294/1954 1018/2318/1974 7563/2295/1955 +f 1013/2316/1972 1014/2319/1975 1015/2323/1979 +f 1015/2323/1979 1019/2322/1978 5245/2325/1981 +f 1014/2319/1975 5233/2288/1951 1019/2322/1978 +f 1019/2322/1978 5232/2287/1950 5244/2324/1980 +f 1016/2327/1983 1013/2316/1972 1020/2326/1982 +f 5248/2333/1989 1016/2327/1983 5247/2328/1984 +f 1020/2326/1982 1015/2323/1979 5246/2329/1985 +f 5247/2328/1984 1020/2326/1982 7629/2330/1986 +f 1013/2316/1972 1016/2327/1983 1017/2317/1973 +f 1017/2317/1973 1021/2331/1987 5242/2320/1976 +f 1016/2327/1983 5248/2333/1989 1021/2331/1987 +f 1021/2331/1987 5249/2334/1990 5241/2332/1988 +f 1023/2339/1995 1022/2336/1992 1027/2338/1994 +f 5245/2346/1981 1023/2339/1995 5246/2340/1985 +f 1027/2338/1994 1026/2337/1993 5252/2342/1997 +f 5246/2340/1985 1027/2338/1994 7629/2343/1986 +f 1022/2336/1992 1023/2339/1995 1024/2345/1999 +f 1024/2345/1999 1028/2344/1998 5236/2301/1959 +f 1023/2339/1995 5245/2346/1981 1028/2344/1998 +f 1028/2344/1998 5244/2347/1980 5237/2302/1960 +f 1025/2349/2001 1022/2336/1992 1029/2348/2000 +f 5254/2353/2005 1025/2349/2001 5253/2350/2002 +f 1029/2348/2000 1024/2345/1999 5235/2313/1969 +f 5253/2350/2002 1029/2348/2000 7564/2315/1971 +f 1022/2336/1992 1025/2349/2001 1026/2337/1993 +f 1026/2337/1993 1030/2351/2003 5251/2341/1996 +f 1025/2349/2001 5254/2353/2005 1030/2351/2003 +f 1030/2351/2003 5255/2354/2006 5250/2352/2004 +f 1031/2361/2013 1035/2356/2008 1032/2358/2010 +f 1032/2358/2010 1036/2357/2009 5215/2245/1909 +f 1035/2356/2008 5257/2359/2011 1036/2357/2009 +f 1036/2357/2009 5258/2360/2012 5216/2252/1916 +f 1033/2363/2015 1031/2361/2013 1037/2362/2014 +f 5260/2367/2019 1033/2363/2015 5259/2364/2016 +f 1037/2362/2014 1032/2358/2010 5214/2244/1908 +f 5259/2364/2016 1037/2362/2014 7632/2248/1912 +f 1031/2361/2013 1033/2363/2015 1034/2366/2018 +f 5248/2333/1989 1034/2366/2018 5249/2334/1990 +f 1033/2363/2015 5260/2367/2019 1038/2365/2017 +f 5249/2334/1990 1038/2365/2017 7634/2335/1991 +f 1035/2356/2008 1031/2361/2013 1039/2369/2021 +f 5257/2359/2011 1035/2356/2008 5256/2370/2022 +f 1039/2369/2021 1034/2366/2018 5247/2328/1984 +f 5256/2370/2022 1039/2369/2021 7629/2330/1986 +f 1040/2379/2028 1044/2371/2023 1041/2373/2025 +f 1041/2373/2025 1045/2372/2024 5263/2375/2027 +f 1045/2372/2024 1044/2371/2023 5250/2377/2004 +f 5262/2374/2026 1045/2372/2024 7635/2378/2007 +f 1042/2381/2030 1040/2379/2028 1046/2380/2029 +f 5224/2268/1932 1042/2381/2030 5225/2269/1933 +f 1046/2380/2029 1041/2373/2025 5264/2382/2031 +f 5225/2269/1933 1046/2380/2029 7633/2270/1934 +f 1040/2379/2028 1042/2381/2030 1043/2384/2033 +f 1043/2384/2033 1047/2383/2032 5257/2359/2011 +f 1042/2381/2030 5224/2268/1932 1047/2383/2032 +f 1047/2383/2032 5223/2261/1925 5258/2360/2012 +f 1044/2371/2023 1040/2379/2028 1048/2385/2034 +f 5251/2376/1996 1044/2371/2023 5252/2386/1997 +f 1048/2385/2034 1043/2384/2033 5256/2370/2022 +f 5252/2386/1997 1048/2385/2034 7629/2330/1986 +f 1050/2390/2038 1049/2387/2035 1054/2389/2037 +f 5269/2399/2047 1050/2390/2038 5268/2391/2039 +f 1054/2389/2037 1053/2388/2036 5267/2393/2041 +f 5268/2391/2039 1054/2389/2037 7586/2394/2042 +f 1049/2387/2035 1050/2390/2038 1051/2396/2044 +f 1051/2396/2044 1055/2395/2043 5272/2398/2046 +f 1050/2390/2038 5269/2399/2047 1055/2395/2043 +f 1055/2395/2043 5270/2400/2048 5271/2397/2045 +f 1052/2403/2051 1049/2387/2035 1056/2402/2050 +f 4828/1247/1013 1052/2403/2051 4829/1248/1014 +f 1056/2402/2050 1051/2396/2044 5273/2404/2052 +f 4829/1248/1014 1056/2402/2050 7627/1249/1015 +f 1049/2387/2035 1052/2403/2051 1053/2388/2036 +f 1053/2388/2036 1057/2405/2053 5266/2392/2040 +f 1052/2403/2051 4828/1247/1013 1057/2405/2053 +f 1057/2405/2053 4827/1240/1006 5265/2406/2054 +f 1059/2410/2058 1058/2407/2055 1063/2409/2057 +f 5272/2398/2046 1059/2410/2058 5273/2404/2052 +f 1063/2409/2057 1062/2408/2056 4833/1257/1023 +f 5273/2404/2052 1063/2409/2057 7627/1249/1015 +f 1058/2407/2055 1059/2410/2058 1060/2412/2060 +f 1060/2412/2060 1064/2411/2059 5275/2414/2062 +f 1059/2410/2058 5272/2398/2046 1064/2411/2059 +f 1064/2411/2059 5271/2397/2045 5274/2413/2061 +f 1061/2416/2064 1058/2407/2055 1065/2415/2063 +f 5278/2421/2069 1061/2416/2064 5277/2417/2065 +f 1065/2415/2063 1060/2412/2060 5276/2418/2066 +f 5277/2417/2065 1065/2415/2063 7587/2419/2067 +f 1058/2407/2055 1061/2416/2064 1062/2408/2056 +f 1062/2408/2056 1066/2420/2068 4834/1258/1024 +f 1061/2416/2064 5278/2421/2069 1066/2420/2068 +f 1066/2420/2068 5279/2422/2070 4835/1263/1029 +f 1068/2426/2074 1067/2423/2071 1072/2425/2073 +f 5284/2433/2081 1068/2426/2074 5283/2427/2075 +f 1071/2424/2072 5281/2428/2076 1072/2425/2073 +f 1072/2425/2073 5282/2429/2077 5283/2427/2075 +f 1067/2423/2071 1068/2426/2074 1069/2432/2080 +f 1069/2432/2080 1073/2431/2079 5266/2392/2040 +f 1068/2426/2074 5284/2433/2081 1073/2431/2079 +f 1073/2431/2079 5285/2434/2082 5267/2393/2041 +f 1070/2436/2084 1067/2423/2071 1074/2435/2083 +f 4849/1290/1056 1070/2436/2084 4850/1291/1057 +f 1074/2435/2083 1069/2432/2080 5265/2406/2054 +f 4850/1291/1057 1074/2435/2083 7625/1242/1008 +f 1067/2423/2071 1070/2436/2084 1071/2424/2072 +f 1071/2424/2072 1075/2437/2085 5281/2428/2076 +f 1070/2436/2084 4849/1290/1056 1075/2437/2085 +f 1075/2437/2085 4848/1285/1051 5280/2438/2086 +f 1077/2442/2090 1076/2439/2087 1081/2441/2089 +f 5278/2421/2069 1077/2442/2090 5279/2422/2070 +f 1081/2441/2089 1080/2440/2088 4851/1297/1063 +f 5279/2422/2070 1081/2441/2089 7626/1264/1030 +f 1076/2439/2087 1077/2442/2090 1078/2444/2092 +f 1078/2444/2092 1082/2443/2091 5287/2446/2094 +f 1077/2442/2090 5278/2421/2069 1082/2443/2091 +f 1082/2443/2091 5277/2417/2065 5286/2445/2093 +f 1079/2452/2100 1076/2439/2087 1083/2447/2095 +f 1079/2452/2100 1083/2447/2095 5290/2449/2097 +f 1083/2447/2095 1078/2444/2092 5288/2450/2098 +f 1083/2447/2095 5288/2450/2098 5289/2448/2096 +f 1076/2439/2087 1079/2452/2100 1080/2440/2088 +f 1080/2440/2088 1084/2453/2101 4852/1298/1064 +f 1079/2452/2100 5290/2449/2097 1084/2453/2101 +f 1084/2453/2101 5291/2454/2102 4853/1303/1069 +f 1085/2463/2111 1089/2455/2103 1086/2457/2105 +f 1086/2457/2105 1090/2456/2104 5296/2459/2107 +f 1089/2455/2103 5293/2460/2108 1090/2456/2104 +f 1090/2456/2104 5294/2461/2109 5295/2458/2106 +f 1087/2465/2113 1085/2463/2111 1091/2464/2112 +f 5281/2471/2076 1087/2465/2113 5282/2466/2077 +f 1091/2464/2112 1086/2457/2105 5297/2467/2114 +f 5282/2466/2077 1091/2464/2112 7584/2468/2078 +f 1085/2463/2111 1087/2465/2113 1088/2470/2116 +f 1088/2470/2116 1092/2469/2115 4867/1325/1091 +f 1087/2465/2113 5281/2471/2076 1092/2469/2115 +f 1092/2469/2115 5280/2472/2086 4868/1332/1096 +f 1089/2455/2103 1085/2463/2111 1093/2473/2117 +f 5293/2460/2108 1089/2455/2103 5292/2474/2118 +f 1093/2473/2117 1088/2470/2116 4866/1324/1090 +f 5292/2474/2118 1093/2473/2117 7621/1328/1094 +f 1094/2478/2122 1098/2475/2119 1095/2477/2121 +f 1095/2477/2121 1099/2476/2120 5290/2449/2097 +f 1098/2475/2119 4870/1348/1109 1099/2476/2120 +f 1099/2476/2120 4869/1343/1104 5291/2454/2102 +f 1096/2480/2124 1094/2478/2122 1100/2479/2123 +f 5299/2486/2130 1096/2480/2124 5298/2481/2125 +f 1100/2479/2123 1095/2477/2121 5289/2448/2096 +f 5298/2481/2125 1100/2479/2123 7585/2451/2099 +f 1094/2478/2122 1096/2480/2124 1097/2483/2127 +f 1097/2483/2127 1101/2482/2126 5302/2485/2129 +f 1096/2480/2124 5299/2486/2130 1101/2482/2126 +f 1101/2482/2126 5300/2487/2131 5301/2484/2128 +f 1098/2475/2119 1094/2478/2122 1102/2489/2133 +f 4870/1348/1109 1098/2475/2119 4871/1349/1110 +f 1102/2489/2133 1097/2483/2127 5303/2490/2134 +f 4871/1349/1110 1102/2489/2133 7622/1350/1111 +f 1104/2494/2138 1103/2491/2135 1108/2493/2137 +f 5305/2500/2144 1104/2494/2138 5304/2495/2139 +f 1108/2493/2137 1107/2492/2136 4902/1409/1170 +f 5304/2495/2139 1108/2493/2137 7617/1412/1173 +f 1103/2491/2135 1104/2494/2138 1105/2497/2141 +f 1105/2497/2141 1109/2496/2140 5308/2499/2143 +f 1104/2494/2138 5305/2500/2144 1109/2496/2140 +f 1109/2496/2140 5306/2501/2145 5307/2498/2142 +f 1106/2504/2148 1103/2491/2135 1110/2503/2147 +f 5311/2509/2153 1106/2504/2148 5310/2505/2149 +f 1110/2503/2147 1105/2497/2141 5309/2506/2150 +f 5310/2505/2149 1110/2503/2147 7636/2507/2151 +f 1103/2491/2135 1106/2504/2148 1107/2492/2136 +f 1107/2492/2136 1111/2508/2152 4903/1410/1171 +f 1106/2504/2148 5311/2509/2153 1111/2508/2152 +f 1111/2508/2152 5312/2510/2154 4904/1416/1177 +f 1113/2514/2158 1112/2511/2155 1117/2513/2157 +f 5317/2523/2167 1113/2514/2158 5316/2515/2159 +f 1117/2513/2157 1116/2512/2156 5315/2517/2161 +f 5316/2515/2159 1117/2513/2157 7637/2518/2162 +f 1112/2511/2155 1113/2514/2158 1114/2520/2164 +f 1114/2520/2164 1118/2519/2163 5320/2522/2166 +f 1113/2514/2158 5317/2523/2167 1118/2519/2163 +f 1118/2519/2163 5318/2524/2168 5319/2521/2165 +f 1115/2527/2171 1112/2511/2155 1119/2526/2170 +f 4906/2533/1189 1115/2527/2171 4907/2528/1190 +f 1119/2526/2170 1114/2520/2164 5321/2529/2172 +f 4907/2528/1190 1119/2526/2170 7618/2530/1191 +f 1112/2511/2155 1115/2527/2171 1116/2512/2156 +f 1116/2512/2156 1120/2531/2173 5314/2516/2160 +f 1115/2527/2171 4906/2533/1189 1120/2531/2173 +f 1120/2531/2173 4905/2534/1184 5313/2532/2174 +f 1122/2539/2178 1121/2536/2175 1126/2538/2177 +f 5293/2460/2108 1122/2539/2178 5294/2461/2109 +f 1126/2538/2177 1125/2537/2176 5324/2541/2180 +f 5294/2461/2109 1126/2538/2177 7582/2462/2110 +f 1121/2536/2175 1122/2539/2178 1123/2543/2182 +f 1123/2543/2182 1127/2542/2181 4885/1371/1132 +f 1122/2539/2178 5293/2460/2108 1127/2542/2181 +f 1127/2542/2181 5292/2474/2118 4886/1376/1137 +f 1124/2545/2184 1121/2536/2175 1128/2544/2183 +f 5311/2509/2153 1124/2545/2184 5312/2510/2154 +f 1128/2544/2183 1123/2543/2182 4884/1370/1131 +f 5312/2510/2154 1128/2544/2183 7619/1373/1134 +f 1121/2536/2175 1124/2545/2184 1125/2537/2176 +f 1125/2537/2176 1129/2546/2185 5323/2540/2179 +f 1124/2545/2184 5311/2509/2153 1129/2546/2185 +f 1129/2546/2185 5310/2505/2149 5322/2547/2186 +f 1131/2551/2190 1130/2548/2187 1135/2550/2189 +f 4888/1388/1149 1131/2551/2190 4889/1389/1150 +f 1135/2550/2189 1134/2549/2188 5313/2553/2174 +f 4889/1389/1150 1135/2550/2189 7620/1390/1151 +f 1130/2548/2187 1131/2551/2190 1132/2555/2192 +f 1132/2555/2192 1136/2554/2191 5302/2485/2129 +f 1131/2551/2190 4888/1388/1149 1136/2554/2191 +f 1136/2554/2191 4887/1383/1144 5303/2490/2134 +f 1133/2557/2194 1130/2548/2187 1137/2556/2193 +f 5326/2561/2197 1133/2557/2194 5325/2558/2195 +f 1137/2556/2193 1132/2555/2192 5301/2484/2128 +f 5325/2558/2195 1137/2556/2193 7583/2488/2132 +f 1130/2548/2187 1133/2557/2194 1134/2549/2188 +f 1134/2549/2188 1138/2559/2196 5314/2552/2160 +f 1133/2557/2194 5326/2561/2197 1138/2559/2196 +f 1138/2559/2196 5327/2562/2198 5315/2560/2161 +f 1139/2572/2207 1143/2564/2199 1140/2566/2201 +f 1140/2566/2201 1144/2565/2200 5332/2568/2203 +f 1143/2564/2199 5329/2569/2204 1144/2565/2200 +f 1144/2565/2200 5330/2570/2205 5331/2567/2202 +f 1141/2574/2209 1139/2572/2207 1145/2573/2208 +f 5335/2582/2217 1141/2574/2209 5334/2575/2210 +f 1145/2573/2208 1140/2566/2201 5333/2576/2211 +f 5334/2575/2210 1145/2573/2208 7641/2577/2212 +f 1139/2572/2207 1141/2574/2209 1142/2579/2214 +f 1142/2579/2214 1146/2578/2213 5338/2581/2216 +f 1141/2574/2209 5335/2582/2217 1146/2578/2213 +f 1146/2578/2213 5336/2583/2218 5337/2580/2215 +f 1143/2564/2199 1139/2572/2207 1147/2585/2220 +f 5329/2569/2204 1143/2564/2199 5328/2586/2221 +f 1147/2585/2220 1142/2579/2214 5339/2587/2222 +f 1147/2585/2220 5339/2587/2222 5328/2586/2221 +f 1148/2593/2228 1152/2588/2223 1149/2590/2225 +f 1149/2590/2225 1153/2589/2224 5335/2582/2217 +f 1152/2588/2223 5341/2591/2226 1153/2589/2224 +f 1153/2589/2224 5342/2592/2227 5336/2583/2218 +f 1150/2595/2230 1148/2593/2228 1154/2594/2229 +f 5344/2601/2236 1150/2595/2230 5343/2596/2231 +f 1154/2594/2229 1149/2590/2225 5334/2575/2210 +f 5343/2596/2231 1154/2594/2229 7641/2577/2212 +f 1148/2593/2228 1150/2595/2230 1151/2598/2233 +f 1151/2598/2233 1155/2597/2232 5347/2600/2235 +f 1150/2595/2230 5344/2601/2236 1155/2597/2232 +f 1155/2597/2232 5345/2602/2237 5346/2599/2234 +f 1152/2588/2223 1148/2593/2228 1156/2604/2239 +f 5341/2591/2226 1152/2588/2223 5340/2605/2240 +f 1156/2604/2239 1151/2598/2233 5348/2606/2241 +f 1156/2604/2239 5348/2606/2241 5340/2605/2240 +f 1157/2612/2247 1161/2607/2242 1158/2609/2244 +f 1158/2609/2244 1162/2608/2243 5308/2499/2143 +f 1161/2607/2242 5350/2610/2245 1162/2608/2243 +f 1162/2608/2243 5351/2611/2246 5309/2506/2150 +f 1159/2614/2249 1157/2612/2247 1163/2613/2248 +f 5338/2581/2216 1159/2614/2249 5339/2587/2222 +f 1163/2613/2248 1158/2609/2244 5307/2498/2142 +f 5339/2587/2222 1163/2613/2248 7638/2502/2146 +f 1157/2612/2247 1159/2614/2249 1160/2616/2251 +f 1160/2616/2251 1164/2615/2250 5353/2618/2253 +f 1159/2614/2249 5338/2581/2216 1164/2615/2250 +f 1164/2615/2250 5337/2580/2215 5352/2617/2252 +f 1161/2607/2242 1157/2612/2247 1165/2619/2254 +f 5350/2610/2245 1161/2607/2242 5349/2620/2255 +f 1165/2619/2254 1160/2616/2251 5354/2621/2256 +f 5349/2620/2255 1165/2619/2254 7520/2622/2257 +f 1166/2626/2261 1170/2623/2258 1167/2625/2260 +f 1167/2625/2260 1171/2624/2259 5341/2591/2226 +f 1170/2623/2258 5353/2618/2253 1171/2624/2259 +f 1171/2624/2259 5352/2617/2252 5342/2592/2227 +f 1168/2628/2263 1166/2626/2261 1172/2627/2262 +f 5317/2523/2167 1168/2628/2263 5318/2524/2168 +f 1172/2627/2262 1167/2625/2260 5340/2605/2240 +f 5318/2524/2168 1172/2627/2262 7639/2525/2169 +f 1166/2626/2261 1168/2628/2263 1169/2630/2265 +f 1169/2630/2265 1173/2629/2264 5356/2632/2267 +f 1168/2628/2263 5317/2523/2167 1173/2629/2264 +f 1173/2629/2264 5316/2515/2159 5355/2631/2266 +f 1170/2623/2258 1166/2626/2261 1174/2633/2268 +f 5353/2618/2253 1170/2623/2258 5354/2621/2256 +f 1174/2633/2268 1169/2630/2265 5357/2634/2269 +f 5354/2621/2256 1174/2633/2268 7520/2622/2257 +f 1175/2643/2278 1179/2635/2270 1176/2637/2272 +f 1176/2637/2272 1180/2636/2271 5362/2639/2274 +f 1179/2635/2270 5359/2640/2275 1180/2636/2271 +f 1180/2636/2271 5360/2641/2276 5361/2638/2273 +f 1177/2646/2281 1175/2643/2278 1181/2644/2279 +f 1177/2646/2281 1181/2644/2279 5323/2540/2179 +f 1181/2644/2279 1176/2637/2272 5363/2645/2280 +f 5324/2541/2180 1181/2644/2279 7582/2462/2110 +f 1178/2648/2283 1175/2643/2278 1182/2647/2282 +f 5350/2610/2245 1178/2648/2283 5351/2611/2246 +f 1182/2647/2282 1177/2646/2281 5322/2547/2186 +f 5351/2611/2246 1182/2647/2282 7636/2507/2151 +f 1179/2635/2270 1175/2643/2278 1183/2649/2284 +f 5359/2640/2275 1179/2635/2270 5358/2650/2285 +f 1178/2648/2283 5350/2610/2245 1183/2649/2284 +f 5358/2650/2285 1183/2649/2284 7520/2622/2257 +f 1185/2654/2289 1184/2651/2286 1189/2653/2288 +f 5326/2659/2197 1185/2654/2289 5327/2655/2198 +f 1189/2653/2288 1188/2652/2287 5355/2631/2266 +f 5327/2655/2198 1189/2653/2288 7637/2518/2162 +f 1186/2657/2291 1184/2651/2286 1190/2656/2290 +f 5365/2664/2295 1186/2657/2291 5364/2658/2292 +f 1185/2654/2289 5326/2659/2197 1190/2656/2290 +f 5364/2658/2292 1190/2656/2290 7583/2661/2132 +f 1184/2651/2286 1186/2657/2291 1187/2663/2294 +f 1187/2663/2294 1191/2662/2293 5359/2640/2275 +f 1186/2657/2291 5365/2664/2295 1191/2662/2293 +f 1191/2662/2293 5366/2665/2296 5360/2641/2276 +f 1188/2652/2287 1184/2651/2286 1192/2666/2297 +f 1188/2652/2287 1192/2666/2297 5356/2632/2267 +f 1192/2666/2297 1187/2663/2294 5358/2650/2285 +f 5357/2634/2269 1192/2666/2297 7520/2622/2257 +f 1194/2670/2301 1193/2667/2298 1198/2669/2300 +f 5371/2679/2310 1194/2670/2301 5370/2671/2302 +f 1198/2669/2300 1197/2668/2299 5369/2673/2304 +f 5370/2671/2302 1198/2669/2300 7593/2674/2305 +f 1193/2667/2298 1194/2670/2301 1195/2676/2307 +f 1195/2676/2307 1199/2675/2306 5374/2678/2309 +f 1194/2670/2301 5371/2679/2310 1199/2675/2306 +f 1199/2675/2306 5372/2680/2311 5373/2677/2308 +f 1196/2683/2314 1193/2667/2298 1200/2682/2313 +f 5377/2689/2320 1196/2683/2314 5376/2684/2315 +f 1200/2682/2313 1195/2676/2307 5375/2685/2316 +f 5376/2684/2315 1200/2682/2313 7642/2686/2317 +f 1193/2667/2298 1196/2683/2314 1197/2668/2299 +f 1197/2668/2299 1201/2687/2318 5368/2672/2303 +f 1196/2683/2314 5377/2689/2320 1201/2687/2318 +f 1201/2687/2318 5378/2690/2321 5367/2688/2319 +f 1203/2695/2325 1202/2692/2322 1207/2694/2324 +f 5383/2704/2334 1203/2695/2325 5382/2696/2326 +f 1207/2694/2324 1206/2693/2323 5381/2698/2328 +f 5382/2696/2326 1207/2694/2324 7643/2699/2329 +f 1202/2692/2322 1203/2695/2325 1204/2701/2331 +f 1204/2701/2331 1208/2700/2330 5386/2703/2333 +f 1203/2695/2325 5383/2704/2334 1208/2700/2330 +f 1208/2700/2330 5384/2705/2335 5385/2702/2332 +f 1205/2708/2338 1202/2692/2322 1209/2707/2337 +f 5389/2714/2344 1205/2708/2338 5388/2709/2339 +f 1209/2707/2337 1204/2701/2331 5387/2710/2340 +f 5388/2709/2339 1209/2707/2337 7594/2711/2341 +f 1202/2692/2322 1205/2708/2338 1206/2693/2323 +f 1206/2693/2323 1210/2712/2342 5380/2697/2327 +f 1205/2708/2338 5389/2714/2344 1210/2712/2342 +f 1210/2712/2342 5390/2715/2345 5379/2713/2343 +f 1211/2725/2354 1215/2717/2346 1212/2719/2348 +f 1212/2719/2348 1216/2718/2347 5395/2721/2350 +f 1215/2717/2346 5392/2722/2351 1216/2718/2347 +f 1216/2718/2347 5393/2723/2352 5394/2720/2349 +f 1213/2727/2356 1211/2725/2354 1217/2726/2355 +f 5398/2734/2362 1213/2727/2356 5397/2728/2357 +f 1217/2726/2355 1212/2719/2348 5396/2729/2358 +f 5397/2728/2357 1217/2726/2355 7646/2730/2359 +f 1211/2725/2354 1213/2727/2356 1214/2732/2361 +f 5371/2739/2310 1214/2732/2361 5372/2733/2311 +f 1213/2727/2356 5398/2734/2362 1218/2731/2360 +f 5372/2733/2311 1218/2731/2360 7644/2736/2312 +f 1211/2725/2354 1214/2732/2361 1215/2717/2346 +f 1215/2717/2346 1219/2737/2364 5392/2722/2351 +f 1214/2732/2361 5371/2739/2310 1219/2737/2364 +f 1219/2737/2364 5370/2740/2302 5391/2738/2365 +f 1220/2750/2371 1224/2742/2366 1221/2744/2368 +f 1221/2744/2368 1225/2743/2367 5401/2746/2370 +f 1225/2743/2367 1224/2742/2366 5385/2748/2332 +f 5400/2745/2369 1225/2743/2367 7645/2749/2336 +f 1222/2752/2373 1220/2750/2371 1226/2751/2372 +f 5404/2760/2381 1222/2752/2373 5403/2753/2374 +f 1226/2751/2372 1221/2744/2368 5402/2754/2375 +f 5403/2753/2374 1226/2751/2372 7647/2755/2376 +f 1220/2750/2371 1222/2752/2373 1223/2757/2378 +f 1223/2757/2378 1227/2756/2377 5407/2759/2380 +f 1222/2752/2373 5404/2760/2381 1227/2756/2377 +f 1227/2756/2377 5405/2761/2382 5406/2758/2379 +f 1220/2750/2371 1223/2757/2378 1224/2742/2366 +f 1224/2742/2366 1228/2763/2384 5386/2747/2333 +f 1223/2757/2378 5407/2759/2380 1228/2763/2384 +f 1228/2763/2384 5408/2765/2385 5387/2764/2340 +f 1229/2775/2394 1233/2767/2386 1230/2769/2388 +f 1230/2769/2388 1234/2768/2387 5413/2771/2390 +f 1233/2767/2386 5410/2772/2391 1234/2768/2387 +f 1234/2768/2387 5411/2773/2392 5412/2770/2389 +f 1231/2777/2396 1229/2775/2394 1235/2776/2395 +f 5416/2783/2402 1231/2777/2396 5415/2778/2397 +f 1235/2776/2395 1230/2769/2388 5414/2779/2398 +f 5415/2778/2397 1235/2776/2395 7648/2780/2399 +f 1229/2775/2394 1231/2777/2396 1232/2782/2401 +f 1232/2782/2401 1236/2781/2400 5395/2721/2350 +f 1231/2777/2396 5416/2783/2402 1236/2781/2400 +f 1236/2781/2400 5417/2784/2403 5396/2729/2358 +f 1233/2767/2386 1229/2775/2394 1237/2785/2404 +f 5410/2772/2391 1233/2767/2386 5409/2786/2405 +f 1237/2785/2404 1232/2782/2401 5394/2720/2349 +f 5409/2786/2405 1237/2785/2404 7591/2724/2353 +f 1238/2795/2411 1242/2787/2406 1239/2789/2408 +f 1239/2789/2408 1243/2788/2407 5419/2791/2410 +f 1242/2787/2406 5404/2792/2381 1243/2788/2407 +f 1243/2788/2407 5403/2793/2374 5418/2790/2409 +f 1240/2797/2413 1238/2795/2411 1244/2796/2412 +f 5422/2805/2421 1240/2797/2413 5421/2798/2414 +f 1244/2796/2412 1239/2789/2408 5420/2799/2415 +f 5421/2798/2414 1244/2796/2412 7649/2800/2416 +f 1238/2795/2411 1240/2797/2413 1241/2802/2418 +f 1241/2802/2418 1245/2801/2417 5425/2804/2420 +f 1240/2797/2413 5422/2805/2421 1245/2801/2417 +f 1245/2801/2417 5423/2806/2422 5424/2803/2419 +f 1242/2787/2406 1238/2795/2411 1246/2808/2423 +f 5404/2792/2381 1242/2787/2406 5405/2809/2382 +f 1246/2808/2423 1241/2802/2418 5426/2810/2424 +f 5405/2809/2382 1246/2808/2423 7592/2811/2383 +f 1248/2815/2428 1247/2812/2425 1252/2814/2427 +f 5431/2823/2436 1248/2815/2428 5430/2816/2429 +f 1252/2814/2427 1251/2813/2426 5429/2818/2431 +f 5430/2816/2429 1252/2814/2427 7589/2819/2432 +f 1249/2821/2434 1247/2812/2425 1253/2820/2433 +f 5434/2830/2441 1249/2821/2434 5433/2822/2435 +f 1248/2815/2428 5431/2823/2436 1253/2820/2433 +f 1253/2820/2433 5432/2824/2437 5433/2822/2435 +f 1247/2812/2425 1249/2821/2434 1250/2827/2440 +f 1250/2827/2440 1254/2826/2439 5413/2829/2390 +f 1249/2821/2434 5434/2830/2441 1254/2826/2439 +f 1254/2826/2439 5435/2831/2442 5414/2828/2398 +f 1251/2813/2426 1247/2812/2425 1255/2833/2443 +f 5428/2817/2430 1251/2813/2426 5427/2834/2444 +f 1255/2833/2443 1250/2827/2440 5412/2835/2389 +f 5427/2834/2444 1255/2833/2443 7590/2836/2393 +f 1256/2845/2450 1260/2837/2445 1257/2839/2447 +f 1257/2839/2447 1261/2838/2446 5437/2841/2449 +f 1260/2837/2445 5422/2842/2421 1261/2838/2446 +f 1261/2838/2446 5421/2843/2414 5436/2840/2448 +f 1258/2851/2453 1256/2845/2450 1262/2846/2451 +f 1258/2851/2453 1262/2846/2451 5431/2848/2436 +f 1262/2846/2451 1257/2839/2447 5438/2849/2452 +f 1262/2846/2451 5438/2849/2452 5432/2847/2437 +f 1259/2853/2455 1256/2845/2450 1263/2852/2454 +f 5428/2859/2430 1259/2853/2455 5429/2854/2431 +f 1263/2852/2454 1258/2851/2453 5430/2855/2429 +f 5429/2854/2431 1263/2852/2454 7589/2856/2432 +f 1260/2837/2445 1256/2845/2450 1264/2857/2456 +f 5422/2842/2421 1260/2837/2445 5423/2858/2422 +f 1264/2857/2456 1259/2853/2455 5427/2860/2444 +f 5423/2858/2422 1264/2857/2456 7590/2861/2393 +f 1265/2870/2462 1269/2862/2457 1266/2864/2459 +f 1266/2864/2459 1270/2863/2458 5332/2866/2203 +f 1269/2862/2457 5440/2867/2460 1270/2863/2458 +f 1270/2863/2458 5441/2868/2461 5333/2865/2211 +f 1267/2872/2464 1265/2870/2462 1271/2871/2463 +f 5377/2689/2320 1267/2872/2464 5378/2690/2321 +f 1271/2871/2463 1266/2864/2459 5331/2873/2202 +f 5378/2690/2321 1271/2871/2463 7595/2691/2206 +f 1265/2870/2462 1267/2872/2464 1268/2875/2466 +f 1268/2875/2466 1272/2874/2465 5443/2877/2468 +f 1267/2872/2464 5377/2689/2320 1272/2874/2465 +f 1272/2874/2465 5376/2684/2315 5442/2876/2467 +f 1269/2862/2457 1265/2870/2462 1273/2878/2469 +f 5440/2867/2460 1269/2862/2457 5439/2879/2470 +f 1273/2878/2469 1268/2875/2466 5444/2880/2471 +f 5439/2879/2470 1273/2878/2469 7521/2881/2472 +f 1274/2887/2478 1278/2882/2473 1275/2884/2475 +f 1275/2884/2475 1279/2883/2474 5380/2697/2327 +f 1278/2882/2473 5446/2885/2476 1279/2883/2474 +f 1279/2883/2474 5447/2886/2477 5381/2698/2328 +f 1276/2889/2480 1274/2887/2478 1280/2888/2479 +f 5344/2895/2236 1276/2889/2480 5345/2890/2237 +f 1280/2888/2479 1275/2884/2475 5379/2713/2343 +f 5345/2890/2237 1280/2888/2479 7596/2716/2238 +f 1274/2887/2478 1276/2889/2480 1277/2892/2482 +f 1277/2892/2482 1281/2891/2481 5440/2894/2460 +f 1276/2889/2480 5344/2895/2236 1281/2891/2481 +f 1281/2891/2481 5343/2896/2231 5441/2893/2461 +f 1278/2882/2473 1274/2887/2478 1282/2898/2483 +f 5446/2885/2476 1278/2882/2473 5445/2899/2484 +f 1282/2898/2483 1277/2892/2482 5439/2900/2470 +f 5445/2899/2484 1282/2898/2483 7521/2901/2472 +f 1283/2909/2490 1287/2902/2485 1284/2904/2487 +f 1284/2904/2487 1288/2903/2486 5443/2906/2468 +f 1287/2902/2485 5449/2907/2488 1288/2903/2486 +f 1288/2903/2486 5450/2908/2489 5444/2905/2471 +f 1285/2911/2492 1283/2909/2490 1289/2910/2491 +f 5452/2919/2498 1285/2911/2492 5451/2912/2493 +f 1289/2910/2491 1284/2904/2487 5442/2913/2467 +f 5451/2912/2493 1289/2910/2491 7642/2914/2317 +f 1283/2909/2490 1285/2911/2492 1286/2916/2495 +f 1286/2916/2495 1290/2915/2494 5455/2918/2497 +f 1290/2915/2494 1285/2911/2492 5453/2920/2499 +f 5454/2917/2496 1290/2915/2494 7657/2921/2500 +f 1287/2902/2485 1283/2909/2490 1291/2922/2501 +f 5449/2907/2488 1287/2902/2485 5448/2923/2502 +f 1291/2922/2501 1286/2916/2495 5456/2924/2503 +f 5448/2923/2502 1291/2922/2501 7659/2925/2504 +f 1292/2933/2512 1296/2926/2505 1293/2928/2507 +f 5461/2936/2515 1293/2928/2507 5460/2929/2508 +f 1296/2926/2505 5458/2930/2509 1297/2927/2506 +f 5460/2929/2508 1297/2927/2506 7658/2932/2511 +f 1294/2935/2514 1292/2933/2512 1298/2934/2513 +f 5446/2885/2476 1294/2935/2514 5447/2886/2477 +f 1298/2934/2513 1293/2928/2507 5462/2937/2516 +f 5447/2886/2477 1298/2934/2513 7643/2699/2329 +f 1292/2933/2512 1294/2935/2514 1295/2939/2518 +f 1295/2939/2518 1299/2938/2517 5449/2907/2488 +f 1294/2935/2514 5446/2885/2476 1299/2938/2517 +f 1299/2938/2517 5445/2899/2484 5450/2908/2489 +f 1296/2926/2505 1292/2933/2512 1300/2940/2519 +f 5458/2930/2509 1296/2926/2505 5457/2941/2520 +f 1300/2940/2519 1295/2939/2518 5448/2923/2502 +f 5457/2941/2520 1300/2940/2519 7659/2925/2504 +f 1302/2945/2524 1301/2942/2521 1306/2944/2523 +f 5464/2950/2529 1302/2945/2524 5463/2946/2525 +f 1306/2944/2523 1305/2943/2522 5433/2822/2435 +f 5463/2946/2525 1306/2944/2523 7522/2825/2438 +f 1301/2942/2521 1302/2945/2524 1303/2948/2527 +f 5467/2956/2535 1303/2948/2527 5466/2949/2528 +f 1302/2945/2524 5464/2950/2529 1307/2947/2526 +f 1307/2947/2526 5465/2951/2530 5466/2949/2528 +f 1304/2954/2533 1301/2942/2521 1308/2953/2532 +f 5470/2960/2539 1304/2954/2533 5469/2955/2534 +f 1308/2953/2532 1303/2948/2527 5468/2957/2536 +f 5469/2955/2534 1308/2953/2532 7651/2958/2537 +f 1301/2942/2521 1304/2954/2533 1305/2943/2522 +f 1305/2943/2522 1309/2959/2538 5434/2830/2441 +f 1304/2954/2533 5470/2960/2539 1309/2959/2538 +f 1309/2959/2538 5471/2961/2540 5435/2831/2442 +f 1311/2965/2544 1310/2962/2541 1315/2964/2543 +f 5476/2974/2551 1311/2965/2544 5475/2966/2545 +f 1315/2964/2543 1314/2963/2542 5474/2968/2547 +f 5475/2966/2545 1315/2964/2543 7652/2969/2548 +f 1310/2962/2541 1311/2965/2544 1312/2971/2550 +f 1312/2971/2550 1316/2970/2549 5464/2973/2529 +f 1316/2970/2549 1311/2965/2544 5477/2975/2552 +f 1316/2970/2549 5477/2975/2552 5465/2972/2530 +f 1313/2978/2554 1310/2962/2541 1317/2977/2553 +f 5437/2841/2449 1313/2978/2554 5438/2849/2452 +f 1317/2977/2553 1312/2971/2550 5463/2979/2525 +f 5438/2849/2452 1317/2977/2553 7522/2850/2438 +f 1310/2962/2541 1313/2978/2554 1314/2963/2542 +f 1314/2963/2542 1318/2980/2555 5473/2967/2546 +f 1313/2978/2554 5437/2841/2449 1318/2980/2555 +f 1318/2980/2555 5436/2840/2448 5472/2981/2556 +f 1319/3002/2570 1323/2982/2557 1320/2984/2559 +f 5470/2993/2539 1320/2984/2559 5471/2985/2540 +f 1323/2982/2557 5416/2986/2402 1324/2983/2558 +f 1324/2983/2558 5415/2987/2397 5471/2985/2540 +f 1319/3002/2570 1320/2984/2559 1321/2990/2561 +f 1321/2990/2561 1325/2989/2560 5479/2992/2563 +f 1320/2984/2559 5470/2993/2539 1325/2989/2560 +f 1325/2989/2560 5469/2994/2534 5478/2991/2562 +f 1319/3002/2570 1321/2990/2561 1322/2997/2565 +f 1322/2997/2565 1326/2996/2564 5482/2999/2567 +f 1321/2990/2561 5479/2992/2563 1326/2996/2564 +f 1326/2996/2564 5480/3000/2568 5481/2998/2566 +f 1323/2982/2557 1319/3002/2570 1327/3003/2571 +f 5416/2986/2402 1323/2982/2557 5417/3004/2403 +f 1327/3003/2571 1322/2997/2565 5483/3005/2572 +f 5417/3004/2403 1327/3003/2571 7646/3006/2359 +f 1328/3027/2586 1332/3007/2573 1329/3009/2575 +f 1329/3009/2575 1333/3008/2574 5488/3011/2577 +f 1332/3007/2573 5485/3012/2578 1333/3008/2574 +f 1333/3008/2574 5486/3013/2579 5487/3010/2576 +f 1328/3027/2586 1329/3009/2575 1330/3016/2582 +f 1330/3016/2582 1334/3015/2581 5473/3018/2546 +f 1329/3009/2575 5488/3011/2577 1334/3015/2581 +f 1334/3015/2581 5489/3019/2583 5474/3017/2547 +f 1328/3027/2586 1330/3016/2582 1331/3022/2585 +f 1331/3022/2585 1335/3021/2584 5419/3024/2410 +f 1335/3021/2584 1330/3016/2582 5472/3025/2556 +f 1335/3021/2584 5472/3025/2556 5420/3023/2415 +f 1332/3007/2573 1328/3027/2586 1336/3028/2587 +f 5485/3012/2578 1332/3007/2573 5484/3029/2588 +f 1336/3028/2587 1331/3022/2585 5418/3030/2409 +f 5484/3029/2588 1336/3028/2587 7647/2755/2376 +f 1337/3036/2592 1341/3031/2589 1338/3033/2591 +f 1338/3033/2591 1342/3032/2590 5482/2999/2567 +f 1341/3031/2589 5398/3034/2362 1342/3032/2590 +f 1342/3032/2590 5397/3035/2357 5483/3005/2572 +f 1339/3038/2594 1337/3036/2592 1343/3037/2593 +f 5491/3044/2600 1339/3038/2594 5490/3039/2595 +f 1343/3037/2593 1338/3033/2591 5481/2998/2566 +f 5490/3039/2595 1343/3037/2593 7653/3001/2569 +f 1337/3036/2592 1339/3038/2594 1340/3041/2597 +f 1340/3041/2597 1344/3040/2596 5494/3043/2599 +f 1344/3040/2596 1339/3038/2594 5492/3045/2601 +f 1344/3040/2596 5492/3045/2601 5493/3042/2598 +f 1341/3031/2589 1337/3036/2592 1345/3047/2603 +f 5398/3034/2362 1341/3031/2589 5399/3048/2363 +f 1345/3047/2603 1340/3041/2597 5495/3049/2604 +f 5399/3048/2363 1345/3047/2603 7644/3050/2312 +f 1346/3058/2612 1350/3051/2605 1347/3053/2607 +f 5500/3061/2615 1347/3053/2607 5499/3054/2608 +f 1350/3051/2605 5497/3055/2609 1351/3052/2606 +f 1351/3052/2606 5498/3056/2610 5499/3054/2608 +f 1348/3060/2614 1346/3058/2612 1352/3059/2613 +f 5485/3012/2578 1348/3060/2614 5486/3013/2579 +f 1352/3059/2613 1347/3053/2607 5501/3062/2616 +f 5486/3013/2579 1352/3059/2613 7654/3014/2580 +f 1346/3058/2612 1348/3060/2614 1349/3064/2618 +f 1349/3064/2618 1353/3063/2617 5401/2746/2370 +f 1348/3060/2614 5485/3012/2578 1353/3063/2617 +f 1353/3063/2617 5484/3029/2588 5402/2754/2375 +f 1350/3051/2605 1346/3058/2612 1354/3065/2619 +f 5497/3055/2609 1350/3051/2605 5496/3066/2620 +f 1354/3065/2619 1349/3064/2618 5400/2745/2369 +f 5496/3066/2620 1354/3065/2619 7645/2749/2336 +f 1356/3070/2624 1355/3067/2621 1360/3069/2623 +f 5494/3075/2599 1356/3070/2624 5495/3071/2604 +f 1360/3069/2623 1359/3068/2622 5373/2677/2308 +f 5495/3071/2604 1360/3069/2623 7644/2681/2312 +f 1357/3073/2626 1355/3067/2621 1361/3072/2625 +f 5503/3082/2630 1357/3073/2626 5502/3074/2627 +f 1361/3072/2625 1356/3070/2624 5493/3076/2598 +f 5502/3074/2627 1361/3072/2625 7655/3077/2602 +f 1355/3067/2621 1357/3073/2626 1358/3079/2629 +f 1358/3079/2629 1362/3078/2628 5452/3081/2498 +f 1357/3073/2626 5503/3082/2630 1362/3078/2628 +f 1362/3078/2628 5504/3083/2631 5453/3080/2499 +f 1355/3067/2621 1358/3079/2629 1359/3068/2622 +f 1359/3068/2622 1363/3085/2632 5374/2678/2309 +f 1358/3079/2629 5452/3081/2498 1363/3085/2632 +f 1363/3085/2632 5451/3086/2493 5375/2685/2316 +f 1364/3092/2638 1368/3087/2633 1365/3089/2635 +f 1365/3089/2635 1369/3088/2634 5506/3091/2637 +f 1368/3087/2633 5461/2936/2515 1369/3088/2634 +f 1369/3088/2634 5460/2929/2508 5505/3090/2636 +f 1366/3094/2640 1364/3092/2638 1370/3093/2639 +f 5497/3100/2609 1366/3094/2640 5498/3095/2610 +f 1370/3093/2639 1365/3089/2635 5507/3096/2641 +f 5498/3095/2610 1370/3093/2639 7656/3097/2611 +f 1367/3099/2643 1364/3092/2638 1371/3098/2642 +f 5383/2704/2334 1367/3099/2643 5384/2705/2335 +f 1371/3098/2642 1366/3094/2640 5496/3101/2620 +f 5384/2705/2335 1371/3098/2642 7645/2706/2336 +f 1364/3092/2638 1367/3099/2643 1368/3087/2633 +f 1368/3087/2633 1372/3102/2644 5461/2936/2515 +f 1367/3099/2643 5383/2704/2334 1372/3102/2644 +f 1372/3102/2644 5382/2696/2326 5462/2937/2516 +f 1373/3111/2650 1377/3103/2645 1374/3105/2647 +f 1374/3105/2647 1378/3104/2646 5509/3107/2649 +f 1377/3103/2645 5467/3108/2535 1378/3104/2646 +f 1378/3104/2646 5466/3109/2528 5508/3106/2648 +f 1375/3114/2653 1373/3111/2650 1379/3112/2651 +f 1375/3114/2653 1379/3112/2651 5491/3044/2600 +f 1379/3112/2651 1374/3105/2647 5510/3113/2652 +f 5492/3045/2601 1379/3112/2651 7655/3046/2602 +f 1376/3116/2655 1373/3111/2650 1380/3115/2654 +f 5479/2992/2563 1376/3116/2655 5480/3000/2568 +f 1380/3115/2654 1375/3114/2653 5490/3039/2595 +f 5480/3000/2568 1380/3115/2654 7653/3001/2569 +f 1377/3103/2645 1373/3111/2650 1381/3117/2656 +f 5467/3108/2535 1377/3103/2645 5468/3118/2536 +f 1381/3117/2656 1376/3116/2655 5478/2991/2562 +f 5468/3118/2536 1381/3117/2656 7651/2995/2537 +f 1383/3122/2660 1382/3119/2657 1387/3121/2659 +f 5500/3130/2615 1383/3122/2660 5501/3123/2616 +f 1387/3121/2659 1386/3120/2658 5487/3125/2576 +f 5501/3123/2616 1387/3121/2659 7654/3126/2580 +f 1384/3128/2662 1382/3119/2657 1388/3127/2661 +f 5512/3135/2666 1384/3128/2662 5511/3129/2663 +f 1383/3122/2660 5500/3130/2615 1388/3127/2661 +f 5511/3129/2663 1388/3127/2661 7656/3132/2611 +f 1382/3119/2657 1384/3128/2662 1385/3134/2665 +f 1385/3134/2665 1389/3133/2664 5476/2974/2551 +f 1384/3128/2662 5512/3135/2666 1389/3133/2664 +f 1389/3133/2664 5513/3136/2667 5477/2975/2552 +f 1386/3120/2658 1382/3119/2657 1390/3137/2668 +f 5488/3124/2577 1386/3120/2658 5489/3138/2583 +f 1390/3137/2668 1385/3134/2665 5475/2966/2545 +f 5489/3138/2583 1390/3137/2668 7652/2969/2548 +f 1392/3142/2672 1391/3139/2669 1396/3141/2671 +f 5515/3149/2676 1392/3142/2672 5514/3143/2673 +f 1396/3141/2671 1395/3140/2670 5508/3145/2648 +f 1396/3141/2671 5508/3145/2648 5514/3143/2673 +f 1391/3139/2669 1392/3142/2672 1393/3148/2675 +f 1393/3148/2675 1397/3147/2674 5455/2918/2497 +f 1392/3142/2672 5515/3149/2676 1397/3147/2674 +f 1397/3147/2674 5516/3150/2677 5456/2924/2503 +f 1394/3152/2679 1391/3139/2669 1398/3151/2678 +f 5503/3156/2630 1394/3152/2679 5504/3153/2631 +f 1398/3151/2678 1393/3148/2675 5454/2917/2496 +f 5504/3153/2631 1398/3151/2678 7657/2921/2500 +f 1391/3139/2669 1394/3152/2679 1395/3140/2670 +f 1395/3140/2670 1399/3154/2680 5509/3144/2649 +f 1394/3152/2679 5503/3156/2630 1399/3154/2680 +f 1399/3154/2680 5502/3157/2627 5510/3155/2652 +f 1401/3162/2684 1400/3159/2681 1405/3161/2683 +f 5458/2930/2509 1401/3162/2684 5459/2931/2510 +f 1405/3161/2683 1404/3160/2682 5505/3090/2636 +f 5459/2931/2510 1405/3161/2683 7658/2932/2511 +f 1400/3159/2681 1401/3162/2684 1402/3164/2686 +f 1402/3164/2686 1406/3163/2685 5515/3149/2676 +f 1401/3162/2684 5458/2930/2509 1406/3163/2685 +f 1406/3163/2685 5457/2941/2520 5516/3150/2677 +f 1403/3166/2688 1400/3159/2681 1407/3165/2687 +f 5512/3169/2666 1403/3166/2688 5513/3167/2667 +f 1407/3165/2687 1402/3164/2686 5514/3143/2673 +f 1407/3165/2687 5514/3143/2673 5513/3167/2667 +f 1400/3159/2681 1403/3166/2688 1404/3160/2682 +f 1404/3160/2682 1408/3168/2689 5506/3091/2637 +f 1403/3166/2688 5512/3169/2666 1408/3168/2689 +f 1408/3168/2689 5511/3170/2663 5507/3096/2641 +f 1410/3174/2693 1409/3171/2690 1414/3173/2692 +f 5410/3183/2391 1410/3174/2693 5411/3175/2392 +f 1414/3173/2692 1413/3172/2691 5519/3177/2695 +f 1414/3173/2692 5519/3177/2695 5411/3175/2392 +f 1409/3171/2690 1410/3174/2693 1411/3180/2697 +f 1411/3180/2697 1415/3179/2696 5521/3182/2699 +f 1410/3174/2693 5410/3183/2391 1415/3179/2696 +f 1415/3179/2696 5409/3184/2405 5520/3181/2698 +f 1412/3187/2701 1409/3171/2690 1416/3186/2700 +f 5218/3193/1919 1412/3187/2701 5219/3188/1920 +f 1416/3186/2700 1411/3180/2697 5522/3189/2702 +f 5219/3188/1920 1416/3186/2700 7615/3190/1224 +f 1409/3171/2690 1412/3187/2701 1413/3172/2691 +f 1413/3172/2691 1417/3191/2703 5518/3176/2694 +f 1412/3187/2701 5218/3193/1919 1417/3191/2703 +f 1417/3191/2703 5217/3194/1915 5517/3192/2704 +f 1419/3199/2708 1418/3196/2705 1423/3198/2707 +f 5524/3205/2712 1419/3199/2708 5523/3200/2709 +f 1423/3198/2707 1422/3197/2706 5220/2275/1939 +f 5523/3200/2709 1423/3198/2707 7616/2225/1249 +f 1418/3196/2705 1419/3199/2708 1420/3202/2711 +f 1420/3202/2711 1424/3201/2710 5425/3204/2420 +f 1419/3199/2708 5524/3205/2712 1424/3201/2710 +f 1424/3201/2710 5525/3206/2713 5426/3203/2424 +f 1421/3209/2715 1418/3196/2705 1425/3208/2714 +f 5518/3214/2694 1421/3209/2715 5519/3210/2695 +f 1425/3208/2714 1420/3202/2711 5424/3211/2419 +f 1425/3208/2714 5424/3211/2419 5519/3210/2695 +f 1418/3196/2705 1421/3209/2715 1422/3197/2706 +f 1422/3197/2706 1426/3213/2716 5221/2262/1926 +f 1421/3209/2715 5518/3214/2694 1426/3213/2716 +f 1426/3213/2716 5517/3215/2704 5222/2263/1927 +f 1428/3219/2720 1427/3216/2717 1432/3218/2719 +f 5392/3224/2351 1428/3219/2720 5393/3220/2352 +f 1432/3218/2719 1431/3217/2718 5520/3181/2698 +f 5393/3220/2352 1432/3218/2719 7591/3185/2353 +f 1429/3222/2722 1427/3216/2717 1433/3221/2721 +f 5527/3231/2728 1429/3222/2722 5526/3223/2723 +f 1433/3221/2721 1428/3219/2720 5391/3225/2365 +f 5526/3223/2723 1433/3221/2721 7593/3226/2305 +f 1427/3216/2717 1429/3222/2722 1430/3228/2725 +f 1430/3228/2725 1434/3227/2724 5530/3230/2727 +f 1429/3222/2722 5527/3231/2728 1434/3227/2724 +f 1434/3227/2724 5528/3232/2729 5529/3229/2726 +f 1431/3217/2718 1427/3216/2717 1435/3234/2731 +f 1431/3217/2718 1435/3234/2731 5521/3182/2699 +f 1435/3234/2731 1430/3228/2725 5531/3235/2732 +f 1435/3234/2731 5531/3235/2732 5522/3189/2702 +f 1436/3244/2741 1440/3236/2733 1437/3238/2735 +f 1437/3238/2735 1441/3237/2734 5536/3240/2737 +f 1440/3236/2733 5533/3241/2738 1441/3237/2734 +f 1441/3237/2734 5534/3242/2739 5535/3239/2736 +f 1438/3246/2743 1436/3244/2741 1442/3245/2742 +f 5407/3252/2380 1438/3246/2743 5408/3247/2385 +f 1442/3245/2742 1437/3238/2735 5537/3248/2744 +f 5408/3247/2385 1442/3245/2742 7594/3249/2341 +f 1439/3251/2746 1436/3244/2741 1443/3250/2745 +f 5524/3205/2712 1439/3251/2746 5525/3206/2713 +f 1443/3250/2745 1438/3246/2743 5406/3253/2379 +f 5525/3206/2713 1443/3250/2745 7592/3207/2383 +f 1440/3236/2733 1436/3244/2741 1444/3254/2747 +f 5533/3241/2738 1440/3236/2733 5532/3255/2748 +f 1439/3251/2746 5524/3205/2712 1444/3254/2747 +f 1444/3254/2747 5523/3200/2709 5532/3255/2748 +f 1445/3264/2752 1449/3256/2749 1446/3258/2751 +f 1446/3258/2751 1450/3257/2750 5368/3260/2303 +f 1449/3256/2749 5527/3261/2728 1450/3257/2750 +f 1450/3257/2750 5526/3262/2723 5369/3259/2304 +f 1447/3266/2754 1445/3264/2752 1451/3265/2753 +f 5539/3273/2760 1447/3266/2754 5538/3267/2755 +f 1451/3265/2753 1446/3258/2751 5367/3268/2319 +f 1451/3265/2753 5367/3268/2319 5538/3267/2755 +f 1445/3264/2752 1447/3266/2754 1448/3270/2757 +f 1448/3270/2757 1452/3269/2756 5542/3272/2759 +f 1447/3266/2754 5539/3273/2760 1452/3269/2756 +f 1452/3269/2756 5540/3274/2761 5541/3271/2758 +f 1449/3256/2749 1445/3264/2752 1453/3276/2763 +f 5527/3261/2728 1449/3256/2749 5528/3277/2729 +f 1453/3276/2763 1448/3270/2757 5543/3278/2764 +f 5528/3277/2729 1453/3276/2763 7662/3279/2730 +f 1454/3288/2773 1458/3280/2765 1455/3282/2767 +f 1455/3282/2767 1459/3281/2766 5548/3284/2769 +f 1458/3280/2765 5545/3285/2770 1459/3281/2766 +f 1459/3281/2766 5546/3286/2771 5547/3283/2768 +f 1456/3290/2775 1454/3288/2773 1460/3289/2774 +f 5389/2714/2344 1456/3290/2775 5390/2715/2345 +f 1460/3289/2774 1455/3282/2767 5549/3291/2776 +f 1460/3289/2774 5549/3291/2776 5390/2715/2345 +f 1454/3288/2773 1456/3290/2775 1457/3293/2778 +f 1457/3293/2778 1461/3292/2777 5536/3295/2737 +f 1456/3290/2775 5389/2714/2344 1461/3292/2777 +f 1461/3292/2777 5388/2709/2339 5537/3294/2744 +f 1458/3280/2765 1454/3288/2773 1462/3296/2779 +f 5545/3285/2770 1458/3280/2765 5544/3297/2780 +f 1462/3296/2779 1457/3293/2778 5535/3298/2736 +f 5544/3297/2780 1462/3296/2779 7663/3299/2740 +f 1463/3303/2784 1467/3300/2781 1464/3302/2783 +f 1464/3302/2783 1468/3301/2782 5329/2569/2204 +f 1467/3300/2781 5539/3273/2760 1468/3301/2782 +f 1468/3301/2782 5538/3267/2755 5330/2570/2205 +f 1465/3305/2786 1463/3303/2784 1469/3304/2785 +f 5305/2500/2144 1465/3305/2786 5306/2501/2145 +f 1469/3304/2785 1464/3302/2783 5328/2586/2221 +f 5306/2501/2145 1469/3304/2785 7638/2502/2146 +f 1463/3303/2784 1465/3305/2786 1466/3307/2788 +f 1466/3307/2788 1470/3306/2787 5551/3309/2790 +f 1465/3305/2786 5305/2500/2144 1470/3306/2787 +f 1470/3306/2787 5304/2495/2139 5550/3308/2789 +f 1467/3300/2781 1463/3303/2784 1471/3310/2791 +f 5539/3273/2760 1467/3300/2781 5540/3274/2761 +f 1471/3310/2791 1466/3307/2788 5552/3311/2792 +f 5540/3274/2761 1471/3310/2791 7664/3275/2762 +f 1472/3317/2798 1476/3312/2793 1473/3314/2795 +f 1473/3314/2795 1477/3313/2794 5320/2522/2166 +f 1476/3312/2793 5554/3315/2796 1477/3313/2794 +f 1477/3313/2794 5555/3316/2797 5321/2529/2172 +f 1474/3319/2800 1472/3317/2798 1478/3318/2799 +f 5347/2600/2235 1474/3319/2800 5348/2606/2241 +f 1478/3318/2799 1473/3314/2795 5319/2521/2165 +f 5348/2606/2241 1478/3318/2799 7639/2525/2169 +f 1472/3317/2798 1474/3319/2800 1475/3321/2802 +f 1475/3321/2802 1479/3320/2801 5548/3323/2769 +f 1474/3319/2800 5347/2600/2235 1479/3320/2801 +f 1479/3320/2801 5346/2599/2234 5549/3322/2776 +f 1476/3312/2793 1472/3317/2798 1480/3324/2803 +f 5554/3315/2796 1476/3312/2793 5553/3325/2804 +f 1480/3324/2803 1475/3321/2802 5547/3326/2768 +f 5553/3325/2804 1480/3324/2803 7665/3327/2772 +f 1482/3331/2808 1481/3328/2805 1486/3330/2807 +f 5557/3336/2813 1482/3331/2808 5556/3332/2809 +f 1486/3330/2807 1485/3329/2806 4899/1402/1163 +f 5556/3332/2809 1486/3330/2807 7532/1406/1167 +f 1483/3334/2811 1481/3328/2805 1487/3333/2810 +f 5560/3341/2818 1483/3334/2811 5559/3335/2812 +f 1482/3331/2808 5557/3336/2813 1487/3333/2810 +f 1487/3333/2810 5558/3337/2814 5559/3335/2812 +f 1481/3328/2805 1483/3334/2811 1484/3340/2817 +f 1484/3340/2817 1488/3339/2816 5551/3309/2790 +f 1483/3334/2811 5560/3341/2818 1488/3339/2816 +f 1488/3339/2816 5561/3342/2819 5552/3311/2792 +f 1485/3329/2806 1481/3328/2805 1489/3343/2820 +f 4900/1403/1164 1485/3329/2806 4901/1411/1172 +f 1489/3343/2820 1484/3340/2817 5550/3308/2789 +f 4901/1411/1172 1489/3343/2820 7617/1412/1173 +f 1490/3352/2826 1494/3344/2821 1491/3346/2823 +f 1491/3346/2823 1495/3345/2822 5563/3348/2825 +f 1494/3344/2821 5554/3349/2796 1495/3345/2822 +f 1495/3345/2822 5553/3350/2804 5562/3347/2824 +f 1492/3358/2832 1490/3352/2826 1496/3353/2827 +f 1492/3358/2832 1496/3353/2827 5566/3355/2829 +f 1496/3353/2827 1491/3346/2823 5564/3356/2830 +f 1496/3353/2827 5564/3356/2830 5565/3354/2828 +f 1493/3360/2834 1490/3352/2826 1497/3359/2833 +f 4909/1430/1188 1493/3360/2834 4910/1438/1196 +f 1497/3359/2833 1492/3358/2832 5567/3361/2835 +f 4910/1438/1196 1497/3359/2833 7533/1439/1197 +f 1494/3344/2821 1490/3352/2826 1498/3362/2836 +f 5554/3349/2796 1494/3344/2821 5555/3363/2797 +f 1498/3362/2836 1493/3360/2834 4908/1429/1187 +f 5555/3363/2797 1498/3362/2836 7618/1433/1191 +f 1499/3369/2842 1503/3364/2837 1500/3366/2839 +f 1500/3366/2839 1504/3365/2838 5569/3368/2841 +f 1503/3364/2837 5560/3341/2818 1504/3365/2838 +f 5568/3367/2840 1504/3365/2838 7660/3338/2815 +f 1501/3371/2844 1499/3369/2842 1505/3370/2843 +f 5572/3377/2850 1501/3371/2844 5571/3372/2845 +f 1505/3370/2843 1500/3366/2839 5570/3373/2846 +f 5571/3372/2845 1505/3370/2843 7666/3374/2847 +f 1499/3369/2842 1501/3371/2844 1502/3376/2849 +f 1502/3376/2849 1506/3375/2848 5542/3272/2759 +f 1501/3371/2844 5572/3377/2850 1506/3375/2848 +f 1506/3375/2848 5573/3378/2851 5543/3278/2764 +f 1503/3364/2837 1499/3369/2842 1507/3379/2852 +f 5560/3341/2818 1503/3364/2837 5561/3342/2819 +f 1507/3379/2852 1502/3376/2849 5541/3271/2758 +f 5561/3342/2819 1507/3379/2852 7664/3275/2762 +f 1508/3388/2858 1512/3380/2853 1509/3382/2855 +f 1509/3382/2855 1513/3381/2854 5575/3384/2857 +f 1512/3380/2853 5545/3385/2770 1513/3381/2854 +f 1513/3381/2854 5544/3386/2780 5574/3383/2856 +f 1510/3390/2860 1508/3388/2858 1514/3389/2859 +f 5578/3396/2866 1510/3390/2860 5577/3391/2861 +f 1514/3389/2859 1509/3382/2855 5576/3392/2862 +f 5577/3391/2861 1514/3389/2859 7667/3393/2863 +f 1508/3388/2858 1510/3390/2860 1511/3395/2865 +f 1511/3395/2865 1515/3394/2864 5563/3348/2825 +f 1510/3390/2860 5578/3396/2866 1515/3394/2864 +f 5564/3356/2830 1515/3394/2864 7661/3357/2831 +f 1512/3380/2853 1508/3388/2858 1516/3398/2868 +f 5545/3385/2770 1512/3380/2853 5546/3399/2771 +f 1516/3398/2868 1511/3395/2865 5562/3347/2824 +f 5546/3399/2771 1516/3398/2868 7665/3351/2772 +f 1518/3403/2872 1517/3400/2869 1522/3402/2871 +f 4915/3412/1205 1518/3403/2872 4916/3404/1206 +f 1522/3402/2871 1521/3401/2870 5582/3406/2874 +f 1522/3402/2871 5582/3406/2874 4916/3404/1206 +f 1517/3400/2869 1518/3403/2872 1519/3409/2876 +f 1519/3409/2876 1523/3408/2875 5530/3411/2727 +f 1518/3403/2872 4915/3412/1205 1523/3408/2875 +f 1523/3408/2875 4914/3413/1221 5531/3410/2732 +f 1520/3416/2878 1517/3400/2869 1524/3415/2877 +f 5572/3377/2850 1520/3416/2878 5573/3378/2851 +f 1524/3415/2877 1519/3409/2876 5529/3417/2726 +f 5573/3378/2851 1524/3415/2877 7662/3279/2730 +f 1517/3400/2869 1520/3416/2878 1521/3401/2870 +f 1521/3401/2870 1525/3418/2879 5581/3405/2873 +f 1520/3416/2878 5572/3377/2850 1525/3418/2879 +f 1525/3418/2879 5571/3372/2845 5580/3419/2880 +f 1527/3423/2884 1526/3420/2881 1531/3422/2883 +f 5533/3429/2738 1527/3423/2884 5534/3424/2739 +f 1531/3422/2883 1530/3421/2882 5574/3383/2856 +f 5534/3424/2739 1531/3422/2883 7663/3387/2740 +f 1526/3420/2881 1527/3423/2884 1528/3426/2886 +f 1528/3426/2886 1532/3425/2885 4936/3428/1247 +f 1527/3423/2884 5533/3429/2738 1532/3425/2885 +f 1532/3425/2885 5532/3430/2748 4937/3427/1248 +f 1529/3433/2888 1526/3420/2881 1533/3432/2887 +f 5584/3438/2891 1529/3433/2888 5583/3434/2889 +f 1533/3432/2887 1528/3426/2886 4935/3435/1242 +f 1533/3432/2887 4935/3435/1242 5583/3434/2889 +f 1526/3420/2881 1529/3433/2888 1530/3421/2882 +f 1530/3421/2882 1534/3437/2890 5575/3384/2857 +f 1529/3433/2888 5584/3438/2891 1534/3437/2890 +f 1534/3437/2890 5585/3439/2892 5576/3392/2862 +f 1535/3448/2898 1539/3440/2893 1536/3442/2895 +f 1536/3442/2895 1540/3441/2894 5362/3444/2274 +f 1539/3440/2893 5587/3445/2896 1540/3441/2894 +f 1540/3441/2894 5588/3446/2897 5363/3443/2280 +f 1537/3450/2900 1535/3448/2898 1541/3449/2899 +f 5590/3458/2906 1537/3450/2900 5589/3451/2901 +f 1541/3449/2899 1536/3442/2895 5361/3452/2273 +f 5589/3451/2901 1541/3449/2899 7581/3453/2277 +f 1535/3448/2898 1537/3450/2900 1538/3455/2903 +f 1538/3455/2903 1542/3454/2902 5593/3457/2905 +f 1537/3450/2900 5590/3458/2906 1542/3454/2902 +f 1542/3454/2902 5591/3459/2907 5592/3456/2904 +f 1539/3440/2893 1535/3448/2898 1543/3461/2909 +f 5587/3445/2896 1539/3440/2893 5586/3462/2910 +f 1543/3461/2909 1538/3455/2903 5594/3463/2911 +f 5586/3462/2910 1543/3461/2909 7673/3464/2912 +f 1544/3470/2918 1548/3465/2913 1545/3467/2915 +f 1545/3467/2915 1549/3466/2914 5590/3458/2906 +f 1548/3465/2913 5596/3468/2916 1549/3466/2914 +f 1549/3466/2914 5597/3469/2917 5591/3459/2907 +f 1546/3472/2920 1544/3470/2918 1550/3471/2919 +f 5365/3478/2295 1546/3472/2920 5366/3473/2296 +f 1550/3471/2919 1545/3467/2915 5589/3451/2901 +f 5366/3473/2296 1550/3471/2919 7581/3453/2277 +f 1544/3470/2918 1546/3472/2920 1547/3475/2922 +f 1547/3475/2922 1551/3474/2921 5599/3477/2924 +f 1546/3472/2920 5365/3478/2295 1551/3474/2921 +f 1551/3474/2921 5364/3479/2292 5598/3476/2923 +f 1548/3465/2913 1544/3470/2918 1552/3480/2925 +f 5596/3468/2916 1548/3465/2913 5595/3481/2926 +f 1552/3480/2925 1547/3475/2922 5600/3482/2927 +f 5595/3481/2926 1552/3480/2925 7674/3483/2928 +f 1553/3491/2934 1557/3484/2929 1554/3486/2931 +f 1554/3486/2931 1558/3485/2930 5296/3488/2107 +f 1557/3484/2929 5602/3489/2932 1558/3485/2930 +f 1558/3485/2930 5603/3490/2933 5297/3487/2114 +f 1555/3493/2936 1553/3491/2934 1559/3492/2935 +f 5587/3445/2896 1555/3493/2936 5588/3446/2897 +f 1559/3492/2935 1554/3486/2931 5295/3494/2106 +f 5588/3446/2897 1559/3492/2935 7582/3447/2110 +f 1553/3491/2934 1555/3493/2936 1556/3496/2938 +f 5605/3500/2942 1556/3496/2938 5604/3497/2939 +f 1555/3493/2936 5587/3445/2896 1560/3495/2937 +f 5604/3497/2939 1560/3495/2937 7673/3464/2912 +f 1557/3484/2929 1553/3491/2934 1561/3498/2940 +f 5602/3489/2932 1557/3484/2929 5601/3499/2941 +f 1556/3496/2938 5605/3500/2942 1561/3498/2940 +f 1561/3498/2940 5606/3501/2943 5601/3499/2941 +f 1562/3511/2950 1566/3503/2945 1563/3505/2947 +f 1563/3505/2947 1567/3504/2946 5599/3507/2924 +f 1567/3504/2946 1566/3503/2945 5609/3509/2949 +f 5600/3506/2927 1567/3504/2946 7674/3510/2928 +f 1564/3513/2952 1562/3511/2950 1568/3512/2951 +f 5299/3521/2130 1564/3513/2952 5300/3514/2131 +f 1568/3512/2951 1563/3505/2947 5598/3515/2923 +f 5300/3514/2131 1568/3512/2951 7583/3516/2132 +f 1562/3511/2950 1564/3513/2952 1565/3518/2954 +f 1565/3518/2954 1569/3517/2953 5611/3520/2956 +f 1564/3513/2952 5299/3521/2130 1569/3517/2953 +f 1569/3517/2953 5298/3522/2125 5610/3519/2955 +f 1566/3503/2945 1562/3511/2950 1570/3524/2957 +f 1566/3503/2945 1570/3524/2957 5608/3508/2948 +f 1570/3524/2957 1565/3518/2954 5612/3526/2959 +f 1570/3524/2957 5612/3526/2959 5607/3525/2958 +f 1572/3531/2964 1571/3528/2961 1576/3530/2963 +f 5284/2433/2081 1572/3531/2964 5285/2434/2082 +f 1576/3530/2963 1575/3529/2962 5615/3533/2966 +f 5285/2434/2082 1576/3530/2963 7586/2394/2042 +f 1571/3528/2961 1572/3531/2964 1573/3535/2968 +f 5602/3489/2932 1573/3535/2968 5603/3490/2933 +f 1572/3531/2964 5284/2433/2081 1577/3534/2967 +f 5603/3490/2933 1577/3534/2967 7584/2430/2078 +f 1574/3537/2970 1571/3528/2961 1578/3536/2969 +f 5617/3541/2974 1574/3537/2970 5616/3538/2971 +f 1573/3535/2968 5602/3489/2932 1578/3536/2969 +f 5616/3538/2971 1578/3536/2969 7671/3502/2944 +f 1571/3528/2961 1574/3537/2970 1575/3529/2962 +f 1575/3529/2962 1579/3539/2972 5614/3532/2965 +f 1574/3537/2970 5617/3541/2974 1579/3539/2972 +f 1579/3539/2972 5618/3542/2975 5613/3540/2973 +f 1581/3549/2982 1580/3544/2977 1585/3546/2979 +f 1581/3549/2982 1585/3546/2979 5611/3520/2956 +f 1585/3546/2979 1584/3545/2978 5621/3548/2981 +f 5612/3526/2959 1585/3546/2979 7672/3527/2960 +f 1580/3544/2977 1581/3549/2982 1582/3551/2984 +f 1582/3551/2984 1586/3550/2983 5287/3553/2094 +f 1586/3550/2983 1581/3549/2982 5610/3519/2955 +f 5288/3552/2098 1586/3550/2983 7585/3523/2099 +f 1583/3555/2986 1580/3544/2977 1587/3554/2985 +f 5623/3561/2990 1583/3555/2986 5622/3556/2987 +f 1587/3554/2985 1582/3551/2984 5286/3557/2093 +f 5622/3556/2987 1587/3554/2985 7587/3558/2067 +f 1580/3544/2977 1583/3555/2986 1584/3545/2978 +f 1584/3545/2978 1588/3559/2988 5620/3547/2980 +f 1583/3555/2986 5623/3561/2990 1588/3559/2988 +f 1588/3559/2988 5624/3562/2991 5619/3560/2989 +f 1590/3567/2996 1589/3564/2993 1594/3566/2995 +f 5269/3576/2047 1590/3567/2996 5270/3568/2048 +f 1594/3566/2995 1593/3565/2994 5627/3570/2998 +f 5270/3568/2048 1594/3566/2995 7588/3571/2049 +f 1589/3564/2993 1590/3567/2996 1591/3573/3000 +f 1591/3573/3000 1595/3572/2999 5614/3575/2965 +f 1590/3567/2996 5269/3576/2047 1595/3572/2999 +f 1595/3572/2999 5268/3577/2039 5615/3574/2966 +f 1592/3580/3002 1589/3564/2993 1596/3579/3001 +f 5629/3586/3006 1592/3580/3002 5628/3581/3003 +f 1596/3579/3001 1591/3573/3000 5613/3582/2973 +f 5628/3581/3003 1596/3579/3001 7669/3583/2976 +f 1589/3564/2993 1592/3580/3002 1593/3565/2994 +f 1593/3565/2994 1597/3584/3004 5626/3569/2997 +f 1592/3580/3002 5629/3586/3006 1597/3584/3004 +f 1597/3584/3004 5630/3587/3007 5625/3585/3005 +f 1599/3592/3012 1598/3589/3009 1603/3591/3011 +f 5623/3561/2990 1599/3592/3012 5624/3562/2991 +f 1603/3591/3011 1602/3590/3010 5633/3594/3014 +f 5624/3562/2991 1603/3591/3011 7670/3563/2992 +f 1598/3589/3009 1599/3592/3012 1600/3596/3016 +f 1600/3596/3016 1604/3595/3015 5275/3598/2062 +f 1599/3592/3012 5623/3561/2990 1604/3595/3015 +f 1604/3595/3015 5622/3556/2987 5276/3597/2066 +f 1601/3600/3018 1598/3589/3009 1605/3599/3017 +f 5626/3569/2997 1601/3600/3018 5627/3570/2998 +f 1605/3599/3017 1600/3596/3016 5274/3601/2061 +f 5627/3570/2998 1605/3599/3017 7588/3571/2049 +f 1598/3589/3009 1601/3600/3018 1602/3590/3010 +f 1602/3590/3010 1606/3602/3019 5632/3593/3013 +f 1601/3600/3018 5626/3569/2997 1606/3602/3019 +f 1606/3602/3019 5625/3585/3005 5631/3603/3020 +f 1607/3611/3026 1611/3604/3021 1608/3606/3023 +f 5629/3615/3006 1608/3606/3023 5630/3607/3007 +f 1611/3604/3021 5635/3608/3024 1612/3605/3022 +f 5630/3607/3007 1612/3605/3022 7668/3610/3008 +f 1609/3613/3028 1607/3611/3026 1613/3612/3027 +f 5638/3622/3034 1609/3613/3028 5637/3614/3029 +f 1608/3606/3023 5629/3615/3006 1613/3612/3027 +f 1613/3612/3027 5628/3616/3003 5637/3614/3029 +f 1607/3611/3026 1609/3613/3028 1610/3619/3031 +f 1610/3619/3031 1614/3618/3030 5641/3621/3033 +f 1609/3613/3028 5638/3622/3034 1614/3618/3030 +f 1614/3618/3030 5639/3623/3035 5640/3620/3032 +f 1611/3604/3021 1607/3611/3026 1615/3625/3037 +f 5635/3608/3024 1611/3604/3021 5634/3626/3038 +f 1615/3625/3037 1610/3619/3031 5642/3627/3039 +f 5634/3626/3038 1615/3625/3037 7682/3628/3040 +f 1616/3637/3049 1620/3629/3041 1617/3631/3043 +f 1617/3631/3043 1621/3630/3042 5647/3633/3045 +f 1620/3629/3041 5644/3634/3046 1621/3630/3042 +f 1621/3630/3042 5645/3635/3047 5646/3632/3044 +f 1618/3643/3052 1616/3637/3049 1622/3638/3050 +f 1618/3643/3052 1622/3638/3050 5632/3640/3013 +f 1622/3638/3050 1617/3631/3043 5648/3641/3051 +f 1622/3638/3050 5648/3641/3051 5633/3639/3014 +f 1616/3637/3049 1618/3643/3052 1619/3645/3054 +f 1619/3645/3054 1623/3644/3053 5635/3647/3024 +f 1623/3644/3053 1618/3643/3052 5631/3648/3020 +f 5636/3646/3025 1623/3644/3053 7668/3649/3008 +f 1620/3629/3041 1616/3637/3049 1624/3650/3055 +f 5644/3634/3046 1620/3629/3041 5643/3651/3056 +f 1624/3650/3055 1619/3645/3054 5634/3652/3038 +f 5643/3651/3056 1624/3650/3055 7682/3653/3040 +f 1626/3657/3060 1625/3654/3057 1630/3656/3059 +f 5617/3663/2974 1626/3657/3060 5618/3658/2975 +f 1629/3655/3058 5638/3622/3034 1630/3656/3059 +f 5618/3658/2975 1630/3656/3059 7669/3617/2976 +f 1625/3654/3057 1626/3657/3060 1627/3660/3062 +f 1627/3660/3062 1631/3659/3061 5650/3662/3064 +f 1626/3657/3060 5617/3663/2974 1631/3659/3061 +f 1631/3659/3061 5616/3664/2971 5649/3661/3063 +f 1628/3671/3070 1625/3654/3057 1632/3666/3065 +f 1628/3671/3070 1632/3666/3065 5653/3668/3067 +f 1632/3666/3065 1627/3660/3062 5651/3669/3068 +f 5652/3667/3066 1632/3666/3065 7678/3670/3069 +f 1629/3655/3058 1625/3654/3057 1633/3672/3071 +f 5638/3622/3034 1629/3655/3058 5639/3623/3035 +f 1633/3672/3071 1628/3671/3070 5654/3673/3072 +f 5639/3623/3035 1633/3672/3071 7680/3624/3036 +f 1635/3677/3076 1634/3674/3073 1639/3676/3075 +f 5659/3686/3083 1635/3677/3076 5658/3678/3077 +f 1638/3675/3074 5656/3679/3078 1639/3676/3075 +f 5658/3678/3077 1639/3676/3075 7679/3681/3080 +f 1634/3674/3073 1635/3677/3076 1636/3683/3082 +f 1636/3683/3082 1640/3682/3081 5620/3685/2980 +f 1635/3677/3076 5659/3686/3083 1640/3682/3081 +f 1640/3682/3081 5660/3687/3084 5621/3684/2981 +f 1637/3691/3086 1634/3674/3073 1641/3689/3085 +f 1637/3691/3086 1641/3689/3085 5647/3633/3045 +f 1641/3689/3085 1636/3683/3082 5619/3690/2989 +f 5648/3641/3051 1641/3689/3085 7670/3642/2992 +f 1638/3675/3074 1634/3674/3073 1642/3692/3087 +f 5656/3679/3078 1638/3675/3074 5655/3693/3088 +f 1642/3692/3087 1637/3691/3086 5646/3632/3044 +f 5655/3693/3088 1642/3692/3087 7681/3636/3048 +f 1644/3697/3092 1643/3694/3089 1648/3696/3091 +f 5605/3703/2942 1644/3697/3092 5606/3698/2943 +f 1648/3696/3091 1647/3695/3090 5649/3661/3063 +f 5606/3698/2943 1648/3696/3091 7671/3665/2944 +f 1643/3694/3089 1644/3697/3092 1645/3700/3094 +f 1645/3700/3094 1649/3699/3093 5662/3702/3096 +f 1644/3697/3092 5605/3703/2942 1649/3699/3093 +f 1649/3699/3093 5604/3704/2939 5661/3701/3095 +f 1646/3707/3098 1643/3694/3089 1650/3706/3097 +f 5665/3712/3103 1646/3707/3098 5664/3708/3099 +f 1650/3706/3097 1645/3700/3094 5663/3709/3100 +f 5664/3708/3099 1650/3706/3097 7676/3710/3101 +f 1643/3694/3089 1646/3707/3098 1647/3695/3090 +f 1647/3695/3090 1651/3711/3102 5650/3662/3064 +f 1646/3707/3098 5665/3712/3103 1651/3711/3102 +f 1651/3711/3102 5666/3713/3104 5651/3669/3068 +f 1653/3717/3108 1652/3714/3105 1657/3716/3107 +f 5671/3726/3115 1653/3717/3108 5670/3718/3109 +f 1657/3716/3107 1656/3715/3106 5669/3720/3111 +f 5670/3718/3109 1657/3716/3107 7677/3721/3112 +f 1652/3714/3105 1653/3717/3108 1654/3723/3114 +f 1654/3723/3114 1658/3722/3113 5608/3725/2948 +f 1653/3717/3108 5671/3726/3115 1658/3722/3113 +f 1658/3722/3113 5672/3727/3116 5609/3724/2949 +f 1655/3730/3118 1652/3714/3105 1659/3729/3117 +f 5659/3736/3083 1655/3730/3118 5660/3731/3084 +f 1659/3729/3117 1654/3723/3114 5607/3732/2958 +f 5660/3731/3084 1659/3729/3117 7672/3733/2960 +f 1652/3714/3105 1655/3730/3118 1656/3715/3106 +f 1656/3715/3106 1660/3734/3119 5668/3719/3110 +f 1655/3730/3118 5659/3736/3083 1660/3734/3119 +f 1660/3734/3119 5658/3737/3077 5667/3735/3120 +f 1661/3747/3124 1665/3739/3121 1662/3741/3123 +f 1662/3741/3123 1666/3740/3122 5593/3743/2905 +f 1665/3739/3121 5662/3744/3096 1666/3740/3122 +f 1666/3740/3122 5661/3745/3095 5594/3742/2911 +f 1663/3749/3126 1661/3747/3124 1667/3748/3125 +f 5674/3757/3132 1663/3749/3126 5673/3750/3127 +f 1667/3748/3125 1662/3741/3123 5592/3751/2904 +f 5673/3750/3127 1667/3748/3125 7523/3752/2908 +f 1661/3747/3124 1663/3749/3126 1664/3754/3129 +f 1664/3754/3129 1668/3753/3128 5677/3756/3131 +f 1663/3749/3126 5674/3757/3132 1668/3753/3128 +f 1668/3753/3128 5675/3758/3133 5676/3755/3130 +f 1665/3739/3121 1661/3747/3124 1669/3760/3135 +f 5662/3744/3096 1665/3739/3121 5663/3761/3100 +f 1669/3760/3135 1664/3754/3129 5678/3762/3136 +f 5663/3761/3100 1669/3760/3135 7676/3763/3101 +f 1670/3772/3142 1674/3764/3137 1671/3766/3139 +f 1671/3766/3139 1675/3765/3138 5674/3768/3132 +f 1674/3764/3137 5680/3769/3140 1675/3765/3138 +f 1675/3765/3138 5681/3770/3141 5675/3767/3133 +f 1672/3774/3144 1670/3772/3142 1676/3773/3143 +f 5596/3468/2916 1672/3774/3144 5597/3469/2917 +f 1676/3773/3143 1671/3766/3139 5673/3775/3127 +f 5597/3469/2917 1676/3773/3143 7523/3460/2908 +f 1670/3772/3142 1672/3774/3144 1673/3777/3146 +f 1673/3777/3146 1677/3776/3145 5671/3779/3115 +f 1672/3774/3144 5596/3468/2916 1677/3776/3145 +f 1677/3776/3145 5595/3481/2926 5672/3778/3116 +f 1674/3764/3137 1670/3772/3142 1678/3780/3147 +f 5680/3769/3140 1674/3764/3137 5679/3781/3148 +f 1678/3780/3147 1673/3777/3146 5670/3782/3109 +f 5679/3781/3148 1678/3780/3147 7677/3783/3112 +f 1680/3787/3152 1679/3784/3149 1684/3786/3151 +f 5683/3796/3156 1680/3787/3152 5682/3788/3153 +f 1684/3786/3151 1683/3785/3150 5676/3790/3130 +f 5682/3788/3153 1684/3786/3151 7675/3791/3134 +f 1679/3784/3149 1680/3787/3152 1681/3793/3155 +f 1681/3793/3155 1685/3792/3154 5641/3795/3033 +f 1680/3787/3152 5683/3796/3156 1685/3792/3154 +f 1685/3792/3154 5684/3797/3157 5642/3794/3039 +f 1682/3800/3159 1679/3784/3149 1686/3799/3158 +f 5686/3806/3162 1682/3800/3159 5685/3801/3160 +f 1686/3799/3158 1681/3793/3155 5640/3802/3032 +f 5685/3801/3160 1686/3799/3158 7680/3803/3036 +f 1679/3784/3149 1682/3800/3159 1683/3785/3150 +f 1683/3785/3150 1687/3804/3161 5677/3789/3131 +f 1682/3800/3159 5686/3806/3162 1687/3804/3161 +f 1687/3804/3161 5687/3807/3163 5678/3805/3136 +f 1689/3812/3167 1688/3809/3164 1693/3811/3166 +f 5644/3819/3046 1689/3812/3167 5645/3813/3047 +f 1693/3811/3166 1692/3810/3165 5690/3815/3169 +f 5645/3813/3047 1693/3811/3166 7681/3816/3048 +f 1688/3809/3164 1689/3812/3167 1690/3818/3171 +f 1690/3818/3171 1694/3817/3170 5683/3796/3156 +f 1689/3812/3167 5644/3819/3046 1694/3817/3170 +f 1694/3817/3170 5643/3820/3056 5684/3797/3157 +f 1691/3822/3173 1688/3809/3164 1695/3821/3172 +f 5680/3826/3140 1691/3822/3173 5681/3823/3141 +f 1695/3821/3172 1690/3818/3171 5682/3788/3153 +f 5681/3823/3141 1695/3821/3172 7675/3791/3134 +f 1688/3809/3164 1691/3822/3173 1692/3810/3165 +f 1692/3810/3165 1696/3824/3174 5689/3814/3168 +f 1691/3822/3173 5680/3826/3140 1696/3824/3174 +f 1696/3824/3174 5679/3827/3148 5688/3825/3175 +f 1697/3838/3181 1700/3829/3176 1698/3831/3178 +f 5686/3806/3162 1698/3831/3178 5687/3807/3163 +f 1700/3829/3176 5665/3832/3103 1701/3830/3177 +f 1701/3830/3177 5664/3833/3099 5687/3807/3163 +f 1697/3838/3181 1698/3831/3178 1699/3835/3180 +f 1699/3835/3180 1702/3834/3179 5653/3837/3067 +f 1698/3831/3178 5686/3806/3162 1702/3834/3179 +f 1702/3834/3179 5685/3801/3160 5654/3836/3072 +f 1700/3829/3176 1697/3838/3181 1703/3839/3182 +f 5665/3832/3103 1700/3829/3176 5666/3840/3104 +f 1703/3839/3182 1699/3835/3180 5652/3841/3066 +f 5666/3840/3104 1703/3839/3182 7678/3842/3069 +f 1705/3846/3186 1704/3843/3183 1708/3845/3185 +f 5656/3855/3078 1705/3846/3186 5657/3847/3079 +f 1708/3845/3185 1707/3844/3184 5667/3849/3120 +f 5657/3847/3079 1708/3845/3185 7679/3850/3080 +f 1704/3843/3183 1705/3846/3186 1706/3852/3188 +f 1706/3852/3188 1709/3851/3187 5689/3854/3168 +f 1705/3846/3186 5656/3855/3078 1709/3851/3187 +f 1709/3851/3187 5655/3856/3088 5690/3853/3169 +f 1704/3843/3183 1706/3852/3188 1707/3844/3184 +f 1707/3844/3184 1710/3858/3189 5668/3848/3110 +f 1710/3858/3189 1706/3852/3188 5688/3860/3175 +f 1710/3858/3189 5688/3860/3175 5669/3859/3111 +f 1712/3865/3193 1711/3862/3190 1716/3864/3192 +f 5260/3874/2019 1712/3865/3193 5261/3866/2020 +f 1716/3864/3192 1715/3863/3191 5693/3868/3195 +f 1716/3864/3192 5693/3868/3195 5261/3866/2020 +f 1711/3862/3190 1712/3865/3193 1713/3871/3197 +f 1713/3871/3197 1717/3870/3196 5695/3873/3199 +f 1712/3865/3193 5260/3874/2019 1717/3870/3196 +f 1717/3870/3196 5259/3875/2016 5694/3872/3198 +f 1714/3878/3201 1711/3862/3190 1718/3877/3200 +f 5698/3884/3207 1714/3878/3201 5697/3879/3202 +f 1718/3877/3200 1713/3871/3197 5696/3880/3203 +f 5697/3879/3202 1718/3877/3200 7685/3881/3204 +f 1711/3862/3190 1714/3878/3201 1715/3863/3191 +f 1715/3863/3191 1719/3882/3205 5692/3867/3194 +f 1714/3878/3201 5698/3884/3207 1719/3882/3205 +f 1719/3882/3205 5699/3885/3208 5691/3883/3206 +f 1721/3890/3213 1720/3887/3210 1725/3889/3212 +f 5704/3897/3220 1721/3890/3213 5703/3891/3214 +f 1725/3889/3212 1724/3888/3211 5702/3893/3216 +f 5703/3891/3214 1725/3889/3212 7686/3894/3217 +f 1720/3887/3210 1721/3890/3213 1722/3896/3219 +f 1722/3896/3219 1726/3895/3218 5263/2375/2027 +f 1721/3890/3213 5704/3897/3220 1726/3895/3218 +f 1726/3895/3218 5705/3898/3221 5264/2382/2031 +f 1723/3900/3223 1720/3887/3210 1727/3899/3222 +f 5707/3904/3227 1723/3900/3223 5706/3901/3224 +f 1727/3899/3222 1722/3896/3219 5262/2374/2026 +f 1727/3899/3222 5262/2374/2026 5706/3901/3224 +f 1720/3887/3210 1723/3900/3223 1724/3888/3211 +f 1724/3888/3211 1728/3902/3225 5701/3892/3215 +f 1723/3900/3223 5707/3904/3227 1728/3902/3225 +f 1728/3902/3225 5708/3905/3228 5700/3903/3226 +f 1729/3915/3235 1733/3907/3230 1730/3909/3232 +f 1730/3909/3232 1734/3908/3231 5242/3911/1976 +f 1733/3907/3230 5710/3912/3233 1734/3908/3231 +f 1734/3908/3231 5711/3913/3234 5243/3910/1977 +f 1731/3917/3237 1729/3915/3235 1735/3916/3236 +f 5692/3867/3194 1731/3917/3237 5693/3868/3195 +f 1735/3916/3236 1730/3909/3232 5241/3918/1988 +f 5693/3868/3195 1735/3916/3236 7634/3869/1991 +f 1732/3920/3239 1729/3915/3235 1736/3919/3238 +f 5713/3924/3243 1732/3920/3239 5712/3921/3240 +f 1736/3919/3238 1731/3917/3237 5691/3883/3206 +f 5712/3921/3240 1736/3919/3238 7683/3886/3209 +f 1729/3915/3235 1732/3920/3239 1733/3907/3230 +f 1733/3907/3230 1737/3922/3241 5710/3912/3233 +f 1732/3920/3239 5713/3924/3243 1737/3922/3241 +f 1737/3922/3241 5714/3925/3244 5709/3923/3242 +f 1739/3930/3249 1738/3927/3246 1743/3929/3248 +f 5707/3904/3227 1739/3930/3249 5708/3905/3228 +f 1743/3929/3248 1742/3928/3247 5717/3932/3251 +f 5708/3905/3228 1743/3929/3248 7684/3906/3229 +f 1740/3934/3253 1738/3927/3246 1744/3933/3252 +f 5254/3940/2005 1740/3934/3253 5255/3935/2006 +f 1744/3933/3252 1739/3930/3249 5706/3901/3224 +f 5255/3935/2006 1744/3933/3252 7635/2378/2007 +f 1738/3927/3246 1740/3934/3253 1741/3937/3255 +f 1741/3937/3255 1745/3936/3254 5719/3939/3257 +f 1740/3934/3253 5254/3940/2005 1745/3936/3254 +f 1745/3936/3254 5253/3941/2002 5718/3938/3256 +f 1738/3927/3246 1741/3937/3255 1742/3928/3247 +f 1742/3928/3247 1746/3943/3258 5716/3931/3250 +f 1741/3937/3255 5719/3939/3257 1746/3943/3258 +f 1746/3943/3258 5720/3945/3260 5715/3944/3259 +f 1747/3952/3267 1751/3947/3262 1748/3949/3264 +f 5230/2279/1943 1748/3949/3264 5231/2280/1944 +f 1752/3948/3263 1751/3947/3262 5723/3951/3266 +f 5231/2280/1944 1752/3948/3263 7561/1886/1612 +f 1749/3954/3269 1747/3952/3267 1753/3953/3268 +f 5710/3960/3233 1749/3954/3269 5711/3955/3234 +f 1748/3949/3264 5230/2279/1943 1753/3953/3268 +f 5711/3955/3234 1753/3953/3268 7563/2295/1955 +f 1747/3952/3267 1749/3954/3269 1750/3957/3271 +f 1750/3957/3271 1754/3956/3270 5725/3959/3273 +f 1749/3954/3269 5710/3960/3233 1754/3956/3270 +f 5724/3958/3272 1754/3956/3270 7705/3962/3245 +f 1751/3947/3262 1747/3952/3267 1755/3963/3274 +f 5722/3950/3265 1751/3947/3262 5721/3964/3275 +f 1755/3963/3274 1750/3957/3271 5726/3965/3276 +f 5721/3964/3275 1755/3963/3274 7707/3966/3277 +f 1756/3972/3283 1760/3967/3278 1757/3969/3280 +f 1757/3969/3280 1761/3968/3279 5719/3939/3257 +f 1760/3967/3278 5728/3970/3281 1761/3968/3279 +f 5720/3945/3260 1761/3968/3279 7706/3946/3261 +f 1758/3976/3285 1756/3972/3283 1762/3973/3284 +f 1758/3976/3285 1762/3973/3284 5239/3975/1967 +f 1762/3973/3284 1757/3969/3280 5718/3938/3256 +f 5240/3974/1970 1762/3973/3284 7564/3942/1971 +f 1756/3972/3283 1758/3976/3285 1759/3978/3287 +f 5731/3984/3291 1759/3978/3287 5730/3979/3288 +f 1763/3977/3286 1758/3976/3285 5238/3980/1966 +f 5730/3979/3288 1763/3977/3286 7562/3981/1642 +f 1760/3967/3278 1756/3972/3283 1764/3982/3289 +f 5728/3970/3281 1760/3967/3278 5727/3983/3290 +f 1764/3982/3289 1759/3978/3287 5732/3985/3292 +f 5727/3983/3290 1764/3982/3289 7708/3986/3293 +f 1766/3990/3297 1765/3987/3294 1770/3989/3296 +f 5212/3995/1910 1766/3990/3297 5213/3991/1911 +f 1770/3989/3296 1769/3988/3295 5694/3872/3198 +f 5213/3991/1911 1770/3989/3296 7632/3876/1912 +f 1767/3993/3299 1765/3987/3294 1771/3992/3298 +f 5734/4002/3305 1767/3993/3299 5733/3994/3300 +f 1766/3990/3297 5212/3995/1910 1771/3992/3298 +f 5733/3994/3300 1771/3992/3298 7565/3997/1876 +f 1765/3987/3294 1767/3993/3299 1768/3999/3302 +f 1768/3999/3302 1772/3998/3301 5737/4001/3304 +f 1767/3993/3299 5734/4002/3305 1772/3998/3301 +f 1772/3998/3301 5735/4003/3306 5736/4000/3303 +f 1769/3988/3295 1765/3987/3294 1773/4005/3308 +f 1769/3988/3295 1773/4005/3308 5695/3873/3199 +f 1773/4005/3308 1768/3999/3302 5738/4006/3309 +f 1773/4005/3308 5738/4006/3309 5696/3880/3203 +f 1774/4015/3318 1778/4007/3310 1775/4009/3312 +f 1775/4009/3312 1779/4008/3311 5743/4011/3314 +f 1778/4007/3310 5740/4012/3315 1779/4008/3311 +f 1779/4008/3311 5741/4013/3316 5742/4010/3313 +f 1776/4018/3321 1774/4015/3318 1780/4016/3319 +f 1776/4018/3321 1780/4016/3319 5227/2267/1931 +f 1780/4016/3319 1775/4009/3312 5744/4017/3320 +f 5228/2272/1936 1780/4016/3319 7566/2231/1897 +f 1777/4020/3323 1774/4015/3318 1781/4019/3322 +f 5704/3897/3220 1777/4020/3323 5705/3898/3221 +f 1781/4019/3322 1776/4018/3321 5226/2266/1930 +f 5705/3898/3221 1781/4019/3322 7633/2270/1934 +f 1778/4007/3310 1774/4015/3318 1782/4021/3324 +f 5740/4012/3315 1778/4007/3310 5739/4022/3325 +f 1777/4020/3323 5704/3897/3220 1782/4021/3324 +f 1782/4021/3324 5703/3891/3214 5739/4022/3325 +f 1784/4026/3329 1783/4023/3326 1788/4025/3328 +f 5200/4032/1874 1784/4026/3329 5201/4027/1875 +f 1788/4025/3328 1787/4024/3327 5733/3994/3300 +f 1788/4025/3328 5733/3994/3300 5201/4027/1875 +f 1783/4023/3326 1784/4026/3329 1785/4029/3331 +f 1785/4029/3331 1789/4028/3330 5746/4031/3333 +f 1784/4026/3329 5200/4032/1874 1789/4028/3330 +f 1789/4028/3330 5199/4033/1884 5745/4030/3332 +f 1786/4036/3335 1783/4023/3326 1790/4035/3334 +f 5749/4041/3340 1786/4036/3335 5748/4037/3336 +f 1790/4035/3334 1785/4029/3331 5747/4038/3337 +f 5748/4037/3336 1790/4035/3334 7701/4039/3338 +f 1783/4023/3326 1786/4036/3335 1787/4024/3327 +f 5734/4002/3305 1787/4024/3327 5735/4003/3306 +f 1786/4036/3335 5749/4041/3340 1791/4040/3339 +f 5735/4003/3306 1791/4040/3339 7703/4004/3307 +f 1793/4046/3345 1792/4043/3342 1797/4045/3344 +f 5755/4055/3352 1793/4046/3345 5754/4047/3346 +f 1797/4045/3344 1796/4044/3343 5753/4049/3348 +f 5754/4047/3346 1797/4045/3344 7702/4050/3349 +f 1792/4043/3342 1793/4046/3345 1794/4052/3351 +f 1794/4052/3351 1798/4051/3350 5209/4054/1899 +f 1793/4046/3345 5755/4055/3352 1798/4051/3350 +f 1798/4051/3350 5756/4056/3353 5210/4053/1900 +f 1795/4058/3355 1792/4043/3342 1799/4057/3354 +f 5743/4064/3314 1795/4058/3355 5744/4059/3320 +f 1799/4057/3354 1794/4052/3351 5208/4060/1895 +f 1799/4057/3354 5208/4060/1895 5744/4059/3320 +f 1792/4043/3342 1795/4058/3355 1796/4044/3343 +f 1796/4044/3343 1800/4062/3356 5752/4048/3347 +f 1800/4062/3356 1795/4058/3355 5742/4065/3313 +f 5751/4063/3357 1800/4062/3356 7704/4066/3317 +f 1802/4070/3361 1801/4067/3358 1806/4069/3360 +f 5188/2161/1842 1802/4070/3361 5189/2162/1843 +f 1806/4069/3360 1805/4068/3359 5745/4072/3332 +f 5189/2162/1843 1806/4069/3360 7567/2163/1844 +f 1801/4067/3358 1802/4070/3361 1803/4074/3363 +f 1803/4074/3363 1807/4073/3362 5758/4076/3365 +f 1802/4070/3361 5188/2161/1842 1807/4073/3362 +f 1807/4073/3362 5187/2175/1852 5757/4075/3364 +f 1804/4078/3367 1801/4067/3358 1808/4077/3366 +f 5761/4084/3372 1804/4078/3367 5760/4079/3368 +f 1808/4077/3366 1803/4074/3363 5759/4080/3369 +f 5760/4079/3368 1808/4077/3366 7699/4081/3370 +f 1801/4067/3358 1804/4078/3367 1805/4068/3359 +f 1805/4068/3359 1809/4082/3371 5746/4071/3333 +f 1804/4078/3367 5761/4084/3372 1809/4082/3371 +f 1809/4082/3371 5762/4085/3373 5747/4083/3337 +f 1811/4090/3377 1810/4087/3374 1815/4089/3376 +f 5767/4097/3384 1811/4090/3377 5766/4091/3378 +f 1815/4089/3376 1814/4088/3375 5765/4093/3380 +f 5766/4091/3378 1815/4089/3376 7700/4094/3381 +f 1810/4087/3374 1811/4090/3377 1812/4096/3383 +f 1812/4096/3383 1816/4095/3382 5197/2186/1863 +f 1811/4090/3377 5767/4097/3384 1816/4095/3382 +f 1816/4095/3382 5768/4098/3385 5198/2191/1868 +f 1813/4100/3387 1810/4087/3374 1817/4099/3386 +f 5755/4055/3352 1813/4100/3387 5756/4056/3353 +f 1817/4099/3386 1812/4096/3383 5196/2185/1862 +f 5756/4056/3353 1817/4099/3386 7568/2189/1866 +f 1810/4087/3374 1813/4100/3387 1814/4088/3375 +f 1814/4088/3375 1818/4101/3388 5764/4092/3379 +f 1813/4100/3387 5755/4055/3352 1818/4101/3388 +f 1818/4101/3388 5754/4047/3346 5763/4102/3389 +f 1820/4106/3393 1819/4103/3390 1824/4105/3392 +f 5176/2129/1810 1820/4106/3393 5177/2130/1811 +f 1824/4105/3392 1823/4104/3391 5757/4075/3364 +f 5177/2130/1811 1824/4105/3392 7569/2131/1812 +f 1819/4103/3390 1820/4106/3393 1821/4108/3395 +f 1821/4108/3395 1825/4107/3394 5770/4110/3397 +f 1820/4106/3393 5176/2129/1810 1825/4107/3394 +f 1825/4107/3394 5175/2139/1820 5769/4109/3396 +f 1822/4112/3399 1819/4103/3390 1826/4111/3398 +f 5773/4117/3404 1822/4112/3399 5772/4113/3400 +f 1826/4111/3398 1821/4108/3395 5771/4114/3401 +f 5772/4113/3400 1826/4111/3398 7697/4115/3402 +f 1819/4103/3390 1822/4112/3399 1823/4104/3391 +f 1823/4104/3391 1827/4116/3403 5758/4076/3365 +f 1822/4112/3399 5773/4117/3404 1827/4116/3403 +f 1827/4116/3403 5774/4118/3405 5759/4080/3369 +f 1829/4122/3409 1828/4119/3406 1833/4121/3408 +f 5779/4129/3416 1829/4122/3409 5778/4123/3410 +f 1833/4121/3408 1832/4120/3407 5777/4125/3412 +f 5778/4123/3410 1833/4121/3408 7698/4126/3413 +f 1828/4119/3406 1829/4122/3409 1830/4128/3415 +f 1830/4128/3415 1834/4127/3414 5185/2150/1831 +f 1829/4122/3409 5779/4129/3416 1834/4127/3414 +f 1834/4127/3414 5780/4130/3417 5186/2155/1836 +f 1831/4132/3419 1828/4119/3406 1835/4131/3418 +f 5767/4097/3384 1831/4132/3419 5768/4098/3385 +f 1835/4131/3418 1830/4128/3415 5184/2149/1830 +f 5768/4098/3385 1835/4131/3418 7570/2153/1834 +f 1828/4119/3406 1831/4132/3419 1832/4120/3407 +f 1832/4120/3407 1836/4133/3420 5776/4124/3411 +f 1831/4132/3419 5767/4097/3384 1836/4133/3420 +f 1836/4133/3420 5766/4091/3378 5775/4134/3421 +f 1838/4138/3425 1837/4135/3422 1842/4137/3424 +f 5164/2093/1778 1838/4138/3425 5165/2094/1779 +f 1842/4137/3424 1841/4136/3423 5769/4109/3396 +f 5165/2094/1779 1842/4137/3424 7571/2095/1780 +f 1837/4135/3422 1838/4138/3425 1839/4140/3427 +f 1839/4140/3427 1843/4139/3426 5782/4142/3429 +f 1838/4138/3425 5164/2093/1778 1843/4139/3426 +f 1843/4139/3426 5163/2105/1788 5781/4141/3428 +f 1840/4144/3431 1837/4135/3422 1844/4143/3430 +f 5785/4149/3436 1840/4144/3431 5784/4145/3432 +f 1844/4143/3430 1839/4140/3427 5783/4146/3433 +f 5784/4145/3432 1844/4143/3430 7695/4147/3434 +f 1837/4135/3422 1840/4144/3431 1841/4136/3423 +f 1841/4136/3423 1845/4148/3435 5770/4110/3397 +f 1840/4144/3431 5785/4149/3436 1845/4148/3435 +f 1845/4148/3435 5786/4150/3437 5771/4114/3401 +f 1847/4154/3441 1846/4151/3438 1851/4153/3440 +f 5791/4161/3448 1847/4154/3441 5790/4155/3442 +f 1851/4153/3440 1850/4152/3439 5789/4157/3444 +f 5790/4155/3442 1851/4153/3440 7696/4158/3445 +f 1846/4151/3438 1847/4154/3441 1848/4160/3447 +f 1848/4160/3447 1852/4159/3446 5173/2118/1799 +f 1847/4154/3441 5791/4161/3448 1852/4159/3446 +f 1852/4159/3446 5792/4162/3449 5174/2123/1804 +f 1849/4164/3451 1846/4151/3438 1853/4163/3450 +f 5779/4129/3416 1849/4164/3451 5780/4130/3417 +f 1853/4163/3450 1848/4160/3447 5172/2117/1798 +f 5780/4130/3417 1853/4163/3450 7572/2121/1802 +f 1846/4151/3438 1849/4164/3451 1850/4152/3439 +f 1850/4152/3439 1854/4165/3452 5788/4156/3443 +f 1849/4164/3451 5779/4129/3416 1854/4165/3452 +f 1854/4165/3452 5778/4123/3410 5787/4166/3453 +f 1855/4172/3457 1859/4167/3454 1856/4169/3456 +f 1856/4169/3456 1860/4168/3455 5152/4171/1746 +f 1860/4168/3455 1859/4167/3454 5781/4141/3428 +f 5153/4170/1747 1860/4168/3455 7573/2107/1748 +f 1857/4174/3459 1855/4172/3457 1861/4173/3458 +f 5794/4182/3465 1857/4174/3459 5793/4175/3460 +f 1861/4173/3458 1856/4169/3456 5151/4176/1756 +f 5793/4175/3460 1861/4173/3458 7575/4177/1716 +f 1855/4172/3457 1857/4174/3459 1858/4179/3462 +f 1858/4179/3462 1862/4178/3461 5797/4181/3464 +f 1857/4174/3459 5794/4182/3465 1862/4178/3461 +f 1862/4178/3461 5795/4183/3466 5796/4180/3463 +f 1859/4167/3454 1855/4172/3457 1863/4185/3468 +f 1859/4167/3454 1863/4185/3468 5782/4142/3429 +f 1863/4185/3468 1858/4179/3462 5798/4186/3469 +f 5783/4146/3433 1863/4185/3468 7695/4147/3434 +f 1864/4195/3478 1868/4187/3470 1865/4189/3472 +f 1865/4189/3472 1869/4188/3471 5803/4191/3474 +f 1868/4187/3470 5800/4192/3475 1869/4188/3471 +f 1869/4188/3471 5801/4193/3476 5802/4190/3473 +f 1866/4197/3480 1864/4195/3478 1870/4196/3479 +f 5161/4204/1767 1866/4197/3480 5162/4198/1772 +f 1870/4196/3479 1865/4189/3472 5804/4199/3481 +f 5162/4198/1772 1870/4196/3479 7576/4200/1737 +f 1864/4195/3478 1866/4197/3480 1867/4202/3483 +f 5791/4209/3448 1867/4202/3483 5792/4203/3449 +f 1866/4197/3480 5161/4204/1767 1871/4201/3482 +f 5792/4203/3449 1871/4201/3482 7574/4206/1770 +f 1868/4187/3470 1864/4195/3478 1872/4207/3484 +f 5800/4192/3475 1868/4187/3470 5799/4208/3485 +f 1867/4202/3483 5791/4209/3448 1872/4207/3484 +f 5799/4208/3485 1872/4207/3484 7696/4211/3445 +f 1873/4217/3489 1877/4212/3486 1874/4214/3488 +f 1874/4214/3488 1878/4213/3487 5140/4216/1714 +f 1877/4212/3486 5794/4182/3465 1878/4213/3487 +f 1878/4213/3487 5793/4175/3460 5141/4215/1715 +f 1875/4219/3491 1873/4217/3489 1879/4218/3490 +f 5806/4227/3497 1875/4219/3491 5805/4220/3492 +f 1879/4218/3490 1874/4214/3488 5139/4221/1724 +f 1879/4218/3490 5139/4221/1724 5805/4220/3492 +f 1873/4217/3489 1875/4219/3491 1876/4224/3494 +f 1876/4224/3494 1880/4223/3493 5809/4226/3496 +f 1875/4219/3491 5806/4227/3497 1880/4223/3493 +f 1880/4223/3493 5807/4228/3498 5808/4225/3495 +f 1877/4212/3486 1873/4217/3489 1881/4230/3500 +f 5794/4182/3465 1877/4212/3486 5795/4183/3466 +f 1881/4230/3500 1876/4224/3494 5810/4231/3501 +f 5795/4183/3466 1881/4230/3500 7693/4184/3467 +f 1882/4240/3510 1886/4232/3502 1883/4234/3504 +f 1883/4234/3504 1887/4233/3503 5815/4236/3506 +f 1886/4232/3502 5812/4237/3507 1887/4233/3503 +f 1887/4233/3503 5813/4238/3508 5814/4235/3505 +f 1884/4242/3512 1882/4240/3510 1888/4241/3511 +f 5149/4248/1739 1884/4242/3512 5150/4243/1740 +f 1888/4241/3511 1883/4234/3504 5816/4244/3513 +f 1888/4241/3511 5816/4244/3513 5150/4243/1740 +f 1882/4240/3510 1884/4242/3512 1885/4247/3515 +f 1885/4247/3515 1889/4246/3514 5803/4191/3474 +f 1884/4242/3512 5149/4248/1739 1889/4246/3514 +f 1889/4246/3514 5148/4249/1735 5804/4199/3481 +f 1886/4232/3502 1882/4240/3510 1890/4250/3516 +f 5812/4237/3507 1886/4232/3502 5811/4251/3517 +f 1890/4250/3516 1885/4247/3515 5802/4190/3473 +f 5811/4251/3517 1890/4250/3516 7694/4194/3477 +f 1892/4255/3521 1891/4252/3518 1896/4254/3520 +f 5128/4261/1682 1892/4255/3521 5129/4256/1683 +f 1896/4254/3520 1895/4253/3519 5805/4220/3492 +f 5129/4256/1683 1896/4254/3520 7577/4222/1684 +f 1891/4252/3518 1892/4255/3521 1893/4258/3523 +f 1893/4258/3523 1897/4257/3522 5818/4260/3525 +f 1892/4255/3521 5128/4261/1682 1897/4257/3522 +f 1897/4257/3522 5127/4262/1692 5817/4259/3524 +f 1894/4264/3527 1891/4252/3518 1898/4263/3526 +f 5821/4269/3532 1894/4264/3527 5820/4265/3528 +f 1898/4263/3526 1893/4258/3523 5819/4266/3529 +f 5820/4265/3528 1898/4263/3526 7687/4267/3530 +f 1891/4252/3518 1894/4264/3527 1895/4253/3519 +f 1895/4253/3519 1899/4268/3531 5806/4227/3497 +f 1894/4264/3527 5821/4269/3532 1899/4268/3531 +f 5807/4228/3498 1899/4268/3531 7691/4229/3499 +f 1901/4274/3537 1900/4271/3534 1905/4273/3536 +f 5827/4283/3544 1901/4274/3537 5826/4275/3538 +f 1905/4273/3536 1904/4272/3535 5825/4277/3540 +f 5826/4275/3538 1905/4273/3536 7688/4278/3541 +f 1900/4271/3534 1901/4274/3537 1902/4280/3543 +f 1902/4280/3543 1906/4279/3542 5137/4282/1703 +f 1901/4274/3537 5827/4283/3544 1906/4279/3542 +f 1906/4279/3542 5828/4284/3545 5138/4281/1708 +f 1903/4287/3547 1900/4271/3534 1907/4286/3546 +f 5815/4236/3506 1903/4287/3547 5816/4244/3513 +f 1907/4286/3546 1902/4280/3543 5136/4288/1702 +f 5816/4244/3513 1907/4286/3546 7578/4245/1706 +f 1900/4271/3534 1903/4287/3547 1904/4272/3535 +f 1904/4272/3535 1908/4289/3548 5824/4276/3539 +f 1903/4287/3547 5815/4236/3506 1908/4289/3548 +f 5823/4290/3549 1908/4289/3548 7692/4239/3509 +f 1910/4294/3553 1909/4291/3550 1914/4293/3552 +f 5116/1937/1654 1910/4294/3553 5117/1938/1655 +f 1914/4293/3552 1913/4292/3551 5817/4259/3524 +f 5117/1938/1655 1914/4293/3552 7630/1939/1656 +f 1909/4291/3550 1910/4294/3553 1911/4296/3555 +f 1911/4296/3555 1915/4295/3554 5830/4298/3557 +f 1910/4294/3553 5116/1937/1654 1915/4295/3554 +f 1915/4295/3554 5115/1932/1649 5829/4297/3556 +f 1912/4300/3559 1909/4291/3550 1916/4299/3558 +f 5833/4305/3564 1912/4300/3559 5832/4301/3560 +f 1916/4299/3558 1911/4296/3555 5831/4302/3561 +f 5832/4301/3560 1916/4299/3558 7689/4303/3562 +f 1909/4291/3550 1912/4300/3559 1913/4292/3551 +f 1913/4292/3551 1917/4304/3563 5818/4260/3525 +f 1912/4300/3559 5833/4305/3564 1917/4304/3563 +f 1917/4304/3563 5834/4306/3565 5819/4266/3529 +f 1919/4310/3569 1918/4307/3566 1923/4309/3568 +f 5839/4319/3576 1919/4310/3569 5838/4311/3570 +f 1923/4309/3568 1922/4308/3567 5837/4313/3572 +f 5838/4311/3570 1923/4309/3568 7690/4314/3573 +f 1918/4307/3566 1919/4310/3569 1920/4316/3575 +f 1920/4316/3575 1924/4315/3574 5125/4318/1669 +f 1919/4310/3569 5839/4319/3576 1924/4315/3574 +f 1924/4315/3574 5840/4320/3577 5126/4317/1674 +f 1921/4323/3579 1918/4307/3566 1925/4322/3578 +f 5827/4329/3544 1921/4323/3579 5828/4324/3545 +f 1925/4322/3578 1920/4316/3575 5124/4325/1668 +f 5828/4324/3545 1925/4322/3578 7631/4326/1672 +f 1918/4307/3566 1921/4323/3579 1922/4308/3567 +f 1922/4308/3567 1926/4327/3580 5836/4312/3571 +f 1921/4323/3579 5827/4329/3544 1926/4327/3580 +f 1926/4327/3580 5826/4330/3538 5835/4328/3581 +f 1928/4335/3585 1927/4332/3582 1932/4334/3584 +f 1928/4335/3585 1932/4334/3584 5101/1883/1609 +f 1932/4334/3584 1931/4333/3583 5829/4297/3556 +f 1932/4334/3584 5829/4297/3556 5102/1891/1617 +f 1927/4332/3582 1928/4335/3585 1929/4337/3587 +f 1929/4337/3587 1933/4336/3586 5722/3950/3265 +f 1933/4336/3586 1928/4335/3585 5100/1882/1608 +f 1933/4336/3586 5100/1882/1608 5723/3951/3266 +f 1930/4339/3589 1927/4332/3582 1934/4338/3588 +f 5842/4342/3592 1930/4339/3589 5841/4340/3590 +f 1934/4338/3588 1929/4337/3587 5721/3964/3275 +f 5841/4340/3590 1934/4338/3588 7707/3966/3277 +f 1927/4332/3582 1930/4339/3589 1931/4333/3583 +f 1931/4333/3583 1935/4341/3591 5830/4298/3557 +f 1930/4339/3589 5842/4342/3592 1935/4341/3591 +f 1935/4341/3591 5843/4343/3593 5831/4302/3561 +f 1937/4347/3597 1936/4344/3594 1941/4346/3596 +f 5731/3984/3291 1937/4347/3597 5732/3985/3292 +f 1941/4346/3596 1940/4345/3595 5846/4349/3599 +f 5732/3985/3292 1941/4346/3596 7708/3986/3293 +f 1936/4344/3594 1937/4347/3597 1938/4351/3601 +f 5110/4355/1640 1938/4351/3601 5111/4352/1641 +f 1937/4347/3597 5731/3984/3291 1942/4350/3600 +f 1942/4350/3600 5730/3979/3288 5111/4352/1641 +f 1939/4354/3603 1936/4344/3594 1943/4353/3602 +f 5839/4319/3576 1939/4354/3603 5840/4320/3577 +f 1938/4351/3601 5110/4355/1640 1943/4353/3602 +f 1943/4353/3602 5109/4356/1633 5840/4320/3577 +f 1936/4344/3594 1939/4354/3603 1940/4345/3595 +f 1940/4345/3595 1944/4357/3604 5845/4348/3598 +f 1939/4354/3603 5839/4319/3576 1944/4357/3604 +f 1944/4357/3604 5838/4311/3570 5844/4358/3605 +f 1946/4362/3609 1945/4359/3606 1950/4361/3608 +f 5842/4342/3592 1946/4362/3609 5843/4343/3593 +f 1950/4361/3608 1949/4360/3607 5849/4364/3611 +f 5843/4343/3593 1950/4361/3608 7689/4303/3562 +f 1945/4359/3606 1946/4362/3609 1947/4366/3613 +f 1947/4366/3613 1951/4365/3612 5851/4368/3615 +f 1946/4362/3609 5842/4342/3592 1951/4365/3612 +f 1951/4365/3612 5841/4340/3590 5850/4367/3614 +f 1948/4374/3621 1945/4359/3606 1952/4369/3616 +f 1948/4374/3621 1952/4369/3616 5854/4371/3618 +f 1952/4369/3616 1947/4366/3613 5852/4372/3619 +f 1952/4369/3616 5852/4372/3619 5853/4370/3617 +f 1949/4360/3607 1945/4359/3606 1953/4375/3622 +f 1949/4360/3607 1953/4375/3622 5848/4363/3610 +f 1953/4375/3622 1948/4374/3621 5855/4377/3624 +f 5847/4376/3623 1953/4375/3622 7727/4378/3625 +f 1955/4382/3629 1954/4379/3626 1959/4381/3628 +f 5860/4391/3636 1955/4382/3629 5859/4383/3630 +f 1958/4380/3627 5857/4384/3631 1959/4381/3628 +f 1959/4381/3628 5858/4385/3632 5859/4383/3630 +f 1954/4379/3626 1955/4382/3629 1956/4388/3635 +f 1956/4388/3635 1960/4387/3634 5845/4390/3598 +f 1955/4382/3629 5860/4391/3636 1960/4387/3634 +f 1960/4387/3634 5861/4392/3637 5846/4389/3599 +f 1957/4395/3639 1954/4379/3626 1961/4394/3638 +f 5863/4401/3643 1957/4395/3639 5862/4396/3640 +f 1961/4394/3638 1956/4388/3635 5844/4397/3605 +f 5862/4396/3640 1961/4394/3638 7690/4398/3573 +f 1958/4380/3627 1954/4379/3626 1962/4399/3641 +f 5857/4384/3631 1958/4380/3627 5856/4400/3642 +f 1957/4395/3639 5863/4401/3643 1962/4399/3641 +f 5856/4400/3642 1962/4399/3641 7728/4403/3645 +f 1964/4407/3649 1963/4404/3646 1968/4406/3648 +f 5833/4305/3564 1964/4407/3649 5834/4306/3565 +f 1968/4406/3648 1967/4405/3647 5867/4409/3651 +f 5834/4306/3565 1968/4406/3648 7687/4267/3530 +f 1963/4404/3646 1964/4407/3649 1965/4411/3653 +f 1965/4411/3653 1969/4410/3652 5848/4363/3610 +f 1964/4407/3649 5833/4305/3564 1969/4410/3652 +f 1969/4410/3652 5832/4301/3560 5849/4364/3611 +f 1966/4413/3655 1963/4404/3646 1970/4412/3654 +f 5869/4417/3659 1966/4413/3655 5868/4414/3656 +f 1970/4412/3654 1965/4411/3653 5847/4376/3623 +f 5868/4414/3656 1970/4412/3654 7727/4378/3625 +f 1963/4404/3646 1966/4413/3655 1967/4405/3647 +f 1967/4405/3647 1971/4415/3657 5866/4408/3650 +f 1966/4413/3655 5869/4417/3659 1971/4415/3657 +f 1971/4415/3657 5870/4418/3660 5865/4416/3658 +f 1973/4423/3665 1972/4420/3662 1977/4422/3664 +f 5863/4401/3643 1973/4423/3665 5864/4402/3644 +f 1977/4422/3664 1976/4421/3663 5873/4425/3667 +f 5864/4402/3644 1977/4422/3664 7728/4403/3645 +f 1972/4420/3662 1973/4423/3665 1974/4427/3669 +f 1974/4427/3669 1978/4426/3668 5836/4429/3571 +f 1973/4423/3665 5863/4401/3643 1978/4426/3668 +f 1978/4426/3668 5862/4396/3640 5837/4428/3572 +f 1975/4431/3671 1972/4420/3662 1979/4430/3670 +f 5875/4437/3675 1975/4431/3671 5874/4432/3672 +f 1979/4430/3670 1974/4427/3669 5835/4433/3581 +f 5874/4432/3672 1979/4430/3670 7688/4434/3541 +f 1972/4420/3662 1975/4431/3671 1976/4421/3663 +f 1976/4421/3663 1980/4435/3673 5872/4424/3666 +f 1975/4431/3671 5875/4437/3675 1980/4435/3673 +f 1980/4435/3673 5876/4438/3676 5871/4436/3674 +f 1982/4443/3681 1981/4440/3678 1986/4442/3680 +f 5821/4452/3532 1982/4443/3681 5822/4444/3533 +f 1985/4441/3679 5878/4445/3682 1986/4442/3680 +f 1986/4442/3680 5879/4446/3683 5822/4444/3533 +f 1981/4440/3678 1982/4443/3681 1983/4449/3685 +f 1983/4449/3685 1987/4448/3684 5866/4451/3650 +f 1982/4443/3681 5821/4452/3532 1987/4448/3684 +f 1987/4448/3684 5820/4453/3528 5867/4450/3651 +f 1984/4456/3687 1981/4440/3678 1988/4455/3686 +f 5881/4462/3691 1984/4456/3687 5880/4457/3688 +f 1988/4455/3686 1983/4449/3685 5865/4458/3658 +f 5880/4457/3688 1988/4455/3686 7729/4459/3661 +f 1981/4440/3678 1984/4456/3687 1985/4441/3679 +f 5878/4445/3682 1985/4441/3679 5877/4461/3690 +f 1984/4456/3687 5881/4462/3691 1989/4460/3689 +f 1989/4460/3689 5882/4463/3692 5877/4461/3690 +f 1991/4468/3697 1990/4465/3694 1995/4467/3696 +f 5875/4437/3675 1991/4468/3697 5876/4438/3676 +f 1995/4467/3696 1994/4466/3695 5885/4470/3699 +f 5876/4438/3676 1995/4467/3696 7730/4439/3677 +f 1990/4465/3694 1991/4468/3697 1992/4472/3701 +f 1992/4472/3701 1996/4471/3700 5824/4474/3539 +f 1991/4468/3697 5875/4437/3675 1996/4471/3700 +f 1996/4471/3700 5874/4432/3672 5825/4473/3540 +f 1993/4480/3705 1990/4465/3694 1997/4475/3702 +f 1993/4480/3705 1997/4475/3702 5887/4477/3704 +f 1997/4475/3702 1992/4472/3701 5823/4478/3549 +f 1997/4475/3702 5823/4478/3549 5886/4476/3703 +f 1990/4465/3694 1993/4480/3705 1994/4466/3695 +f 1994/4466/3695 1998/4481/3706 5884/4469/3698 +f 1998/4481/3706 1993/4480/3705 5888/4483/3708 +f 1998/4481/3706 5888/4483/3708 5883/4482/3707 +f 1999/4493/3715 2003/4485/3710 2000/4487/3712 +f 2000/4487/3712 2004/4486/3711 5809/4489/3496 +f 2003/4485/3710 5890/4490/3713 2004/4486/3711 +f 2004/4486/3711 5891/4491/3714 5810/4488/3501 +f 2001/4495/3717 1999/4493/3715 2005/4494/3716 +f 5878/4445/3682 2001/4495/3717 5879/4446/3683 +f 2005/4494/3716 2000/4487/3712 5808/4496/3495 +f 5879/4446/3683 2005/4494/3716 7691/4447/3499 +f 1999/4493/3715 2001/4495/3717 2002/4498/3719 +f 2002/4498/3719 2006/4497/3718 5893/4500/3721 +f 2001/4495/3717 5878/4445/3682 2006/4497/3718 +f 5892/4499/3720 2006/4497/3718 7725/4464/3693 +f 2003/4485/3710 1999/4493/3715 2007/4501/3722 +f 5890/4490/3713 2003/4485/3710 5889/4502/3723 +f 2007/4501/3722 2002/4498/3719 5894/4503/3724 +f 5889/4502/3723 2007/4501/3722 7723/4504/3725 +f 2008/4510/3731 2012/4505/3726 2009/4507/3728 +f 2009/4507/3728 2013/4506/3727 5887/4477/3704 +f 2012/4505/3726 5896/4508/3729 2013/4506/3727 +f 5888/4483/3708 2013/4506/3727 7726/4484/3709 +f 2010/4512/3733 2008/4510/3731 2014/4511/3732 +f 5812/4518/3507 2010/4512/3733 5813/4513/3508 +f 2014/4511/3732 2009/4507/3728 5886/4476/3703 +f 5813/4513/3508 2014/4511/3732 7692/4479/3509 +f 2008/4510/3731 2010/4512/3733 2011/4515/3735 +f 2011/4515/3735 2015/4514/3734 5899/4517/3737 +f 2010/4512/3733 5812/4518/3507 2015/4514/3734 +f 2015/4514/3734 5811/4519/3517 5898/4516/3736 +f 2012/4505/3726 2008/4510/3731 2016/4521/3738 +f 5896/4508/3729 2012/4505/3726 5895/4522/3739 +f 2016/4521/3738 2011/4515/3735 5900/4523/3740 +f 5895/4522/3739 2016/4521/3738 7724/4524/3741 +f 2017/4533/3747 2021/4525/3742 2018/4527/3744 +f 2018/4527/3744 2022/4526/3743 5797/4529/3464 +f 2022/4526/3743 2021/4525/3742 5903/4531/3746 +f 5798/4528/3469 2022/4526/3743 7695/4532/3434 +f 2019/4535/3749 2017/4533/3747 2023/4534/3748 +f 5890/4490/3713 2019/4535/3749 5891/4491/3714 +f 2023/4534/3748 2018/4527/3744 5796/4536/3463 +f 5891/4491/3714 2023/4534/3748 7693/4492/3467 +f 2017/4533/3747 2019/4535/3749 2020/4538/3751 +f 2020/4538/3751 2024/4537/3750 5905/4540/3753 +f 2019/4535/3749 5890/4490/3713 2024/4537/3750 +f 2024/4537/3750 5889/4502/3723 5904/4539/3752 +f 2021/4525/3742 2017/4533/3747 2025/4541/3754 +f 2021/4525/3742 2025/4541/3754 5902/4530/3745 +f 2020/4538/3751 5905/4540/3753 2025/4541/3754 +f 2025/4541/3754 5906/4543/3756 5901/4542/3755 +f 2026/4553/3763 2030/4545/3758 2027/4547/3760 +f 2027/4547/3760 2031/4546/3759 5899/4549/3737 +f 2030/4545/3758 5908/4550/3761 2031/4546/3759 +f 2031/4546/3759 5909/4551/3762 5900/4548/3740 +f 2028/4555/3765 2026/4553/3763 2032/4554/3764 +f 5800/4562/3475 2028/4555/3765 5801/4556/3476 +f 2032/4554/3764 2027/4547/3760 5898/4557/3736 +f 5801/4556/3476 2032/4554/3764 7694/4558/3477 +f 2026/4553/3763 2028/4555/3765 2029/4560/3767 +f 5911/4567/3771 2029/4560/3767 5910/4561/3768 +f 2028/4555/3765 5800/4562/3475 2033/4559/3766 +f 5910/4561/3768 2033/4559/3766 7696/4564/3445 +f 2030/4545/3758 2026/4553/3763 2034/4565/3769 +f 2030/4545/3758 2034/4565/3769 5908/4550/3761 +f 2029/4560/3767 5911/4567/3771 2034/4565/3769 +f 2034/4565/3769 5912/4568/3772 5907/4566/3770 +f 2036/4573/3777 2035/4570/3774 2040/4572/3776 +f 5785/4580/3436 2036/4573/3777 5786/4574/3437 +f 2040/4572/3776 2039/4571/3775 5915/4576/3779 +f 5786/4574/3437 2040/4572/3776 7697/4577/3402 +f 2035/4570/3774 2036/4573/3777 2037/4579/3781 +f 2037/4579/3781 2041/4578/3780 5902/4530/3745 +f 2036/4573/3777 5785/4580/3436 2041/4578/3780 +f 2041/4578/3780 5784/4581/3432 5903/4531/3746 +f 2038/4583/3783 2035/4570/3774 2042/4582/3782 +f 5917/4587/3787 2038/4583/3783 5916/4584/3784 +f 2042/4582/3782 2037/4579/3781 5901/4542/3755 +f 5916/4584/3784 2042/4582/3782 7721/4544/3757 +f 2035/4570/3774 2038/4583/3783 2039/4571/3775 +f 2039/4571/3775 2043/4585/3785 5914/4575/3778 +f 2038/4583/3783 5917/4587/3787 2043/4585/3785 +f 2043/4585/3785 5918/4588/3788 5913/4586/3786 +f 2045/4593/3793 2044/4590/3790 2049/4592/3792 +f 5911/4567/3771 2045/4593/3793 5912/4568/3772 +f 2049/4592/3792 2048/4591/3791 5921/4595/3795 +f 5912/4568/3772 2049/4592/3792 7722/4569/3773 +f 2044/4590/3790 2045/4593/3793 2046/4597/3797 +f 2046/4597/3797 2050/4596/3796 5788/4599/3443 +f 2045/4593/3793 5911/4567/3771 2050/4596/3796 +f 2050/4596/3796 5910/4561/3768 5789/4598/3444 +f 2047/4601/3799 2044/4590/3790 2051/4600/3798 +f 5923/4607/3803 2047/4601/3799 5922/4602/3800 +f 2051/4600/3798 2046/4597/3797 5787/4603/3453 +f 5922/4602/3800 2051/4600/3798 7698/4604/3413 +f 2044/4590/3790 2047/4601/3799 2048/4591/3791 +f 2048/4591/3791 2052/4605/3801 5920/4594/3794 +f 2047/4601/3799 5923/4607/3803 2052/4605/3801 +f 2052/4605/3801 5924/4608/3804 5919/4606/3802 +f 2054/4613/3809 2053/4610/3806 2058/4612/3808 +f 5773/4117/3404 2054/4613/3809 5774/4118/3405 +f 2058/4612/3808 2057/4611/3807 5927/4615/3811 +f 5774/4118/3405 2058/4612/3808 7699/4081/3370 +f 2053/4610/3806 2054/4613/3809 2055/4617/3813 +f 2055/4617/3813 2059/4616/3812 5914/4619/3778 +f 2054/4613/3809 5773/4117/3404 2059/4616/3812 +f 2059/4616/3812 5772/4113/3400 5915/4618/3779 +f 2056/4621/3815 2053/4610/3806 2060/4620/3814 +f 5929/4627/3819 2056/4621/3815 5928/4622/3816 +f 2060/4620/3814 2055/4617/3813 5913/4623/3786 +f 5928/4622/3816 2060/4620/3814 7719/4624/3789 +f 2053/4610/3806 2056/4621/3815 2057/4611/3807 +f 2057/4611/3807 2061/4625/3817 5926/4614/3810 +f 2056/4621/3815 5929/4627/3819 2061/4625/3817 +f 5925/4626/3818 2061/4625/3817 7717/4629/3821 +f 2063/4633/3825 2062/4630/3822 2067/4632/3824 +f 5923/4607/3803 2063/4633/3825 5924/4608/3804 +f 2067/4632/3824 2066/4631/3823 5933/4635/3827 +f 5924/4608/3804 2067/4632/3824 7720/4609/3805 +f 2062/4630/3822 2063/4633/3825 2064/4637/3829 +f 2064/4637/3829 2068/4636/3828 5776/4639/3411 +f 2063/4633/3825 5923/4607/3803 2068/4636/3828 +f 2068/4636/3828 5922/4602/3800 5777/4638/3412 +f 2065/4641/3831 2062/4630/3822 2069/4640/3830 +f 5935/4647/3835 2065/4641/3831 5934/4642/3832 +f 2069/4640/3830 2064/4637/3829 5775/4643/3421 +f 5934/4642/3832 2069/4640/3830 7700/4644/3381 +f 2062/4630/3822 2065/4641/3831 2066/4631/3823 +f 2066/4631/3823 2070/4645/3833 5932/4634/3826 +f 2065/4641/3831 5935/4647/3835 2070/4645/3833 +f 5931/4646/3834 2070/4645/3833 7718/4649/3837 +f 2071/4655/3843 2075/4650/3838 2072/4652/3840 +f 5761/4084/3372 2072/4652/3840 5762/4085/3373 +f 2075/4650/3838 5938/4653/3841 2076/4651/3839 +f 5762/4085/3373 2076/4651/3839 7701/4086/3338 +f 2073/4657/3845 2071/4655/3843 2077/4656/3844 +f 5926/4614/3810 2073/4657/3845 5927/4615/3811 +f 2072/4652/3840 5761/4084/3372 2077/4656/3844 +f 2077/4656/3844 5760/4079/3368 5927/4615/3811 +f 2071/4655/3843 2073/4657/3845 2074/4659/3847 +f 2074/4659/3847 2078/4658/3846 5941/4661/3849 +f 2073/4657/3845 5926/4614/3810 2078/4658/3846 +f 2078/4658/3846 5925/4626/3818 5940/4660/3848 +f 2075/4650/3838 2071/4655/3843 2079/4662/3850 +f 5938/4653/3841 2075/4650/3838 5937/4663/3851 +f 2079/4662/3850 2074/4659/3847 5942/4664/3852 +f 5937/4663/3851 2079/4662/3850 7715/4665/3853 +f 2080/4671/3859 2084/4666/3854 2081/4668/3856 +f 2081/4668/3856 2085/4667/3855 5935/4647/3835 +f 2084/4666/3854 5944/4669/3857 2085/4667/3855 +f 2085/4667/3855 5945/4670/3858 5936/4648/3836 +f 2082/4675/3861 2080/4671/3859 2086/4672/3860 +f 2082/4675/3861 2086/4672/3860 5764/4674/3379 +f 2086/4672/3860 2081/4668/3856 5934/4642/3832 +f 2086/4672/3860 5934/4642/3832 5765/4673/3380 +f 2080/4671/3859 2082/4675/3861 2083/4677/3863 +f 2083/4677/3863 2087/4676/3862 5947/4679/3865 +f 2087/4676/3862 2082/4675/3861 5763/4680/3389 +f 5946/4678/3864 2087/4676/3862 7702/4681/3349 +f 2084/4666/3854 2080/4671/3859 2088/4682/3866 +f 5944/4669/3857 2084/4666/3854 5943/4683/3867 +f 2088/4682/3866 2083/4677/3863 5948/4684/3868 +f 5943/4683/3867 2088/4682/3866 7716/4685/3869 +f 2089/4694/3875 2093/4686/3870 2090/4688/3872 +f 2090/4688/3872 2094/4687/3871 5749/4690/3340 +f 2093/4686/3870 5950/4691/3873 2094/4687/3871 +f 2094/4687/3871 5951/4692/3874 5750/4689/3341 +f 2091/4696/3877 2089/4694/3875 2095/4695/3876 +f 5938/4653/3841 2091/4696/3877 5939/4654/3842 +f 2095/4695/3876 2090/4688/3872 5748/4697/3336 +f 2095/4695/3876 5748/4697/3336 5939/4654/3842 +f 2089/4694/3875 2091/4696/3877 2092/4699/3879 +f 2092/4699/3879 2096/4698/3878 5953/4701/3881 +f 2091/4696/3877 5938/4653/3841 2096/4698/3878 +f 2096/4698/3878 5937/4663/3851 5952/4700/3880 +f 2093/4686/3870 2089/4694/3875 2097/4702/3882 +f 5950/4691/3873 2093/4686/3870 5949/4703/3883 +f 2097/4702/3882 2092/4699/3879 5954/4704/3884 +f 5949/4703/3883 2097/4702/3882 7713/4705/3885 +f 2098/4714/3891 2102/4706/3886 2099/4708/3888 +f 2099/4708/3888 2103/4707/3887 5947/4710/3865 +f 2102/4706/3886 5956/4711/3889 2103/4707/3887 +f 2103/4707/3887 5957/4712/3890 5948/4709/3868 +f 2100/4716/3893 2098/4714/3891 2104/4715/3892 +f 5752/4724/3347 2100/4716/3893 5753/4717/3348 +f 2104/4715/3892 2099/4708/3888 5946/4718/3864 +f 2104/4715/3892 5946/4718/3864 5753/4717/3348 +f 2098/4714/3891 2100/4716/3893 2101/4721/3895 +f 2101/4721/3895 2105/4720/3894 5959/4723/3897 +f 2100/4716/3893 5752/4724/3347 2105/4720/3894 +f 2105/4720/3894 5751/4725/3357 5958/4722/3896 +f 2102/4706/3886 2098/4714/3891 2106/4727/3898 +f 5956/4711/3889 2102/4706/3886 5955/4728/3899 +f 2106/4727/3898 2101/4721/3895 5960/4729/3900 +f 5955/4728/3899 2106/4727/3898 7714/4730/3901 +f 2108/4739/3907 2107/4731/3902 2112/4733/3904 +f 2108/4739/3907 2112/4733/3904 5737/4735/3304 +f 2112/4733/3904 2111/4732/3903 5963/4737/3906 +f 5738/4734/3309 2112/4733/3904 7685/4738/3204 +f 2109/4741/3909 2107/4731/3902 2113/4740/3908 +f 5950/4691/3873 2109/4741/3909 5951/4692/3874 +f 2113/4740/3908 2108/4739/3907 5736/4742/3303 +f 5951/4692/3874 2113/4740/3908 7703/4693/3307 +f 2107/4731/3902 2109/4741/3909 2110/4744/3911 +f 2110/4744/3911 2114/4743/3910 5965/4746/3913 +f 2109/4741/3909 5950/4691/3873 2114/4743/3910 +f 2114/4743/3910 5949/4703/3883 5964/4745/3912 +f 2107/4731/3902 2110/4744/3911 2111/4732/3903 +f 2111/4732/3903 2115/4747/3914 5962/4736/3905 +f 2110/4744/3911 5965/4746/3913 2115/4747/3914 +f 2115/4747/3914 5966/4749/3916 5961/4748/3915 +f 2116/4756/3923 2120/4751/3918 2117/4753/3920 +f 2117/4753/3920 2121/4752/3919 5959/4723/3897 +f 2120/4751/3918 5968/4754/3921 2121/4752/3919 +f 2121/4752/3919 5969/4755/3922 5960/4729/3900 +f 2118/4758/3925 2116/4756/3923 2122/4757/3924 +f 5740/4763/3315 2118/4758/3925 5741/4759/3316 +f 2122/4757/3924 2117/4753/3920 5958/4722/3896 +f 5741/4759/3316 2122/4757/3924 7704/4726/3317 +f 2119/4761/3927 2116/4756/3923 2123/4760/3926 +f 5971/4768/3931 2119/4761/3927 5970/4762/3928 +f 2118/4758/3925 5740/4763/3315 2123/4760/3926 +f 5970/4762/3928 2123/4760/3926 7686/4765/3217 +f 2116/4756/3923 2119/4761/3927 2120/4751/3918 +f 2120/4751/3918 2124/4766/3929 5968/4754/3921 +f 2119/4761/3927 5971/4768/3931 2124/4766/3929 +f 2124/4766/3929 5972/4769/3932 5967/4767/3930 +f 2126/4774/3937 2125/4771/3934 2130/4773/3936 +f 5725/3959/3273 2126/4774/3937 5726/3965/3276 +f 2130/4773/3936 2129/4772/3935 5850/4367/3614 +f 2130/4773/3936 5850/4367/3614 5726/3965/3276 +f 2125/4771/3934 2126/4774/3937 2127/4776/3939 +f 2127/4776/3939 2131/4775/3938 5974/4778/3941 +f 2126/4774/3937 5725/3959/3273 2131/4775/3938 +f 2131/4775/3938 5724/3958/3272 5973/4777/3940 +f 2128/4780/3943 2125/4771/3934 2132/4779/3942 +f 5977/4785/3948 2128/4780/3943 5976/4781/3944 +f 2132/4779/3942 2127/4776/3939 5975/4782/3945 +f 5976/4781/3944 2132/4779/3942 7711/4783/3946 +f 2125/4771/3934 2128/4780/3943 2129/4772/3935 +f 2129/4772/3935 2133/4784/3947 5851/4368/3615 +f 2128/4780/3943 5977/4785/3948 2133/4784/3947 +f 2133/4784/3947 5978/4786/3949 5852/4372/3619 +f 2135/4790/3953 2134/4787/3950 2139/4789/3952 +f 5983/4799/3960 2135/4790/3953 5982/4791/3954 +f 2139/4789/3952 2138/4788/3951 5981/4793/3956 +f 5982/4791/3954 2139/4789/3952 7712/4794/3957 +f 2134/4787/3950 2135/4790/3953 2136/4796/3959 +f 2136/4796/3959 2140/4795/3958 5728/4798/3281 +f 2135/4790/3953 5983/4799/3960 2140/4795/3958 +f 2140/4795/3958 5984/4800/3961 5729/4797/3282 +f 2137/4803/3963 2134/4787/3950 2141/4802/3962 +f 5860/4391/3636 2137/4803/3963 5861/4392/3637 +f 2141/4802/3962 2136/4796/3959 5727/4804/3290 +f 2141/4802/3962 5727/4804/3290 5861/4392/3637 +f 2134/4787/3950 2137/4803/3963 2138/4788/3951 +f 2138/4788/3951 2142/4805/3964 5980/4792/3955 +f 2137/4803/3963 5860/4391/3636 2142/4805/3964 +f 2142/4805/3964 5859/4383/3630 5979/4806/3965 +f 2144/4810/3969 2143/4807/3966 2148/4809/3968 +f 5713/4816/3243 2144/4810/3969 5714/4811/3244 +f 2148/4809/3968 2147/4808/3967 5973/4777/3940 +f 5714/4811/3244 2148/4809/3968 7705/3962/3245 +f 2143/4807/3966 2144/4810/3969 2145/4813/3971 +f 2145/4813/3971 2149/4812/3970 5986/4815/3973 +f 2144/4810/3969 5713/4816/3243 2149/4812/3970 +f 2149/4812/3970 5712/4817/3240 5985/4814/3972 +f 2146/4820/3975 2143/4807/3966 2150/4819/3974 +f 5989/4825/3980 2146/4820/3975 5988/4821/3976 +f 2150/4819/3974 2145/4813/3971 5987/4822/3977 +f 5988/4821/3976 2150/4819/3974 7733/4823/3978 +f 2143/4807/3966 2146/4820/3975 2147/4808/3967 +f 2147/4808/3967 2151/4824/3979 5974/4778/3941 +f 2146/4820/3975 5989/4825/3980 2151/4824/3979 +f 2151/4824/3979 5990/4826/3981 5975/4782/3945 +f 2153/4830/3985 2152/4827/3982 2157/4829/3984 +f 5995/4839/3992 2153/4830/3985 5994/4831/3986 +f 2157/4829/3984 2156/4828/3983 5993/4833/3988 +f 5994/4831/3986 2157/4829/3984 7734/4834/3989 +f 2152/4827/3982 2153/4830/3985 2154/4836/3991 +f 2154/4836/3991 2158/4835/3990 5716/4838/3250 +f 2153/4830/3985 5995/4839/3992 2158/4835/3990 +f 2158/4835/3990 5996/4840/3993 5717/4837/3251 +f 2155/4843/3995 2152/4827/3982 2159/4842/3994 +f 5983/4799/3960 2155/4843/3995 5984/4800/3961 +f 2159/4842/3994 2154/4836/3991 5715/4844/3259 +f 5984/4800/3961 2159/4842/3994 7706/4801/3261 +f 2152/4827/3982 2155/4843/3995 2156/4828/3983 +f 2156/4828/3983 2160/4845/3996 5992/4832/3987 +f 2155/4843/3995 5983/4799/3960 2160/4845/3996 +f 2160/4845/3996 5982/4791/3954 5991/4846/3997 +f 2162/4850/4001 2161/4847/3998 2166/4849/4000 +f 5698/4854/3207 2162/4850/4001 5699/4851/3208 +f 2166/4849/4000 2165/4848/3999 5985/4814/3972 +f 5699/4851/3208 2166/4849/4000 7683/4818/3209 +f 2161/4847/3998 2162/4850/4001 2163/4853/4003 +f 2163/4853/4003 2167/4852/4002 5962/4736/3905 +f 2162/4850/4001 5698/4854/3207 2167/4852/4002 +f 2167/4852/4002 5697/4855/3202 5963/4737/3906 +f 2164/4857/4005 2161/4847/3998 2168/4856/4004 +f 5998/4860/4008 2164/4857/4005 5997/4858/4006 +f 2168/4856/4004 2163/4853/4003 5961/4748/3915 +f 5997/4858/4006 2168/4856/4004 7731/4750/3917 +f 2161/4847/3998 2164/4857/4005 2165/4848/3999 +f 2165/4848/3999 2169/4859/4007 5986/4815/3973 +f 2164/4857/4005 5998/4860/4008 2169/4859/4007 +f 2169/4859/4007 5999/4861/4009 5987/4822/3977 +f 2171/4865/4013 2170/4862/4010 2175/4864/4012 +f 5971/4768/3931 2171/4865/4013 5972/4769/3932 +f 2175/4864/4012 2174/4863/4011 6002/4867/4015 +f 5972/4769/3932 2175/4864/4012 7732/4770/3933 +f 2170/4862/4010 2171/4865/4013 2172/4869/4017 +f 2172/4869/4017 2176/4868/4016 5701/4871/3215 +f 2171/4865/4013 5971/4768/3931 2176/4868/4016 +f 2176/4868/4016 5970/4762/3928 5702/4870/3216 +f 2173/4873/4019 2170/4862/4010 2177/4872/4018 +f 5995/4879/3992 2173/4873/4019 5996/4874/3993 +f 2177/4872/4018 2172/4869/4017 5700/4875/3226 +f 5996/4874/3993 2177/4872/4018 7684/4876/3229 +f 2170/4862/4010 2173/4873/4019 2174/4863/4011 +f 2174/4863/4011 2178/4877/4020 6001/4866/4014 +f 2173/4873/4019 5995/4879/3992 2178/4877/4020 +f 2178/4877/4020 5994/4880/3986 6000/4878/4021 +f 2179/4887/4027 2183/4882/4022 2180/4884/4024 +f 2180/4884/4024 2184/4883/4023 5083/1843/1574 +f 2184/4883/4023 2183/4882/4022 6005/4886/4026 +f 2184/4883/4023 6005/4886/4026 5084/1848/1579 +f 2181/4889/4029 2179/4887/4027 2185/4888/4028 +f 6007/4895/4035 2181/4889/4029 6006/4890/4030 +f 2185/4888/4028 2180/4884/4024 5082/1842/1573 +f 6006/4890/4030 2185/4888/4028 7559/1807/1540 +f 2179/4887/4027 2181/4889/4029 2182/4892/4032 +f 2182/4892/4032 2186/4891/4031 6010/4894/4034 +f 2181/4889/4029 6007/4895/4035 2186/4891/4031 +f 2186/4891/4031 6008/4896/4036 6009/4893/4033 +f 2183/4882/4022 2179/4887/4027 2187/4898/4038 +f 6004/4885/4025 2183/4882/4022 6003/4899/4039 +f 2187/4898/4038 2182/4892/4032 6011/4900/4040 +f 6003/4899/4039 2187/4898/4038 7524/4901/4041 +f 2188/4910/4050 2192/4902/4042 2189/4904/4044 +f 2189/4904/4044 2193/4903/4043 6016/4906/4046 +f 2192/4902/4042 6013/4907/4047 2193/4903/4043 +f 2193/4903/4043 6014/4908/4048 6015/4905/4045 +f 2190/4912/4052 2188/4910/4050 2194/4911/4051 +f 5095/4919/1601 2190/4912/4052 5096/4913/1602 +f 2194/4911/4051 2189/4904/4044 6017/4914/4053 +f 5096/4913/1602 2194/4911/4051 7560/4915/1563 +f 2188/4910/4050 2190/4912/4052 2191/4917/4055 +f 6004/4924/4025 2191/4917/4055 6005/4918/4026 +f 2190/4912/4052 5095/4919/1601 2195/4916/4054 +f 2195/4916/4054 5094/4920/1598 6005/4918/4026 +f 2192/4902/4042 2188/4910/4050 2196/4922/4056 +f 6013/4907/4047 2192/4902/4042 6012/4923/4057 +f 2196/4922/4056 2191/4917/4055 6003/4925/4039 +f 6012/4923/4057 2196/4922/4056 7524/4926/4041 +f 2197/4930/4061 2201/4927/4058 2198/4929/4060 +f 2198/4929/4060 2202/4928/4059 5065/1798/1534 +f 2201/4927/4058 6007/4895/4035 2202/4928/4059 +f 2202/4928/4059 6006/4890/4030 5066/1806/1539 +f 2199/4932/4063 2197/4930/4061 2203/4931/4062 +f 6019/4938/4069 2199/4932/4063 6018/4933/4064 +f 2203/4931/4062 2198/4929/4060 5064/1797/1533 +f 6018/4933/4064 2203/4931/4062 7557/1801/1500 +f 2197/4930/4061 2199/4932/4063 2200/4935/4066 +f 2200/4935/4066 2204/4934/4065 6022/4937/4068 +f 2204/4934/4065 2199/4932/4063 6020/4939/4070 +f 6021/4936/4067 2204/4934/4065 7737/4940/4071 +f 2201/4927/4058 2197/4930/4061 2205/4941/4072 +f 6007/4895/4035 2201/4927/4058 6008/4896/4036 +f 2205/4941/4072 2200/4935/4066 6023/4942/4073 +f 6008/4896/4036 2205/4941/4072 7735/4897/4037 +f 2206/4950/4081 2210/4943/4074 2207/4945/4076 +f 6028/4954/4084 2207/4945/4076 6027/4946/4077 +f 2210/4943/4074 6025/4947/4078 2211/4944/4075 +f 6027/4946/4077 2211/4944/4075 7738/4949/4080 +f 2208/4952/4083 2206/4950/4081 2212/4951/4082 +f 5080/4959/1566 2208/4952/4083 5081/4953/1567 +f 2212/4951/4082 2207/4945/4076 6029/4955/4085 +f 5081/4953/1567 2212/4951/4082 7558/4956/1523 +f 2206/4950/4081 2208/4952/4083 2209/4958/4087 +f 2209/4958/4087 2213/4957/4086 6016/4906/4046 +f 2208/4952/4083 5080/4959/1566 2213/4957/4086 +f 2213/4957/4086 5079/4960/1561 6017/4914/4053 +f 2210/4943/4074 2206/4950/4081 2214/4961/4088 +f 6025/4947/4078 2210/4943/4074 6024/4962/4089 +f 2214/4961/4088 2209/4958/4087 6015/4905/4045 +f 6024/4962/4089 2214/4961/4088 7736/4909/4049 +f 2215/4971/4093 2219/4963/4090 2216/4965/4092 +f 2216/4965/4092 2220/4964/4091 5047/4967/1494 +f 2219/4963/4090 6019/4968/4069 2220/4964/4091 +f 2220/4964/4091 6018/4969/4064 5048/4966/1499 +f 2217/4973/4095 2215/4971/4093 2221/4972/4094 +f 6031/4981/4101 2217/4973/4095 6030/4974/4096 +f 2221/4972/4094 2216/4965/4092 5046/4975/1493 +f 6030/4974/4096 2221/4972/4094 7555/4976/1460 +f 2215/4971/4093 2217/4973/4095 2218/4978/4098 +f 2218/4978/4098 2222/4977/4097 6034/4980/4100 +f 2217/4973/4095 6031/4981/4101 2222/4977/4097 +f 2222/4977/4097 6032/4982/4102 6033/4979/4099 +f 2219/4963/4090 2215/4971/4093 2223/4984/4104 +f 6019/4968/4069 2219/4963/4090 6020/4985/4070 +f 2223/4984/4104 2218/4978/4098 6035/4986/4105 +f 6020/4985/4070 2223/4984/4104 7737/4987/4071 +f 2224/4996/4114 2228/4988/4106 2225/4990/4108 +f 2225/4990/4108 2229/4989/4107 6040/4992/4110 +f 2228/4988/4106 6037/4993/4111 2229/4989/4107 +f 2229/4989/4107 6038/4994/4112 6039/4991/4109 +f 2226/4998/4116 2224/4996/4114 2230/4997/4115 +f 5062/5004/1526 2226/4998/4116 5063/4999/1527 +f 2230/4997/4115 2225/4990/4108 6041/5000/4117 +f 5063/4999/1527 2230/4997/4115 7556/5001/1483 +f 2224/4996/4114 2226/4998/4116 2227/5003/4119 +f 2227/5003/4119 2231/5002/4118 6028/4954/4084 +f 2226/4998/4116 5062/5004/1526 2231/5002/4118 +f 2231/5002/4118 5061/5005/1521 6029/4955/4085 +f 2228/4988/4106 2224/4996/4114 2232/5006/4120 +f 6037/4993/4111 2228/4988/4106 6036/5007/4121 +f 2232/5006/4120 2227/5003/4119 6027/4946/4077 +f 6036/5007/4121 2232/5006/4120 7738/4949/4080 +f 2233/5013/4125 2237/5008/4122 2234/5010/4124 +f 2234/5010/4124 2238/5009/4123 5029/5012/1454 +f 2237/5008/4122 6031/4981/4101 2238/5009/4123 +f 2238/5009/4123 6030/4974/4096 5030/5011/1459 +f 2235/5015/4127 2233/5013/4125 2239/5014/4126 +f 6043/5022/4132 2235/5015/4127 6042/5016/4128 +f 2239/5014/4126 2234/5010/4124 5028/5017/1453 +f 6042/5016/4128 2239/5014/4126 7553/5018/1420 +f 2233/5013/4125 2235/5015/4127 2236/5020/4130 +f 6046/5026/4136 2236/5020/4130 6045/5021/4131 +f 2235/5015/4127 6043/5022/4132 2240/5019/4129 +f 6045/5021/4131 2240/5019/4129 7741/5024/4134 +f 2237/5008/4122 2233/5013/4125 2241/5025/4135 +f 6031/4981/4101 2237/5008/4122 6032/4982/4102 +f 2241/5025/4135 2236/5020/4130 6047/5027/4137 +f 6032/4982/4102 2241/5025/4135 7739/4983/4103 +f 2242/5036/4146 2246/5028/4138 2243/5030/4140 +f 2243/5030/4140 2247/5029/4139 6052/5032/4142 +f 2247/5029/4139 2246/5028/4138 6050/5034/4144 +f 6051/5031/4141 2247/5029/4139 7742/5035/4145 +f 2244/5038/4148 2242/5036/4146 2248/5037/4147 +f 5044/1745/1486 2244/5038/4148 5045/1746/1487 +f 2248/5037/4147 2243/5030/4140 6053/5039/4149 +f 5045/1746/1487 2248/5037/4147 7554/1702/1443 +f 2242/5036/4146 2244/5038/4148 2245/5041/4151 +f 2245/5041/4151 2249/5040/4150 6040/5043/4110 +f 2244/5038/4148 5044/1745/1486 2249/5040/4150 +f 2249/5040/4150 5043/1739/1480 6041/5042/4117 +f 2246/5028/4138 2242/5036/4146 2250/5044/4152 +f 6049/5033/4143 2246/5028/4138 6048/5045/4153 +f 2250/5044/4152 2245/5041/4151 6039/5046/4109 +f 6048/5045/4153 2250/5044/4152 7740/5047/4113 +f 2251/5053/4157 2255/5048/4154 2252/5050/4156 +f 2252/5050/4156 2256/5049/4155 5011/5052/1414 +f 2255/5048/4154 6043/5022/4132 2256/5049/4155 +f 2256/5049/4155 6042/5016/4128 5012/5051/1419 +f 2253/5055/4159 2251/5053/4157 2257/5054/4158 +f 6055/5063/4165 2253/5055/4159 6054/5056/4160 +f 2257/5054/4158 2252/5050/4156 5010/5057/1413 +f 6054/5056/4160 2257/5054/4158 7551/5058/1380 +f 2251/5053/4157 2253/5055/4159 2254/5060/4162 +f 2254/5060/4162 2258/5059/4161 6058/5062/4164 +f 2253/5055/4159 6055/5063/4165 2258/5059/4161 +f 2258/5059/4161 6056/5064/4166 6057/5061/4163 +f 2255/5048/4154 2251/5053/4157 2259/5066/4168 +f 6043/5022/4132 2255/5048/4154 6044/5023/4133 +f 2259/5066/4168 2254/5060/4162 6059/5067/4169 +f 2259/5066/4168 6059/5067/4169 6044/5023/4133 +f 2260/5076/4178 2264/5068/4170 2261/5070/4172 +f 2261/5070/4172 2265/5069/4171 6064/5072/4174 +f 2264/5068/4170 6061/5073/4175 2265/5069/4171 +f 2265/5069/4171 6062/5074/4176 6063/5071/4173 +f 2262/5078/4180 2260/5076/4178 2266/5077/4179 +f 5026/1705/1446 2262/5078/4180 5027/1706/1447 +f 2266/5077/4179 2261/5070/4172 6065/5079/4181 +f 5027/1706/1447 2266/5077/4179 7552/1657/1403 +f 2260/5076/4178 2262/5078/4180 2263/5081/4183 +f 2263/5081/4183 2267/5080/4182 6052/5032/4142 +f 2262/5078/4180 5026/1705/1446 2267/5080/4182 +f 2267/5080/4182 5025/1700/1441 6053/5039/4149 +f 2264/5068/4170 2260/5076/4178 2268/5082/4184 +f 6061/5073/4175 2264/5068/4170 6060/5083/4185 +f 2268/5082/4184 2263/5081/4183 6051/5031/4141 +f 2268/5082/4184 6051/5031/4141 6060/5083/4185 +f 2269/5089/4189 2273/5084/4186 2270/5086/4188 +f 2270/5086/4188 2274/5085/4187 4993/5088/1374 +f 2273/5084/4186 6055/5063/4165 2274/5085/4187 +f 2274/5085/4187 6054/5056/4160 4994/5087/1379 +f 2271/5091/4191 2269/5089/4189 2275/5090/4190 +f 6067/5099/4197 2271/5091/4191 6066/5092/4192 +f 2275/5090/4190 2270/5086/4188 4992/5093/1373 +f 6066/5092/4192 2275/5090/4190 7549/5094/1340 +f 2269/5089/4189 2271/5091/4191 2272/5096/4194 +f 2272/5096/4194 2276/5095/4193 6070/5098/4196 +f 2271/5091/4191 6067/5099/4197 2276/5095/4193 +f 2276/5095/4193 6068/5100/4198 6069/5097/4195 +f 2273/5084/4186 2269/5089/4189 2277/5102/4200 +f 6055/5063/4165 2273/5084/4186 6056/5064/4166 +f 2277/5102/4200 2272/5096/4194 6071/5103/4201 +f 6056/5064/4166 2277/5102/4200 7743/5065/4167 +f 2278/5112/4210 2282/5104/4202 2279/5106/4204 +f 2279/5106/4204 2283/5105/4203 6076/5108/4206 +f 2282/5104/4202 6073/5109/4207 2283/5105/4203 +f 2283/5105/4203 6074/5110/4208 6075/5107/4205 +f 2280/5114/4212 2278/5112/4210 2284/5113/4211 +f 5008/1662/1406 2280/5114/4212 5009/1663/1407 +f 2284/5113/4211 2279/5106/4204 6077/5115/4213 +f 5009/1663/1407 2284/5113/4211 7550/1664/1363 +f 2278/5112/4210 2280/5114/4212 2281/5117/4215 +f 2281/5117/4215 2285/5116/4214 6064/5072/4174 +f 2280/5114/4212 5008/1662/1406 2285/5116/4214 +f 2285/5116/4214 5007/1655/1401 6065/5079/4181 +f 2282/5104/4202 2278/5112/4210 2286/5118/4216 +f 6073/5109/4207 2282/5104/4202 6072/5119/4217 +f 2286/5118/4216 2281/5117/4215 6063/5071/4173 +f 6072/5119/4217 2286/5118/4216 7744/5075/4177 +f 2287/5125/4221 2291/5120/4218 2288/5122/4220 +f 2288/5122/4220 2292/5121/4219 4975/5124/1334 +f 2291/5120/4218 6067/5099/4197 2292/5121/4219 +f 2292/5121/4219 6066/5092/4192 4976/5123/1339 +f 2289/5127/4223 2287/5125/4221 2293/5126/4222 +f 6079/5135/4229 2289/5127/4223 6078/5128/4224 +f 2293/5126/4222 2288/5122/4220 4974/5129/1333 +f 6078/5128/4224 2293/5126/4222 7547/5130/1300 +f 2287/5125/4221 2289/5127/4223 2290/5132/4226 +f 2290/5132/4226 2294/5131/4225 6082/5134/4228 +f 2289/5127/4223 6079/5135/4229 2294/5131/4225 +f 2294/5131/4225 6080/5136/4230 6081/5133/4227 +f 2291/5120/4218 2287/5125/4221 2295/5138/4232 +f 6067/5099/4197 2291/5120/4218 6068/5100/4198 +f 2295/5138/4232 2290/5132/4226 6083/5139/4233 +f 6068/5100/4198 2295/5138/4232 7745/5101/4199 +f 2296/5148/4242 2300/5140/4234 2297/5142/4236 +f 2297/5142/4236 2301/5141/4235 6088/5144/4238 +f 2300/5140/4234 6085/5145/4239 2301/5141/4235 +f 2301/5141/4235 6086/5146/4240 6087/5143/4237 +f 2298/5150/4244 2296/5148/4242 2302/5149/4243 +f 4990/5158/1366 2298/5150/4244 4991/5151/1367 +f 2302/5149/4243 2297/5142/4236 6089/5152/4245 +f 4991/5151/1367 2302/5149/4243 7548/5153/1324 +f 2296/5148/4242 2298/5150/4244 2299/5155/4247 +f 2299/5155/4247 2303/5154/4246 6076/5157/4206 +f 2298/5150/4244 4990/5158/1366 2303/5154/4246 +f 2303/5154/4246 4989/5159/1361 6077/5156/4213 +f 2300/5140/4234 2296/5148/4242 2304/5161/4248 +f 6085/5145/4239 2300/5140/4234 6084/5162/4249 +f 2304/5161/4248 2299/5155/4247 6075/5163/4205 +f 6084/5162/4249 2304/5161/4248 7746/5164/4209 +f 2306/5168/4253 2305/5165/4250 2310/5167/4252 +f 4957/5177/1298 2306/5168/4253 4958/5169/1299 +f 2309/5166/4251 6079/5170/4229 2310/5167/4252 +f 2310/5167/4252 6078/5171/4224 4958/5169/1299 +f 2305/5165/4250 2306/5168/4253 2307/5174/4255 +f 2307/5174/4255 2311/5173/4254 6091/5176/4257 +f 2306/5168/4253 4957/5177/1298 2311/5173/4254 +f 2311/5173/4254 4956/5178/1294 6090/5175/4256 +f 2308/5181/4259 2305/5165/4250 2312/5180/4258 +f 6094/5187/4264 2308/5181/4259 6093/5182/4260 +f 2312/5180/4258 2307/5174/4255 6092/5183/4261 +f 6093/5182/4260 2312/5180/4258 7749/5184/4262 +f 2305/5165/4250 2308/5181/4259 2309/5166/4251 +f 2309/5166/4251 2313/5185/4263 6079/5170/4229 +f 2308/5181/4259 6094/5187/4264 2313/5185/4263 +f 2313/5185/4263 6095/5188/4265 6080/5186/4230 +f 2315/5193/4269 2314/5190/4266 2319/5192/4268 +f 6100/5202/4276 2315/5193/4269 6099/5194/4270 +f 2319/5192/4268 2318/5191/4267 6098/5196/4272 +f 6099/5194/4270 2319/5192/4268 7750/5197/4273 +f 2314/5190/4266 2315/5193/4269 2316/5199/4275 +f 2316/5199/4275 2320/5198/4274 4972/5201/1321 +f 2315/5193/4269 6100/5202/4276 2320/5198/4274 +f 2320/5198/4274 6101/5203/4277 4973/5200/1327 +f 2317/5207/4279 2314/5190/4266 2321/5205/4278 +f 2317/5207/4279 2321/5205/4278 6088/5144/4238 +f 2321/5205/4278 2316/5199/4275 4971/5206/1320 +f 2321/5205/4278 4971/5206/1320 6089/5152/4245 +f 2314/5190/4266 2317/5207/4279 2318/5191/4267 +f 2318/5191/4267 2322/5208/4280 6097/5195/4271 +f 2317/5207/4279 6088/5144/4238 2322/5208/4280 +f 2322/5208/4280 6087/5143/4237 6096/5209/4281 +f 2324/5213/4285 2323/5210/4282 2328/5212/4284 +f 4939/5219/1259 2324/5213/4285 4940/5214/1260 +f 2328/5212/4284 2327/5211/4283 6090/5175/4256 +f 4940/5214/1260 2328/5212/4284 7545/5179/1261 +f 2323/5210/4282 2324/5213/4285 2325/5216/4287 +f 2325/5216/4287 2329/5215/4286 6103/5218/4289 +f 2324/5213/4285 4939/5219/1259 2329/5215/4286 +f 2329/5215/4286 4938/5220/1254 6102/5217/4288 +f 2326/5222/4291 2323/5210/4282 2330/5221/4290 +f 6106/5227/4296 2326/5222/4291 6105/5223/4292 +f 2330/5221/4290 2325/5216/4287 6104/5224/4293 +f 6105/5223/4292 2330/5221/4290 7751/5225/4294 +f 2323/5210/4282 2326/5222/4291 2327/5211/4283 +f 2327/5211/4283 2331/5226/4295 6091/5176/4257 +f 2326/5222/4291 6106/5227/4296 2331/5226/4295 +f 2331/5226/4295 6107/5228/4297 6092/5183/4261 +f 2333/5232/4301 2332/5229/4298 2337/5231/4300 +f 6112/5241/4308 2333/5232/4301 6111/5233/4302 +f 2337/5231/4300 2336/5230/4299 6110/5235/4304 +f 6111/5233/4302 2337/5231/4300 7752/5236/4305 +f 2332/5229/4298 2333/5232/4301 2334/5238/4307 +f 2334/5238/4307 2338/5237/4306 4954/5240/1281 +f 2333/5232/4301 6112/5241/4308 2338/5237/4306 +f 2338/5237/4306 6113/5242/4309 4955/5239/1287 +f 2335/5245/4311 2332/5229/4298 2339/5244/4310 +f 6100/5202/4276 2335/5245/4311 6101/5203/4277 +f 2339/5244/4310 2334/5238/4307 4953/5246/1280 +f 6101/5203/4277 2339/5244/4310 7546/5204/1284 +f 2332/5229/4298 2335/5245/4311 2336/5230/4299 +f 2336/5230/4299 2340/5247/4312 6109/5234/4303 +f 2335/5245/4311 6100/5202/4276 2340/5247/4312 +f 2340/5247/4312 6099/5194/4270 6108/5248/4313 +f 2341/5257/4322 2345/5249/4314 2342/5251/4316 +f 2342/5251/4316 2346/5250/4315 6118/5253/4318 +f 2345/5249/4314 6115/5254/4319 2346/5250/4315 +f 2346/5250/4315 6116/5255/4320 6117/5252/4317 +f 2343/5259/4324 2341/5257/4322 2347/5258/4323 +f 6121/5267/4332 2343/5259/4324 6120/5260/4325 +f 2347/5258/4323 2342/5251/4316 6119/5261/4326 +f 6120/5260/4325 2347/5258/4323 7760/5262/4327 +f 2341/5257/4322 2343/5259/4324 2344/5264/4329 +f 2344/5264/4329 2348/5263/4328 6124/5266/4331 +f 2343/5259/4324 6121/5267/4332 2348/5263/4328 +f 2348/5263/4328 6122/5268/4333 6123/5265/4330 +f 2345/5249/4314 2341/5257/4322 2349/5270/4335 +f 6115/5254/4319 2345/5249/4314 6114/5271/4336 +f 2349/5270/4335 2344/5264/4329 6125/5272/4337 +f 6114/5271/4336 2349/5270/4335 7789/5273/4338 +f 2350/5282/4347 2354/5274/4339 2351/5276/4341 +f 2351/5276/4341 2355/5275/4340 6130/5278/4343 +f 2354/5274/4339 6127/5279/4344 2355/5275/4340 +f 2355/5275/4340 6128/5280/4345 6129/5277/4342 +f 2352/5284/4349 2350/5282/4347 2356/5283/4348 +f 6118/5292/4318 2352/5284/4349 6119/5285/4326 +f 2356/5283/4348 2351/5276/4341 6131/5286/4350 +f 6119/5285/4326 2356/5283/4348 7760/5287/4327 +f 2350/5282/4347 2352/5284/4349 2353/5289/4352 +f 2353/5289/4352 2357/5288/4351 6133/5291/4354 +f 2352/5284/4349 6118/5292/4318 2357/5288/4351 +f 2357/5288/4351 6117/5293/4317 6132/5290/4353 +f 2354/5274/4339 2350/5282/4347 2358/5295/4355 +f 6127/5279/4344 2354/5274/4339 6126/5296/4356 +f 2358/5295/4355 2353/5289/4352 6134/5297/4357 +f 6126/5296/4356 2358/5295/4355 7790/5298/4358 +f 2359/5307/4367 2363/5299/4359 2360/5301/4361 +f 2360/5301/4361 2364/5300/4360 6139/5303/4363 +f 2363/5299/4359 6136/5304/4364 2364/5300/4360 +f 2364/5300/4360 6137/5305/4365 6138/5302/4362 +f 2361/5309/4369 2359/5307/4367 2365/5308/4368 +f 6115/5316/4319 2361/5309/4369 6116/5310/4320 +f 2365/5308/4368 2360/5301/4361 6140/5311/4370 +f 6116/5310/4320 2365/5308/4368 7759/5294/4321 +f 2359/5307/4367 2361/5309/4369 2362/5313/4372 +f 2362/5313/4372 2366/5312/4371 6142/5315/4374 +f 2361/5309/4369 6115/5316/4319 2366/5312/4371 +f 2366/5312/4371 6114/5317/4336 6141/5314/4373 +f 2363/5299/4359 2359/5307/4367 2367/5319/4375 +f 6136/5304/4364 2363/5299/4359 6135/5320/4376 +f 2367/5319/4375 2362/5313/4372 6143/5321/4377 +f 6135/5320/4376 2367/5319/4375 7787/5322/4378 +f 2368/5328/4384 2372/5323/4379 2369/5325/4381 +f 2369/5325/4381 2373/5324/4380 6133/5291/4354 +f 2372/5323/4379 6145/5326/4382 2373/5324/4380 +f 2373/5324/4380 6146/5327/4383 6134/5297/4357 +f 2370/5330/4386 2368/5328/4384 2374/5329/4385 +f 6139/5303/4363 2370/5330/4386 6140/5311/4370 +f 2374/5329/4385 2369/5325/4381 6132/5290/4353 +f 6140/5311/4370 2374/5329/4385 7759/5294/4321 +f 2368/5328/4384 2370/5330/4386 2371/5332/4388 +f 2371/5332/4388 2375/5331/4387 6148/5334/4390 +f 2370/5330/4386 6139/5303/4363 2375/5331/4387 +f 2375/5331/4387 6138/5302/4362 6147/5333/4389 +f 2372/5323/4379 2368/5328/4384 2376/5335/4391 +f 6145/5326/4382 2372/5323/4379 6144/5336/4392 +f 2376/5335/4391 2371/5332/4388 6149/5337/4393 +f 6144/5336/4392 2376/5335/4391 7788/5338/4394 +f 2377/5347/4403 2381/5339/4395 2378/5341/4397 +f 2378/5341/4397 2382/5340/4396 6154/5343/4399 +f 2381/5339/4395 6151/5344/4400 2382/5340/4396 +f 2382/5340/4396 6152/5345/4401 6153/5342/4398 +f 2379/5349/4405 2377/5347/4403 2383/5348/4404 +f 6136/5304/4364 2379/5349/4405 6137/5305/4365 +f 2383/5348/4404 2378/5341/4397 6155/5350/4406 +f 6137/5305/4365 2383/5348/4404 7758/5306/4366 +f 2377/5347/4403 2379/5349/4405 2380/5352/4408 +f 2380/5352/4408 2384/5351/4407 6157/5354/4410 +f 2379/5349/4405 6136/5304/4364 2384/5351/4407 +f 2384/5351/4407 6135/5320/4376 6156/5353/4409 +f 2381/5339/4395 2377/5347/4403 2385/5355/4411 +f 6151/5344/4400 2381/5339/4395 6150/5356/4412 +f 2385/5355/4411 2380/5352/4408 6158/5357/4413 +f 6150/5356/4412 2385/5355/4411 7791/5358/4414 +f 2386/5364/4420 2390/5359/4415 2387/5361/4417 +f 2387/5361/4417 2391/5360/4416 6148/5334/4390 +f 2390/5359/4415 6160/5362/4418 2391/5360/4416 +f 2391/5360/4416 6161/5363/4419 6149/5337/4393 +f 2388/5366/4422 2386/5364/4420 2392/5365/4421 +f 6154/5343/4399 2388/5366/4422 6155/5350/4406 +f 2392/5365/4421 2387/5361/4417 6147/5333/4389 +f 6155/5350/4406 2392/5365/4421 7758/5306/4366 +f 2386/5364/4420 2388/5366/4422 2389/5368/4424 +f 2389/5368/4424 2393/5367/4423 6163/5370/4426 +f 2388/5366/4422 6154/5343/4399 2393/5367/4423 +f 2393/5367/4423 6153/5342/4398 6162/5369/4425 +f 2390/5359/4415 2386/5364/4420 2394/5371/4427 +f 6160/5362/4418 2390/5359/4415 6159/5372/4428 +f 2394/5371/4427 2389/5368/4424 6164/5373/4429 +f 6159/5372/4428 2394/5371/4427 7792/5374/4430 +f 2396/5378/4434 2395/5375/4431 2400/5377/4433 +f 4822/5387/1000 2396/5378/4434 4823/5379/1001 +f 2400/5377/4433 2399/5376/4432 6167/5381/4436 +f 4823/5379/1001 2400/5377/4433 7540/5382/1002 +f 2395/5375/4431 2396/5378/4434 2397/5384/4438 +f 2397/5384/4438 2401/5383/4437 6169/5386/4440 +f 2396/5378/4434 4822/5387/1000 2401/5383/4437 +f 2401/5383/4437 4821/5388/1017 6168/5385/4439 +f 2398/5391/4442 2395/5375/4431 2402/5390/4441 +f 6151/5397/4400 2398/5391/4442 6152/5392/4401 +f 2402/5390/4441 2397/5384/4438 6170/5393/4443 +f 6152/5392/4401 2402/5390/4441 7757/5394/4402 +f 2395/5375/4431 2398/5391/4442 2399/5376/4432 +f 2399/5376/4432 2403/5395/4444 6166/5380/4435 +f 2398/5391/4442 6151/5397/4400 2403/5395/4444 +f 2403/5395/4444 6150/5398/4412 6165/5396/4445 +f 2405/5403/4449 2404/5400/4446 2409/5402/4448 +f 6169/5410/4440 2405/5403/4449 6170/5404/4443 +f 2409/5402/4448 2408/5401/4447 6162/5406/4425 +f 6170/5404/4443 2409/5402/4448 7757/5407/4402 +f 2404/5400/4446 2405/5403/4449 2406/5409/4451 +f 2406/5409/4451 2410/5408/4450 4840/1268/1034 +f 2405/5403/4449 6169/5410/4440 2410/5408/4450 +f 2410/5408/4450 6168/5411/4439 4841/1273/1039 +f 2407/5413/4453 2404/5400/4446 2411/5412/4452 +f 6172/5417/4456 2407/5413/4453 6171/5414/4454 +f 2411/5412/4452 2406/5409/4451 4839/1267/1033 +f 6171/5414/4454 2411/5412/4452 7541/1271/1037 +f 2404/5400/4446 2407/5413/4453 2408/5401/4447 +f 2408/5401/4447 2412/5415/4455 6163/5405/4426 +f 2407/5413/4453 6172/5417/4456 2412/5415/4455 +f 2412/5415/4455 6173/5418/4457 6164/5416/4429 +f 2414/5423/4461 2413/5420/4458 2418/5422/4460 +f 4843/5430/1045 2414/5423/4461 4844/5424/1046 +f 2418/5422/4460 2417/5421/4459 6176/5426/4463 +f 4844/5424/1046 2418/5422/4460 7538/5427/1047 +f 2413/5420/4458 2414/5423/4461 2415/5429/4465 +f 2415/5429/4465 2419/5428/4464 6166/5380/4435 +f 2414/5423/4461 4843/5430/1045 2419/5428/4464 +f 2419/5428/4464 4842/5431/1059 6167/5381/4436 +f 2416/5433/4467 2413/5420/4458 2420/5432/4466 +f 6178/5437/4471 2416/5433/4467 6177/5434/4468 +f 2420/5432/4466 2415/5429/4465 6165/5396/4445 +f 6177/5434/4468 2420/5432/4466 7791/5399/4414 +f 2413/5420/4458 2416/5433/4467 2417/5421/4459 +f 2417/5421/4459 2421/5435/4469 6175/5425/4462 +f 2416/5433/4467 6178/5437/4471 2421/5435/4469 +f 2421/5435/4469 6179/5438/4472 6174/5436/4470 +f 2423/5443/4477 2422/5440/4474 2427/5442/4476 +f 6172/5452/4456 2423/5443/4477 6173/5444/4457 +f 2427/5442/4476 2426/5441/4475 6182/5446/4479 +f 6173/5444/4457 2427/5442/4476 7792/5447/4430 +f 2422/5440/4474 2423/5443/4477 2424/5449/4481 +f 2424/5449/4481 2428/5448/4480 4858/5451/1074 +f 2423/5443/4477 6172/5452/4456 2428/5448/4480 +f 2428/5448/4480 6171/5453/4454 4859/5450/1079 +f 2425/5456/4483 2422/5440/4474 2429/5455/4482 +f 6184/5462/4487 2425/5456/4483 6183/5457/4484 +f 2429/5455/4482 2424/5449/4481 4857/5458/1073 +f 6183/5457/4484 2429/5455/4482 7539/5459/1077 +f 2422/5440/4474 2425/5456/4483 2426/5441/4475 +f 2426/5441/4475 2430/5460/4485 6181/5445/4478 +f 2425/5456/4483 6184/5462/4487 2430/5460/4485 +f 2430/5460/4485 6185/5463/4488 6180/5461/4486 +f 2432/5468/4493 2431/5465/4490 2436/5467/4492 +f 4861/5475/1085 2432/5468/4493 4862/5469/1086 +f 2436/5467/4492 2435/5466/4491 6188/5471/4495 +f 4862/5469/1086 2436/5467/4492 7536/5472/1087 +f 2431/5465/4490 2432/5468/4493 2433/5474/4497 +f 2433/5474/4497 2437/5473/4496 6175/5425/4462 +f 2432/5468/4493 4861/5475/1085 2437/5473/4496 +f 2437/5473/4496 4860/5476/1099 6176/5426/4463 +f 2434/5478/4499 2431/5465/4490 2438/5477/4498 +f 6190/5482/4503 2434/5478/4499 6189/5479/4500 +f 2438/5477/4498 2433/5474/4497 6174/5436/4470 +f 6189/5479/4500 2438/5477/4498 7785/5439/4473 +f 2431/5465/4490 2434/5478/4499 2435/5466/4491 +f 2435/5466/4491 2439/5480/4501 6187/5470/4494 +f 2434/5478/4499 6190/5482/4503 2439/5480/4501 +f 2439/5480/4501 6191/5483/4504 6186/5481/4502 +f 2441/5488/4509 2440/5485/4506 2445/5487/4508 +f 6184/5497/4487 2441/5488/4509 6185/5489/4488 +f 2445/5487/4508 2444/5486/4507 6194/5491/4511 +f 6185/5489/4488 2445/5487/4508 7786/5492/4489 +f 2440/5485/4506 2441/5488/4509 2442/5494/4513 +f 2442/5494/4513 2446/5493/4512 4876/5496/1114 +f 2441/5488/4509 6184/5497/4487 2446/5493/4512 +f 2446/5493/4512 6183/5498/4484 4877/5495/1119 +f 2443/5501/4515 2440/5485/4506 2447/5500/4514 +f 6196/5507/4519 2443/5501/4515 6195/5502/4516 +f 2447/5500/4514 2442/5494/4513 4875/5503/1113 +f 6195/5502/4516 2447/5500/4514 7537/5504/1116 +f 2440/5485/4506 2443/5501/4515 2444/5486/4507 +f 2444/5486/4507 2448/5505/4517 6193/5490/4510 +f 2443/5501/4515 6196/5507/4519 2448/5505/4517 +f 2448/5505/4517 6197/5508/4520 6192/5506/4518 +f 2450/5513/4525 2449/5510/4522 2454/5512/4524 +f 4879/5522/1125 2450/5513/4525 4880/5514/1126 +f 2454/5512/4524 2453/5511/4523 6200/5516/4527 +f 4880/5514/1126 2454/5512/4524 7534/5517/1127 +f 2449/5510/4522 2450/5513/4525 2451/5519/4529 +f 2451/5519/4529 2455/5518/4528 6187/5521/4494 +f 2450/5513/4525 4879/5522/1125 2455/5518/4528 +f 2455/5518/4528 4878/5523/1139 6188/5520/4495 +f 2452/5526/4531 2449/5510/4522 2456/5525/4530 +f 6202/5532/4535 2452/5526/4531 6201/5527/4532 +f 2456/5525/4530 2451/5519/4529 6186/5528/4502 +f 6201/5527/4532 2456/5525/4530 7781/5529/4505 +f 2449/5510/4522 2452/5526/4531 2453/5511/4523 +f 2453/5511/4523 2457/5530/4533 6199/5515/4526 +f 2452/5526/4531 6202/5532/4535 2457/5530/4533 +f 2457/5530/4533 6203/5533/4536 6198/5531/4534 +f 2459/5538/4541 2458/5535/4538 2463/5537/4540 +f 6196/5547/4519 2459/5538/4541 6197/5539/4520 +f 2463/5537/4540 2462/5536/4539 6206/5541/4543 +f 6197/5539/4520 2463/5537/4540 7782/5542/4521 +f 2458/5535/4538 2459/5538/4541 2460/5544/4545 +f 2460/5544/4545 2464/5543/4544 4894/5546/1154 +f 2459/5538/4541 6196/5547/4519 2464/5543/4544 +f 2464/5543/4544 6195/5548/4516 4895/5545/1159 +f 2461/5551/4547 2458/5535/4538 2465/5550/4546 +f 6208/5556/4551 2461/5551/4547 6207/5552/4548 +f 2465/5550/4546 2460/5544/4545 4893/5553/1153 +f 6207/5552/4548 2465/5550/4546 7535/1443/1156 +f 2458/5535/4538 2461/5551/4547 2462/5536/4539 +f 2462/5536/4539 2466/5554/4549 6205/5540/4542 +f 2461/5551/4547 6208/5556/4551 2466/5554/4549 +f 2466/5554/4549 6209/5557/4552 6204/5555/4550 +f 2467/5570/4562 2471/5559/4554 2468/5561/4556 +f 2468/5561/4556 2472/5560/4555 6211/5563/4558 +f 2471/5559/4554 6202/5564/4535 2472/5560/4555 +f 2472/5560/4555 6201/5565/4532 6210/5562/4557 +f 2467/5570/4562 2468/5561/4556 2469/5568/4560 +f 2469/5568/4560 2473/5567/4559 6142/5315/4374 +f 2468/5561/4556 6211/5563/4558 2473/5567/4559 +f 2473/5567/4559 6212/5569/4561 6143/5321/4377 +f 2470/5572/4564 2467/5570/4562 2474/5571/4563 +f 6214/5576/4567 2470/5572/4564 6213/5573/4565 +f 2474/5571/4563 2469/5568/4560 6141/5314/4373 +f 6213/5573/4565 2474/5571/4563 7789/5318/4338 +f 2471/5559/4554 2467/5570/4562 2475/5574/4566 +f 6202/5564/4535 2471/5559/4554 6203/5575/4536 +f 2475/5574/4566 2470/5572/4564 6215/5577/4568 +f 6203/5575/4536 2475/5574/4566 7783/5578/4537 +f 2477/5582/4572 2476/5579/4569 2481/5581/4571 +f 6145/5326/4382 2477/5582/4572 6146/5327/4383 +f 2481/5581/4571 2480/5580/4570 6218/5584/4574 +f 6146/5327/4383 2481/5581/4571 7790/5298/4358 +f 2476/5579/4569 2477/5582/4572 2478/5586/4576 +f 2478/5586/4576 2482/5585/4575 6220/5588/4578 +f 2477/5582/4572 6145/5326/4382 2482/5585/4575 +f 2482/5585/4575 6144/5336/4392 6219/5587/4577 +f 2476/5579/4569 2478/5586/4576 2479/5590/4580 +f 2479/5590/4580 2483/5589/4579 6205/5592/4542 +f 2478/5586/4576 6220/5588/4578 2483/5589/4579 +f 2483/5589/4579 6221/5593/4581 6206/5591/4543 +f 2480/5580/4570 2476/5579/4569 2484/5594/4582 +f 6217/5583/4573 2480/5580/4570 6216/5595/4583 +f 2484/5594/4582 2479/5590/4580 6204/5596/4550 +f 6216/5595/4583 2484/5594/4582 7784/5597/4553 +f 2486/5601/4587 2485/5598/4584 2490/5600/4586 +f 6190/5606/4503 2486/5601/4587 6191/5602/4504 +f 2490/5600/4586 2489/5599/4585 6210/5562/4557 +f 6191/5602/4504 2490/5600/4586 7781/5566/4505 +f 2487/5604/4589 2485/5598/4584 2491/5603/4588 +f 6178/5611/4471 2487/5604/4589 6179/5605/4472 +f 2486/5601/4587 6190/5606/4503 2491/5603/4588 +f 6179/5605/4472 2491/5603/4588 7785/5608/4473 +f 2485/5598/4584 2487/5604/4589 2488/5610/4591 +f 2488/5610/4591 2492/5609/4590 6157/5354/4410 +f 2487/5604/4589 6178/5611/4471 2492/5609/4590 +f 2492/5609/4590 6177/5612/4468 6158/5357/4413 +f 2489/5599/4585 2485/5598/4584 2493/5613/4592 +f 6211/5563/4558 2489/5599/4585 6212/5569/4561 +f 2493/5613/4592 2488/5610/4591 6156/5353/4409 +f 6212/5569/4561 2493/5613/4592 7787/5322/4378 +f 2494/5619/4596 2498/5614/4593 2495/5616/4595 +f 2495/5616/4595 2499/5615/4594 6181/5618/4478 +f 2498/5614/4593 6160/5362/4418 2499/5615/4594 +f 2499/5615/4594 6159/5372/4428 6182/5617/4479 +f 2496/5622/4598 2494/5619/4596 2500/5620/4597 +f 2496/5622/4598 2500/5620/4597 6193/5490/4510 +f 2500/5620/4597 2495/5616/4595 6180/5621/4486 +f 6194/5491/4511 2500/5620/4597 7786/5492/4489 +f 2497/5624/4600 2494/5619/4596 2501/5623/4599 +f 6220/5588/4578 2497/5624/4600 6221/5593/4581 +f 2501/5623/4599 2496/5622/4598 6192/5506/4518 +f 6221/5593/4581 2501/5623/4599 7782/5509/4521 +f 2498/5614/4593 2494/5619/4596 2502/5625/4601 +f 6160/5362/4418 2498/5614/4593 6161/5363/4419 +f 2502/5625/4601 2497/5624/4600 6219/5587/4577 +f 6161/5363/4419 2502/5625/4601 7788/5338/4394 +f 2503/5643/4615 2507/5626/4602 2504/5628/4604 +f 6226/5637/4611 2504/5628/4604 6225/5629/4605 +f 2507/5626/4602 6223/5630/4606 2508/5627/4603 +f 6225/5629/4605 2508/5627/4603 7777/5632/4608 +f 2503/5643/4615 2504/5628/4604 2505/5634/4610 +f 2505/5634/4610 2509/5633/4609 6214/5636/4567 +f 2504/5628/4604 6226/5637/4611 2509/5633/4609 +f 2509/5633/4609 6227/5638/4612 6215/5635/4568 +f 2503/5643/4615 2505/5634/4610 2506/5641/4614 +f 2506/5641/4614 2510/5640/4613 6124/5266/4331 +f 2505/5634/4610 6214/5636/4567 2510/5640/4613 +f 2510/5640/4613 6213/5642/4565 6125/5272/4337 +f 2507/5626/4602 2503/5643/4615 2511/5644/4616 +f 6223/5630/4606 2507/5626/4602 6222/5645/4617 +f 2511/5644/4616 2506/5641/4614 6123/5265/4330 +f 6222/5645/4617 2511/5644/4616 7779/5269/4334 +f 2512/5659/4631 2516/5646/4618 2513/5648/4620 +f 2513/5648/4620 2517/5647/4619 6217/5583/4573 +f 2516/5646/4618 6127/5279/4344 2517/5647/4619 +f 2517/5647/4619 6126/5296/4356 6218/5584/4574 +f 2512/5659/4631 2513/5648/4620 2514/5650/4622 +f 2514/5650/4622 2518/5649/4621 6229/5652/4624 +f 2513/5648/4620 6217/5583/4573 2518/5649/4621 +f 2518/5649/4621 6216/5595/4583 6228/5651/4623 +f 2512/5659/4631 2514/5650/4622 2515/5654/4626 +f 2515/5654/4626 2519/5653/4625 6232/5656/4628 +f 2519/5653/4625 2514/5650/4622 6230/5657/4629 +f 6231/5655/4627 2519/5653/4625 7778/5658/4630 +f 2516/5646/4618 2512/5659/4631 2520/5660/4632 +f 6127/5279/4344 2516/5646/4618 6128/5280/4345 +f 2520/5660/4632 2515/5654/4626 6233/5661/4633 +f 6128/5280/4345 2520/5660/4632 7780/5281/4346 +f 2522/5665/4637 2521/5662/4634 2526/5664/4636 +f 4897/5672/1165 2522/5665/4637 4898/5666/1166 +f 2526/5664/4636 2525/5663/4635 6236/5668/4639 +f 4898/5666/1166 2526/5664/4636 7532/5669/1167 +f 2521/5662/4634 2522/5665/4637 2523/5671/4641 +f 2523/5671/4641 2527/5670/4640 6199/5515/4526 +f 2522/5665/4637 4897/5672/1165 2527/5670/4640 +f 2527/5670/4640 4896/5673/1179 6200/5516/4527 +f 2524/5675/4643 2521/5662/4634 2528/5674/4642 +f 6226/5679/4611 2524/5675/4643 6227/5676/4612 +f 2528/5674/4642 2523/5671/4641 6198/5531/4534 +f 6227/5676/4612 2528/5674/4642 7783/5534/4537 +f 2521/5662/4634 2524/5675/4643 2525/5663/4635 +f 2525/5663/4635 2529/5677/4644 6235/5667/4638 +f 2524/5675/4643 6226/5679/4611 2529/5677/4644 +f 2529/5677/4644 6225/5680/4605 6234/5678/4645 +f 2531/5685/4649 2530/5682/4646 2535/5684/4648 +f 6208/5556/4551 2531/5685/4649 6209/5557/4552 +f 2535/5684/4648 2534/5683/4647 6228/5687/4623 +f 6209/5557/4552 2535/5684/4648 7784/5558/4553 +f 2530/5682/4646 2531/5685/4649 2532/5689/4651 +f 2532/5689/4651 2536/5688/4650 4912/1437/1195 +f 2531/5685/4649 6208/5556/4551 2536/5688/4650 +f 2536/5688/4650 6207/5552/4548 4913/1442/1199 +f 2533/5691/4653 2530/5682/4646 2537/5690/4652 +f 6238/5695/4656 2533/5691/4653 6237/5692/4654 +f 2537/5690/4652 2532/5689/4651 4911/1436/1194 +f 6237/5692/4654 2537/5690/4652 7533/1439/1197 +f 2530/5682/4646 2533/5691/4653 2534/5683/4647 +f 2534/5683/4647 2538/5693/4655 6229/5686/4624 +f 2533/5691/4653 6238/5695/4656 2538/5693/4655 +f 2538/5693/4655 6239/5696/4657 6230/5694/4629 +f 2540/5706/4663 2539/5698/4658 2544/5700/4660 +f 2540/5706/4663 2544/5700/4660 6241/5702/4662 +f 2543/5699/4659 5569/5703/2841 2544/5700/4660 +f 2544/5700/4660 5568/5704/2840 6240/5701/4661 +f 2539/5698/4658 2540/5706/4663 2541/5708/4665 +f 2541/5708/4665 2545/5707/4664 6244/5710/4667 +f 2540/5706/4663 6241/5702/4662 2545/5707/4664 +f 2545/5707/4664 6242/5711/4668 6243/5709/4666 +f 2542/5714/4671 2539/5698/4658 2546/5713/4670 +f 6247/5720/4676 2542/5714/4671 6246/5715/4672 +f 2546/5713/4670 2541/5708/4665 6245/5716/4673 +f 6246/5715/4672 2546/5713/4670 7795/5717/4674 +f 2539/5698/4658 2542/5714/4671 2543/5699/4659 +f 2543/5699/4659 2547/5718/4675 5569/5703/2841 +f 2542/5714/4671 6247/5720/4676 2547/5718/4675 +f 2547/5718/4675 6248/5721/4677 5570/5719/2846 +f 2549/5726/4681 2548/5723/4678 2553/5725/4680 +f 6253/5735/4690 2549/5726/4681 6252/5727/4682 +f 2553/5725/4680 2552/5724/4679 6251/5729/4684 +f 6252/5727/4682 2553/5725/4680 7796/5730/4685 +f 2548/5723/4678 2549/5726/4681 2550/5732/4687 +f 2550/5732/4687 2554/5731/4686 6256/5734/4689 +f 2549/5726/4681 6253/5735/4690 2554/5731/4686 +f 2554/5731/4686 6254/5736/4691 6255/5733/4688 +f 2551/5740/4695 2548/5723/4678 2555/5738/4693 +f 2551/5740/4695 2555/5738/4693 5578/3396/2866 +f 2550/5732/4687 6256/5734/4689 2555/5738/4693 +f 2555/5738/4693 6257/5739/4694 5579/3397/2867 +f 2548/5723/4678 2551/5740/4695 2552/5724/4679 +f 2552/5724/4679 2556/5741/4696 6250/5728/4683 +f 2551/5740/4695 5578/3396/2866 2556/5741/4696 +f 2556/5741/4696 5577/3391/2861 6249/5742/4697 +f 2557/5748/4701 2561/5743/4698 2558/5745/4700 +f 2558/5745/4700 2562/5744/4699 6235/5667/4638 +f 2561/5743/4698 5557/5746/2813 2562/5744/4699 +f 2562/5744/4699 5556/5747/2809 6236/5668/4639 +f 2559/5750/4703 2557/5748/4701 2563/5749/4702 +f 6259/5754/4707 2559/5750/4703 6258/5751/4704 +f 2563/5749/4702 2558/5745/4700 6234/5678/4645 +f 6258/5751/4704 2563/5749/4702 7777/5681/4608 +f 2557/5748/4701 2559/5750/4703 2560/5753/4706 +f 2560/5753/4706 2564/5752/4705 6241/5702/4662 +f 2559/5750/4703 6259/5754/4707 2564/5752/4705 +f 2564/5752/4705 6260/5755/4708 6242/5711/4668 +f 2561/5743/4698 2557/5748/4701 2565/5756/4709 +f 5557/5746/2813 2561/5743/4698 5558/5757/2814 +f 2565/5756/4709 2560/5753/4706 6240/5701/4661 +f 5558/5757/2814 2565/5756/4709 7660/5705/2815 +f 2566/5763/4715 2570/5758/4710 2567/5760/4712 +f 2567/5760/4712 2571/5759/4711 6262/5762/4714 +f 2570/5758/4710 6256/5734/4689 2571/5759/4711 +f 2571/5759/4711 6255/5733/4688 6261/5761/4713 +f 2568/5765/4717 2566/5763/4715 2572/5764/4716 +f 6238/5695/4656 2568/5765/4717 6239/5696/4657 +f 2572/5764/4716 2567/5760/4712 6263/5766/4718 +f 6239/5696/4657 2572/5764/4716 7778/5697/4630 +f 2566/5763/4715 2568/5765/4717 2569/5768/4720 +f 2569/5768/4720 2573/5767/4719 5566/3355/2829 +f 2568/5765/4717 6238/5695/4656 2573/5767/4719 +f 2573/5767/4719 6237/5692/4654 5567/3361/2835 +f 2570/5758/4710 2566/5763/4715 2574/5769/4721 +f 6256/5734/4689 2570/5758/4710 6257/5739/4694 +f 2574/5769/4721 2569/5768/4720 5565/3354/2828 +f 6257/5739/4694 2574/5769/4721 7661/3357/2831 +f 2575/5778/4727 2579/5770/4722 2576/5772/4724 +f 2576/5772/4724 2580/5771/4723 5581/5774/2873 +f 2580/5771/4723 2579/5770/4722 6266/5776/4726 +f 5582/5773/2874 2580/5771/4723 7530/5777/1207 +f 2577/5780/4729 2575/5778/4727 2581/5779/4728 +f 6247/5720/4676 2577/5780/4729 6248/5721/4677 +f 2581/5779/4728 2576/5772/4724 5580/5781/2880 +f 6248/5721/4677 2581/5779/4728 7666/5722/2847 +f 2575/5778/4727 2577/5780/4729 2578/5783/4731 +f 6268/5787/4735 2578/5783/4731 6267/5784/4732 +f 2577/5780/4729 6247/5720/4676 2582/5782/4730 +f 2582/5782/4730 6246/5715/4672 6267/5784/4732 +f 2575/5778/4727 2578/5783/4731 2579/5770/4722 +f 2579/5770/4722 2583/5785/4733 6265/5775/4725 +f 2578/5783/4731 6268/5787/4735 2583/5785/4733 +f 2583/5785/4733 6269/5788/4736 6264/5786/4734 +f 2584/5795/4743 2588/5790/4738 2585/5792/4740 +f 2585/5792/4740 2589/5791/4739 6250/5728/4683 +f 2589/5791/4739 2588/5790/4738 6272/5794/4742 +f 2589/5791/4739 6272/5794/4742 6251/5729/4684 +f 2586/5797/4745 2584/5795/4743 2590/5796/4744 +f 5584/3438/2891 2586/5797/4745 5585/3439/2892 +f 2590/5796/4744 2585/5792/4740 6249/5742/4697 +f 5585/3439/2892 2590/5796/4744 7667/3393/2863 +f 2584/5795/4743 2586/5797/4745 2587/5799/4747 +f 6274/5803/4751 2587/5799/4747 6273/5800/4748 +f 2586/5797/4745 5584/3438/2891 2591/5798/4746 +f 6273/5800/4748 2591/5798/4746 7531/3436/1244 +f 2584/5795/4743 2587/5799/4747 2588/5790/4738 +f 2588/5790/4738 2592/5801/4749 6271/5793/4741 +f 2587/5799/4747 6274/5803/4751 2592/5801/4749 +f 2592/5801/4749 6275/5804/4752 6270/5802/4750 +f 2593/5811/4757 2597/5806/4754 2594/5808/4756 +f 2594/5808/4756 2598/5807/4755 6265/5810/4725 +f 2597/5806/4754 4918/1456/1212 2598/5807/4755 +f 2598/5807/4755 4917/1448/1204 6266/5809/4726 +f 2595/5817/4761 2593/5811/4757 2599/5812/4758 +f 2595/5817/4761 2599/5812/4758 6277/5814/4760 +f 2599/5812/4758 2594/5808/4756 6264/5815/4734 +f 6276/5813/4759 2599/5812/4758 7797/5816/4737 +f 2593/5811/4757 2595/5817/4761 2596/5819/4763 +f 2596/5819/4763 2600/5818/4762 6103/5218/4289 +f 2600/5818/4762 2595/5817/4761 6278/5820/4764 +f 6104/5224/4293 2600/5818/4762 7751/5225/4294 +f 2597/5806/4754 2593/5811/4757 2601/5821/4765 +f 4918/1456/1212 2597/5806/4754 4919/1457/1213 +f 2601/5821/4765 2596/5819/4763 6102/5217/4288 +f 4919/1457/1213 2601/5821/4765 7543/1458/1214 +f 2602/5826/4770 2606/5822/4766 2603/5824/4768 +f 6280/5830/4773 2603/5824/4768 6279/5825/4769 +f 2606/5822/4766 6112/5241/4308 2607/5823/4767 +f 6279/5825/4769 2607/5823/4767 7752/5236/4305 +f 2604/5828/4772 2602/5826/4770 2608/5827/4771 +f 6274/5837/4751 2604/5828/4772 6275/5829/4752 +f 2603/5824/4768 6280/5830/4773 2608/5827/4771 +f 6275/5829/4752 2608/5827/4771 7798/5832/4753 +f 2602/5826/4770 2604/5828/4772 2605/5834/4776 +f 2605/5834/4776 2609/5833/4775 4933/5836/1236 +f 2604/5828/4772 6274/5837/4751 2609/5833/4775 +f 2609/5833/4775 6273/5838/4748 4934/5835/1243 +f 2606/5822/4766 2602/5826/4770 2610/5840/4777 +f 6112/5241/4308 2606/5822/4766 6113/5242/4309 +f 2610/5840/4777 2605/5834/4776 4932/5841/1235 +f 6113/5242/4309 2610/5840/4777 7544/5243/1239 +f 2611/5850/4786 2615/5842/4778 2612/5844/4780 +f 2612/5844/4780 2616/5843/4779 6286/5846/4782 +f 2615/5842/4778 6283/5847/4783 2616/5843/4779 +f 2616/5843/4779 6284/5848/4784 6285/5845/4781 +f 2613/5852/4788 2611/5850/4786 2617/5851/4787 +f 6289/5860/4796 2613/5852/4788 6288/5853/4789 +f 2617/5851/4787 2612/5844/4780 6287/5854/4790 +f 6288/5853/4789 2617/5851/4787 7775/5855/4791 +f 2611/5850/4786 2613/5852/4788 2614/5857/4793 +f 2614/5857/4793 2618/5856/4792 6292/5859/4795 +f 2613/5852/4788 6289/5860/4796 2618/5856/4792 +f 2618/5856/4792 6290/5861/4797 6291/5858/4794 +f 2615/5842/4778 2611/5850/4786 2619/5863/4799 +f 6283/5847/4783 2615/5842/4778 6282/5864/4800 +f 2619/5863/4799 2614/5857/4793 6293/5865/4801 +f 6282/5864/4800 2619/5863/4799 7755/5866/4802 +f 2620/5875/4811 2624/5867/4803 2621/5869/4805 +f 2621/5869/4805 2625/5868/4804 6298/5871/4807 +f 2624/5867/4803 6295/5872/4808 2625/5868/4804 +f 2625/5868/4804 6296/5873/4809 6297/5870/4806 +f 2622/5877/4813 2620/5875/4811 2626/5876/4812 +f 6301/5885/4819 2622/5877/4813 6300/5878/4814 +f 2626/5876/4812 2621/5869/4805 6299/5879/4815 +f 6300/5878/4814 2626/5876/4812 7776/5880/4816 +f 2620/5875/4811 2622/5877/4813 2623/5882/4818 +f 2623/5882/4818 2627/5881/4817 6283/5884/4783 +f 2622/5877/4813 6301/5885/4819 2627/5881/4817 +f 2627/5881/4817 6302/5886/4820 6284/5883/4784 +f 2624/5867/4803 2620/5875/4811 2628/5888/4821 +f 6295/5872/4808 2624/5867/4803 6294/5889/4822 +f 2628/5888/4821 2623/5882/4818 6282/5890/4800 +f 6294/5889/4822 2628/5888/4821 7755/5891/4802 +f 2629/5897/4828 2633/5892/4823 2630/5894/4825 +f 2630/5894/4825 2634/5893/4824 6292/5859/4795 +f 2633/5892/4823 6304/5895/4826 2634/5893/4824 +f 2634/5893/4824 6305/5896/4827 6293/5865/4801 +f 2631/5901/4832 2629/5897/4828 2635/5898/4829 +f 2631/5901/4832 2635/5898/4829 6307/5900/4831 +f 2635/5898/4829 2630/5894/4825 6291/5858/4794 +f 6306/5899/4830 2635/5898/4829 7803/5862/4798 +f 2629/5897/4828 2631/5901/4832 2632/5903/4834 +f 2632/5903/4834 2636/5902/4833 6310/5905/4836 +f 2636/5902/4833 2631/5901/4832 6308/5906/4837 +f 6309/5904/4835 2636/5902/4833 7801/5907/4838 +f 2633/5892/4823 2629/5897/4828 2637/5908/4839 +f 6304/5895/4826 2633/5892/4823 6303/5909/4840 +f 2637/5908/4839 2632/5903/4834 6311/5910/4841 +f 6303/5909/4840 2637/5908/4839 7529/5911/4842 +f 2638/5919/4850 2642/5912/4843 2639/5914/4845 +f 6316/5922/4853 2639/5914/4845 6315/5915/4846 +f 2642/5912/4843 6313/5916/4847 2643/5913/4844 +f 6315/5915/4846 2643/5913/4844 7802/5918/4849 +f 2640/5921/4852 2638/5919/4850 2644/5920/4851 +f 6295/5872/4808 2640/5921/4852 6296/5873/4809 +f 2639/5914/4845 6316/5922/4853 2644/5920/4851 +f 6296/5873/4809 2644/5920/4851 7804/5874/4810 +f 2638/5919/4850 2640/5921/4852 2641/5925/4856 +f 2641/5925/4856 2645/5924/4855 6304/5927/4826 +f 2640/5921/4852 6295/5872/4808 2645/5924/4855 +f 2645/5924/4855 6294/5889/4822 6305/5926/4827 +f 2642/5912/4843 2638/5919/4850 2646/5928/4857 +f 6313/5916/4847 2642/5912/4843 6312/5929/4858 +f 2646/5928/4857 2641/5925/4856 6303/5930/4840 +f 6312/5929/4858 2646/5928/4857 7529/5931/4842 +f 2648/5937/4864 2647/5932/4859 2652/5934/4861 +f 2648/5937/4864 2652/5934/4861 6310/5905/4836 +f 2651/5933/4860 6319/5935/4862 2652/5934/4861 +f 2652/5934/4861 6320/5936/4863 6311/5910/4841 +f 2647/5932/4859 2648/5937/4864 2649/5939/4866 +f 2649/5939/4866 2653/5938/4865 6322/5941/4868 +f 2653/5938/4865 2648/5937/4864 6309/5904/4835 +f 2653/5938/4865 6309/5904/4835 6321/5940/4867 +f 2650/5943/4870 2647/5932/4859 2654/5942/4869 +f 6325/5949/4876 2650/5943/4870 6324/5944/4871 +f 2654/5942/4869 2649/5939/4866 6323/5945/4872 +f 2654/5942/4869 6323/5945/4872 6324/5944/4871 +f 2647/5932/4859 2650/5943/4870 2651/5933/4860 +f 2651/5933/4860 2655/5947/4874 6319/5935/4862 +f 2650/5943/4870 6325/5949/4876 2655/5947/4874 +f 2655/5947/4874 6326/5950/4877 6318/5948/4875 +f 2657/5955/4882 2656/5952/4879 2661/5954/4881 +f 6331/5963/4889 2657/5955/4882 6330/5956/4883 +f 2661/5954/4881 2660/5953/4880 6329/5958/4885 +f 2661/5954/4881 6329/5958/4885 6330/5956/4883 +f 2656/5952/4879 2657/5955/4882 2658/5961/4888 +f 6313/5969/4847 2658/5961/4888 6314/5962/4848 +f 2657/5955/4882 6331/5963/4889 2662/5960/4887 +f 2662/5960/4887 6332/5964/4890 6314/5962/4848 +f 2659/5972/4892 2656/5952/4879 2663/5966/4891 +f 2659/5972/4892 2663/5966/4891 6319/5968/4862 +f 2658/5961/4888 6313/5969/4847 2663/5966/4891 +f 2663/5966/4891 6312/5970/4858 6320/5967/4863 +f 2656/5952/4879 2659/5972/4892 2660/5953/4880 +f 2660/5953/4880 2664/5973/4893 6328/5957/4884 +f 2659/5972/4892 6319/5968/4862 2664/5973/4893 +f 2664/5973/4893 6318/5975/4875 6327/5974/4894 +f 2666/5980/4898 2665/5977/4895 2670/5979/4897 +f 6325/5949/4876 2666/5980/4898 6326/5950/4877 +f 2670/5979/4897 2669/5978/4896 6335/5982/4900 +f 6326/5950/4877 2670/5979/4897 7756/5951/4878 +f 2665/5977/4895 2666/5980/4898 2667/5984/4902 +f 2667/5984/4902 2671/5983/4901 6337/5986/4904 +f 2666/5980/4898 6325/5949/4876 2671/5983/4901 +f 6336/5985/4903 2671/5983/4901 7799/5946/4873 +f 2668/5988/4906 2665/5977/4895 2672/5987/4905 +f 6121/5267/4332 2668/5988/4906 6122/5268/4333 +f 2672/5987/4905 2667/5984/4902 6338/5989/4907 +f 6122/5268/4333 2672/5987/4905 7779/5269/4334 +f 2665/5977/4895 2668/5988/4906 2669/5978/4896 +f 2669/5978/4896 2673/5990/4908 6334/5981/4899 +f 2668/5988/4906 6121/5267/4332 2673/5990/4908 +f 2673/5990/4908 6120/5260/4325 6333/5991/4909 +f 2675/5995/4913 2674/5992/4910 2679/5994/4912 +f 6340/6002/4917 2675/5995/4913 6339/5996/4914 +f 2679/5994/4912 2678/5993/4911 6129/5998/4342 +f 6339/5996/4914 2679/5994/4912 7780/5999/4346 +f 2674/5992/4910 2675/5995/4913 2676/6001/4916 +f 2676/6001/4916 2680/6000/4915 6328/5957/4884 +f 2675/5995/4913 6340/6002/4917 2680/6000/4915 +f 6329/5958/4885 2680/6000/4915 7800/5959/4886 +f 2677/6005/4920 2674/5992/4910 2681/6004/4919 +f 6334/6009/4899 2677/6005/4920 6335/6006/4900 +f 2681/6004/4919 2676/6001/4916 6327/5974/4894 +f 6335/6006/4900 2681/6004/4919 7756/5976/4878 +f 2674/5992/4910 2677/6005/4920 2678/5993/4911 +f 2678/5993/4911 2682/6007/4921 6130/5997/4343 +f 2677/6005/4920 6334/6009/4899 2682/6007/4921 +f 2682/6007/4921 6333/6010/4909 6131/6008/4350 +f 2683/6017/4925 2687/6012/4922 2684/6014/4924 +f 2684/6014/4924 2688/6013/4923 6223/5630/4606 +f 2687/6012/4922 6259/6015/4707 2688/6013/4923 +f 2688/6013/4923 6258/6016/4704 6224/5631/4607 +f 2685/6019/4927 2683/6017/4925 2689/6018/4926 +f 6337/5986/4904 2685/6019/4927 6338/5989/4907 +f 2689/6018/4926 2684/6014/4924 6222/5645/4617 +f 2689/6018/4926 6222/5645/4617 6338/5989/4907 +f 2683/6017/4925 2685/6019/4927 2686/6021/4929 +f 2686/6021/4929 2690/6020/4928 6343/6023/4931 +f 2685/6019/4927 6337/5986/4904 2690/6020/4928 +f 2690/6020/4928 6336/5985/4903 6342/6022/4930 +f 2687/6012/4922 2683/6017/4925 2691/6024/4932 +f 6259/6015/4707 2687/6012/4922 6260/6025/4708 +f 2691/6024/4932 2686/6021/4929 6344/6026/4933 +f 6260/6025/4708 2691/6024/4932 7793/6027/4669 +f 2692/6036/4939 2696/6028/4934 2693/6030/4936 +f 2693/6030/4936 2697/6029/4935 6340/6032/4917 +f 2696/6028/4934 6346/6033/4937 2697/6029/4935 +f 2697/6029/4935 6347/6034/4938 6341/6031/4918 +f 2694/6038/4941 2692/6036/4939 2698/6037/4940 +f 6232/6044/4628 2694/6038/4941 6233/6039/4633 +f 2698/6037/4940 2693/6030/4936 6339/6040/4914 +f 2698/6037/4940 6339/6040/4914 6233/6039/4633 +f 2692/6036/4939 2694/6038/4941 2695/6043/4943 +f 2695/6043/4943 2699/6042/4942 6262/5762/4714 +f 2694/6038/4941 6232/6044/4628 2699/6042/4942 +f 2699/6042/4942 6231/6045/4627 6263/5766/4718 +f 2696/6028/4934 2692/6036/4939 2700/6046/4944 +f 6346/6033/4937 2696/6028/4934 6345/6047/4945 +f 2700/6046/4944 2695/6043/4943 6261/5761/4713 +f 6345/6047/4945 2700/6046/4944 7794/5737/4692 +f 2702/6051/4949 2701/6048/4946 2706/6050/4948 +f 6094/5187/4264 2702/6051/4949 6095/5188/4265 +f 2706/6050/4948 2705/6049/4947 6350/6053/4951 +f 6095/5188/4265 2706/6050/4948 7747/5189/4231 +f 2701/6048/4946 2702/6051/4949 2703/6055/4953 +f 2703/6055/4953 2707/6054/4952 6352/6057/4955 +f 2702/6051/4949 6094/5187/4264 2707/6054/4952 +f 2707/6054/4952 6093/5182/4260 6351/6056/4954 +f 2704/6059/4957 2701/6048/4946 2708/6058/4956 +f 6355/6065/4963 2704/6059/4957 6354/6060/4958 +f 2708/6058/4956 2703/6055/4953 6353/6061/4959 +f 6354/6060/4958 2708/6058/4956 7769/6062/4960 +f 2701/6048/4946 2704/6059/4957 2705/6049/4947 +f 6349/6052/4950 2705/6049/4947 6348/6064/4962 +f 2704/6059/4957 6355/6065/4963 2709/6063/4961 +f 6348/6064/4962 2709/6063/4961 7761/6067/4965 +f 2711/6071/4969 2710/6068/4966 2715/6070/4968 +f 6361/6080/4976 2711/6071/4969 6360/6072/4970 +f 2715/6070/4968 2714/6069/4967 6359/6074/4972 +f 6360/6072/4970 2715/6070/4968 7770/6075/4973 +f 2710/6068/4966 2711/6071/4969 2712/6077/4975 +f 2712/6077/4975 2716/6076/4974 6097/6079/4271 +f 2711/6071/4969 6361/6080/4976 2716/6076/4974 +f 2716/6076/4974 6362/6081/4977 6098/6078/4272 +f 2713/6084/4979 2710/6068/4966 2717/6083/4978 +f 6364/6090/4983 2713/6084/4979 6363/6085/4980 +f 2717/6083/4978 2712/6077/4975 6096/6086/4281 +f 6363/6085/4980 2717/6083/4978 7748/6087/4241 +f 2710/6068/4966 2713/6084/4979 2714/6069/4967 +f 2714/6069/4967 2718/6088/4981 6358/6073/4971 +f 2718/6088/4981 2713/6084/4979 6365/6091/4984 +f 6357/6089/4982 2718/6088/4981 7762/6092/4985 +f 2719/6101/4994 2723/6093/4986 2720/6095/4988 +f 2720/6095/4988 2724/6094/4987 6370/6097/4990 +f 2723/6093/4986 6367/6098/4991 2724/6094/4987 +f 2724/6094/4987 6368/6099/4992 6369/6096/4989 +f 2721/6103/4996 2719/6101/4994 2725/6102/4995 +f 6373/6111/5002 2721/6103/4996 6372/6104/4997 +f 2725/6102/4995 2720/6095/4988 6371/6105/4998 +f 6372/6104/4997 2725/6102/4995 7767/6106/4999 +f 2719/6101/4994 2721/6103/4996 2722/6108/5001 +f 2722/6108/5001 2726/6107/5000 6286/6110/4782 +f 2721/6103/4996 6373/6111/5002 2726/6107/5000 +f 2726/6107/5000 6374/6112/5003 6287/6109/4790 +f 2723/6093/4986 2719/6101/4994 2727/6114/5004 +f 6367/6098/4991 2723/6093/4986 6366/6115/5005 +f 2727/6114/5004 2722/6108/5001 6285/6116/4781 +f 6366/6115/5005 2727/6114/5004 7528/5887/4785 +f 2728/6122/5011 2732/6117/5006 2729/6119/5008 +f 2729/6119/5008 2733/6118/5007 6376/6121/5010 +f 2732/6117/5006 6301/5885/4819 2733/6118/5007 +f 2733/6118/5007 6300/5878/4814 6375/6120/5009 +f 2730/6124/5013 2728/6122/5011 2734/6123/5012 +f 6379/6130/5019 2730/6124/5013 6378/6125/5014 +f 2734/6123/5012 2729/6119/5008 6377/6126/5015 +f 6378/6125/5014 2734/6123/5012 7768/6127/5016 +f 2728/6122/5011 2730/6124/5013 2731/6129/5018 +f 2731/6129/5018 2735/6128/5017 6367/6098/4991 +f 2730/6124/5013 6379/6130/5019 2735/6128/5017 +f 2735/6128/5017 6380/6131/5020 6368/6099/4992 +f 2732/6117/5006 2728/6122/5011 2736/6132/5021 +f 6301/5885/4819 2732/6117/5006 6302/5886/4820 +f 2736/6132/5021 2731/6129/5018 6366/6115/5005 +f 6302/5886/4820 2736/6132/5021 7528/5887/4785 +f 2737/6141/5030 2741/6133/5022 2738/6135/5024 +f 2738/6135/5024 2742/6134/5023 6385/6137/5026 +f 2741/6133/5022 6382/6138/5027 2742/6134/5023 +f 2742/6134/5023 6383/6139/5028 6384/6136/5025 +f 2739/6143/5032 2737/6141/5030 2743/6142/5031 +f 6388/6151/5040 2739/6143/5032 6387/6144/5033 +f 2743/6142/5031 2738/6135/5024 6386/6145/5034 +f 6387/6144/5033 2743/6142/5031 7809/6146/5035 +f 2737/6141/5030 2739/6143/5032 2740/6148/5037 +f 2740/6148/5037 2744/6147/5036 6391/6150/5039 +f 2739/6143/5032 6388/6151/5040 2744/6147/5036 +f 2744/6147/5036 6389/6152/5041 6390/6149/5038 +f 2741/6133/5022 2737/6141/5030 2745/6154/5043 +f 6382/6138/5027 2741/6133/5022 6381/6155/5044 +f 2740/6148/5037 6391/6150/5039 2745/6154/5043 +f 2745/6154/5043 6392/6156/5045 6381/6155/5044 +f 2746/6166/5055 2750/6158/5047 2747/6160/5049 +f 2747/6160/5049 2751/6159/5048 6397/6162/5051 +f 2750/6158/5047 6394/6163/5052 2751/6159/5048 +f 2751/6159/5048 6395/6164/5053 6396/6161/5050 +f 2748/6168/5057 2746/6166/5055 2752/6167/5056 +f 6400/6176/5063 2748/6168/5057 6399/6169/5058 +f 2752/6167/5056 2747/6160/5049 6398/6170/5059 +f 6399/6169/5058 2752/6167/5056 7810/6171/5060 +f 2746/6166/5055 2748/6168/5057 2749/6173/5062 +f 2749/6173/5062 2753/6172/5061 6382/6175/5027 +f 2748/6168/5057 6400/6176/5063 2753/6172/5061 +f 2753/6172/5061 6401/6177/5064 6383/6174/5028 +f 2750/6158/5047 2746/6166/5055 2754/6179/5065 +f 2750/6158/5047 2754/6179/5065 6394/6163/5052 +f 2754/6179/5065 2749/6173/5062 6381/6181/5044 +f 2754/6179/5065 6381/6181/5044 6393/6180/5066 +f 2756/6186/5070 2755/6183/5067 2760/6185/5069 +f 6391/6150/5039 2756/6186/5070 6392/6156/5045 +f 2760/6185/5069 2759/6184/5068 6404/6188/5072 +f 6392/6156/5045 2760/6185/5069 7754/6157/5046 +f 2755/6183/5067 2756/6186/5070 2757/6190/5074 +f 6406/6195/5079 2757/6190/5074 6405/6191/5075 +f 2756/6186/5070 6391/6150/5039 2761/6189/5073 +f 6405/6191/5075 2761/6189/5073 7807/6153/5042 +f 2758/6193/5077 2755/6183/5067 2762/6192/5076 +f 6409/6200/5084 2758/6193/5077 6408/6194/5078 +f 2757/6190/5074 6406/6195/5079 2762/6192/5076 +f 2762/6192/5076 6407/6196/5080 6408/6194/5078 +f 2755/6183/5067 2758/6193/5077 2759/6184/5068 +f 2759/6184/5068 2763/6198/5082 6403/6187/5071 +f 2758/6193/5077 6409/6200/5084 2763/6198/5082 +f 2763/6198/5082 6410/6201/5085 6402/6199/5083 +f 2765/6211/5095 2764/6203/5087 2769/6205/5089 +f 2765/6211/5095 2769/6205/5089 6415/6207/5091 +f 2769/6205/5089 2768/6204/5088 6413/6209/5093 +f 2769/6205/5089 6413/6209/5093 6414/6206/5090 +f 2764/6203/5087 2765/6211/5095 2766/6213/5097 +f 2766/6213/5097 2770/6212/5096 6394/6215/5052 +f 2770/6212/5096 2765/6211/5095 6416/6216/5098 +f 6395/6214/5053 2770/6212/5096 7808/6217/5054 +f 2767/6219/5100 2764/6203/5087 2771/6218/5099 +f 6403/6187/5071 2767/6219/5100 6404/6188/5072 +f 2771/6218/5099 2766/6213/5097 6393/6220/5066 +f 6404/6188/5072 2771/6218/5099 7754/6157/5046 +f 2764/6203/5087 2767/6219/5100 2768/6204/5088 +f 2768/6204/5088 2772/6221/5101 6412/6208/5092 +f 2767/6219/5100 6403/6187/5071 2772/6221/5101 +f 2772/6221/5101 6402/6199/5083 6411/6222/5102 +f 2774/6226/5106 2773/6223/5103 2778/6225/5105 +f 6409/6200/5084 2774/6226/5106 6410/6201/5085 +f 2778/6225/5105 2777/6224/5104 6419/6228/5108 +f 6410/6201/5085 2778/6225/5105 7526/6202/5086 +f 2773/6223/5103 2774/6226/5106 2775/6230/5110 +f 6421/6235/5113 2775/6230/5110 6420/6231/5111 +f 2774/6226/5106 6409/6200/5084 2779/6229/5109 +f 6420/6231/5111 2779/6229/5109 7805/6197/5081 +f 2776/6238/5115 2773/6223/5103 2780/6232/5112 +f 2776/6238/5115 2780/6232/5112 6370/6234/4990 +f 2775/6230/5110 6421/6235/5113 2780/6232/5112 +f 2780/6232/5112 6422/6236/5114 6371/6233/4998 +f 2773/6223/5103 2776/6238/5115 2777/6224/5104 +f 2777/6224/5104 2781/6239/5116 6418/6227/5107 +f 2781/6239/5116 2776/6238/5115 6369/6241/4989 +f 2781/6239/5116 6369/6241/4989 6417/6240/5117 +f 2783/6248/5123 2782/6243/5118 2787/6245/5120 +f 2783/6248/5123 2787/6245/5120 6424/6247/5122 +f 2786/6244/5119 6379/6130/5019 2787/6245/5120 +f 2787/6245/5120 6378/6125/5014 6423/6246/5121 +f 2782/6243/5118 2783/6248/5123 2784/6250/5125 +f 2784/6250/5125 2788/6249/5124 6412/6252/5092 +f 2788/6249/5124 2783/6248/5123 6425/6253/5126 +f 6413/6251/5093 2788/6249/5124 7806/6254/5094 +f 2785/6256/5128 2782/6243/5118 2789/6255/5127 +f 6418/6261/5107 2785/6256/5128 6419/6257/5108 +f 2789/6255/5127 2784/6250/5125 6411/6258/5102 +f 6419/6257/5108 2789/6255/5127 7526/6259/5086 +f 2782/6243/5118 2785/6256/5128 2786/6244/5119 +f 6379/6130/5019 2786/6244/5119 6380/6131/5020 +f 2785/6256/5128 6418/6261/5107 2790/6260/5129 +f 2790/6260/5129 6417/6262/5117 6380/6131/5020 +f 2792/6266/5133 2791/6263/5130 2796/6265/5132 +f 6070/5098/4196 2792/6266/5133 6071/5103/4201 +f 2796/6265/5132 2795/6264/5131 6428/6268/5135 +f 2796/6265/5132 6428/6268/5135 6071/5103/4201 +f 2793/6270/5137 2791/6263/5130 2797/6269/5136 +f 6430/6275/5142 2793/6270/5137 6429/6271/5138 +f 2797/6269/5136 2792/6266/5133 6069/5097/4195 +f 6429/6271/5138 2797/6269/5136 7745/5101/4199 +f 2794/6273/5140 2791/6263/5130 2798/6272/5139 +f 6433/6280/5147 2794/6273/5140 6432/6274/5141 +f 2793/6270/5137 6430/6275/5142 2798/6272/5139 +f 2798/6272/5139 6431/6276/5143 6432/6274/5141 +f 2791/6263/5130 2794/6273/5140 2795/6264/5131 +f 2795/6264/5131 2799/6278/5145 6427/6267/5134 +f 2794/6273/5140 6433/6280/5147 2799/6278/5145 +f 2799/6278/5145 6434/6281/5148 6426/6279/5146 +f 2801/6291/5158 2800/6283/5150 2805/6285/5152 +f 2801/6291/5158 2805/6285/5152 6439/6287/5154 +f 2805/6285/5152 2804/6284/5151 6437/6289/5156 +f 2805/6285/5152 6437/6289/5156 6438/6286/5153 +f 2802/6293/5160 2800/6283/5150 2806/6292/5159 +f 6073/5109/4207 2802/6293/5160 6074/5110/4208 +f 2806/6292/5159 2801/6291/5158 6440/6294/5161 +f 6074/5110/4208 2806/6292/5159 7746/5111/4209 +f 2803/6296/5163 2800/6283/5150 2807/6295/5162 +f 6442/6300/5167 2803/6296/5163 6441/6297/5164 +f 2807/6295/5162 2802/6293/5160 6072/5119/4217 +f 2807/6295/5162 6072/5119/4217 6441/6297/5164 +f 2800/6283/5150 2803/6296/5163 2804/6284/5151 +f 2804/6284/5151 2808/6298/5165 6436/6288/5155 +f 2803/6296/5163 6442/6300/5167 2808/6298/5165 +f 2808/6298/5165 6443/6301/5168 6435/6299/5166 +f 2810/6306/5173 2809/6303/5170 2814/6305/5172 +f 6433/6315/5147 2810/6306/5173 6434/6307/5148 +f 2814/6305/5172 2813/6304/5171 6446/6309/5175 +f 6434/6307/5148 2814/6305/5172 7815/6310/5149 +f 2809/6303/5170 2810/6306/5173 2811/6312/5177 +f 2811/6312/5177 2815/6311/5176 6448/6314/5179 +f 2810/6306/5173 6433/6315/5147 2815/6311/5176 +f 2815/6311/5176 6432/6316/5141 6447/6313/5178 +f 2812/6319/5181 2809/6303/5170 2816/6318/5180 +f 6451/6325/5187 2812/6319/5181 6450/6320/5182 +f 2816/6318/5180 2811/6312/5177 6449/6321/5183 +f 6450/6320/5182 2816/6318/5180 7819/6322/5184 +f 2809/6303/5170 2812/6319/5181 2813/6304/5171 +f 2813/6304/5171 2817/6323/5185 6445/6308/5174 +f 2812/6319/5181 6451/6325/5187 2817/6323/5185 +f 2817/6323/5185 6452/6326/5188 6444/6324/5186 +f 2819/6331/5193 2818/6328/5190 2823/6330/5192 +f 6457/6338/5200 2819/6331/5193 6456/6332/5194 +f 2823/6330/5192 2822/6329/5191 6455/6334/5196 +f 6456/6332/5194 2823/6330/5192 7820/6335/5197 +f 2818/6328/5190 2819/6331/5193 2820/6337/5199 +f 2820/6337/5199 2824/6336/5198 6436/6288/5155 +f 2819/6331/5193 6457/6338/5200 2824/6336/5198 +f 2824/6336/5198 6458/6339/5201 6437/6289/5156 +f 2821/6341/5203 2818/6328/5190 2825/6340/5202 +f 6460/6345/5207 2821/6341/5203 6459/6342/5204 +f 2825/6340/5202 2820/6337/5199 6435/6299/5166 +f 6459/6342/5204 2825/6340/5202 7816/6302/5169 +f 2818/6328/5190 2821/6341/5203 2822/6329/5191 +f 2822/6329/5191 2826/6343/5205 6454/6333/5195 +f 2821/6341/5203 6460/6345/5207 2826/6343/5205 +f 2826/6343/5205 6461/6346/5208 6453/6344/5206 +f 2828/6351/5213 2827/6348/5210 2832/6350/5212 +f 6451/6360/5187 2828/6351/5213 6452/6352/5188 +f 2832/6350/5212 2831/6349/5211 6464/6354/5215 +f 6452/6352/5188 2832/6350/5212 7817/6355/5189 +f 2827/6348/5210 2828/6351/5213 2829/6357/5217 +f 2829/6357/5217 2833/6356/5216 6466/6359/5219 +f 2828/6351/5213 6451/6360/5187 2833/6356/5216 +f 2833/6356/5216 6450/6361/5182 6465/6358/5218 +f 2830/6364/5221 2827/6348/5210 2834/6363/5220 +f 6469/6370/5227 2830/6364/5221 6468/6365/5222 +f 2834/6363/5220 2829/6357/5217 6467/6366/5223 +f 6468/6365/5222 2834/6363/5220 7821/6367/5224 +f 2827/6348/5210 2830/6364/5221 2831/6349/5211 +f 2831/6349/5211 2835/6368/5225 6463/6353/5214 +f 2830/6364/5221 6469/6370/5227 2835/6368/5225 +f 2835/6368/5225 6470/6371/5228 6462/6369/5226 +f 2837/6376/5233 2836/6373/5230 2841/6375/5232 +f 6475/6383/5240 2837/6376/5233 6474/6377/5234 +f 2841/6375/5232 2840/6374/5231 6473/6379/5236 +f 6474/6377/5234 2841/6375/5232 7822/6380/5237 +f 2836/6373/5230 2837/6376/5233 2838/6382/5239 +f 2838/6382/5239 2842/6381/5238 6454/6333/5195 +f 2837/6376/5233 6475/6383/5240 2842/6381/5238 +f 2842/6381/5238 6476/6384/5241 6455/6334/5196 +f 2839/6386/5243 2836/6373/5230 2843/6385/5242 +f 6478/6390/5247 2839/6386/5243 6477/6387/5244 +f 2843/6385/5242 2838/6382/5239 6453/6344/5206 +f 6477/6387/5244 2843/6385/5242 7818/6347/5209 +f 2836/6373/5230 2839/6386/5243 2840/6374/5231 +f 2840/6374/5231 2844/6388/5245 6472/6378/5235 +f 2839/6386/5243 6478/6390/5247 2844/6388/5245 +f 2844/6388/5245 6479/6391/5248 6471/6389/5246 +f 2845/6408/5265 2849/6393/5250 2846/6395/5252 +f 6469/6370/5227 2846/6395/5252 6470/6371/5228 +f 2849/6393/5250 6481/6396/5253 2850/6394/5251 +f 2850/6394/5251 6482/6397/5254 6470/6371/5228 +f 2845/6408/5265 2846/6395/5252 2847/6399/5256 +f 6484/6405/5262 2847/6399/5256 6483/6400/5257 +f 2846/6395/5252 6469/6370/5227 2851/6398/5255 +f 6483/6400/5257 2851/6398/5255 7821/6367/5224 +f 2845/6408/5265 2847/6399/5256 2848/6402/5259 +f 2848/6402/5259 2852/6401/5258 6487/6404/5261 +f 2847/6399/5256 6484/6405/5262 2852/6401/5258 +f 2852/6401/5258 6485/6406/5263 6486/6403/5260 +f 2849/6393/5250 2845/6408/5265 2853/6409/5266 +f 6481/6396/5253 2849/6393/5250 6480/6410/5267 +f 2853/6409/5266 2848/6402/5259 6488/6411/5268 +f 6480/6410/5267 2853/6409/5266 7825/6412/5269 +f 2854/6428/5285 2858/6413/5270 2855/6415/5272 +f 2855/6415/5272 2859/6414/5271 6493/6417/5274 +f 2858/6413/5270 6490/6418/5275 2859/6414/5271 +f 2859/6414/5271 6491/6419/5276 6492/6416/5273 +f 2854/6428/5285 2855/6415/5272 2856/6422/5279 +f 2856/6422/5279 2860/6421/5278 6472/6378/5235 +f 2860/6421/5278 2855/6415/5272 6494/6423/5280 +f 6473/6379/5236 2860/6421/5278 7822/6380/5237 +f 2854/6428/5285 2856/6422/5279 2857/6425/5282 +f 2857/6425/5282 2861/6424/5281 6496/6427/5284 +f 2861/6424/5281 2856/6422/5279 6471/6389/5246 +f 2861/6424/5281 6471/6389/5246 6495/6426/5283 +f 2858/6413/5270 2854/6428/5285 2862/6429/5286 +f 6490/6418/5275 2858/6413/5270 6489/6430/5287 +f 2862/6429/5286 2857/6425/5282 6497/6431/5288 +f 6489/6430/5287 2862/6429/5286 7826/6432/5289 +f 2863/6440/5297 2867/6433/5290 2864/6435/5292 +f 6502/6444/5301 2864/6435/5292 6501/6436/5293 +f 2868/6434/5291 2867/6433/5290 6500/6438/5295 +f 6501/6436/5293 2868/6434/5291 7765/6439/5296 +f 2865/6442/5299 2863/6440/5297 2869/6441/5298 +f 6505/6449/5306 2865/6442/5299 6504/6443/5300 +f 2869/6441/5298 2864/6435/5292 6503/6445/5302 +f 6504/6443/5300 2869/6441/5298 7829/6446/5303 +f 2863/6440/5297 2865/6442/5299 2866/6448/5305 +f 2866/6448/5305 2870/6447/5304 6487/6404/5261 +f 2865/6442/5299 6505/6449/5306 2870/6447/5304 +f 2870/6447/5304 6506/6450/5307 6488/6411/5268 +f 2867/6433/5290 2863/6440/5297 2871/6451/5308 +f 2867/6433/5290 2871/6451/5308 6499/6437/5294 +f 2871/6451/5308 2866/6448/5305 6486/6403/5260 +f 6498/6452/5309 2871/6451/5308 7827/6407/5264 +f 2872/6461/5315 2876/6453/5310 2873/6455/5312 +f 2873/6455/5312 2877/6454/5311 6508/6457/5314 +f 2876/6453/5310 6490/6458/5275 2877/6454/5311 +f 2877/6454/5311 6489/6459/5287 6507/6456/5313 +f 2874/6463/5317 2872/6461/5315 2878/6462/5316 +f 6511/6470/5324 2874/6463/5317 6510/6464/5318 +f 2878/6462/5316 2873/6455/5312 6509/6465/5319 +f 6510/6464/5318 2878/6462/5316 7830/6466/5320 +f 2872/6461/5315 2874/6463/5317 2875/6468/5322 +f 6514/6475/5328 2875/6468/5322 6513/6469/5323 +f 2879/6467/5321 2874/6463/5317 6512/6471/5325 +f 6513/6469/5323 2879/6467/5321 7766/6472/5326 +f 2876/6453/5310 2872/6461/5315 2880/6473/5327 +f 6490/6458/5275 2876/6453/5310 6491/6474/5276 +f 2875/6468/5322 6514/6475/5328 2880/6473/5327 +f 6491/6474/5276 2880/6473/5327 7828/6477/5277 +f 2882/6481/5333 2881/6478/5330 2886/6480/5332 +f 6421/6235/5113 2882/6481/5333 6422/6236/5114 +f 2886/6480/5332 2885/6479/5331 6518/6483/5335 +f 6422/6236/5114 2886/6480/5332 7767/6237/4999 +f 2881/6478/5330 2882/6481/5333 2883/6485/5337 +f 2883/6485/5337 2887/6484/5336 6520/6487/5339 +f 2882/6481/5333 6421/6235/5113 2887/6484/5336 +f 2887/6484/5336 6420/6231/5111 6519/6486/5338 +f 2884/6489/5341 2881/6478/5330 2888/6488/5340 +f 6505/6449/5306 2884/6489/5341 6506/6450/5307 +f 2888/6488/5340 2883/6485/5337 6521/6490/5342 +f 6506/6450/5307 2888/6488/5340 7825/6412/5269 +f 2881/6478/5330 2884/6489/5341 2885/6479/5331 +f 2885/6479/5331 2889/6491/5343 6517/6482/5334 +f 2884/6489/5341 6505/6449/5306 2889/6491/5343 +f 2889/6491/5343 6504/6443/5300 6516/6492/5344 +f 2891/6496/5348 2890/6493/5345 2895/6495/5347 +f 6523/6500/5352 2891/6496/5348 6522/6497/5349 +f 2895/6495/5347 2894/6494/5346 6507/6456/5313 +f 6522/6497/5349 2895/6495/5347 7826/6460/5289 +f 2890/6493/5345 2891/6496/5348 2892/6499/5351 +f 2892/6499/5351 2896/6498/5350 6424/6247/5122 +f 2891/6496/5348 6523/6500/5352 2896/6498/5350 +f 2896/6498/5350 6524/6501/5353 6425/6253/5126 +f 2893/6503/5355 2890/6493/5345 2897/6502/5354 +f 6526/6506/5358 2893/6503/5355 6525/6504/5356 +f 2897/6502/5354 2892/6499/5351 6423/6246/5121 +f 6525/6504/5356 2897/6502/5354 7768/6127/5016 +f 2890/6493/5345 2893/6503/5355 2894/6494/5346 +f 2894/6494/5346 2898/6505/5357 6508/6457/5314 +f 2893/6503/5355 6526/6506/5358 2898/6505/5357 +f 2898/6505/5357 6527/6507/5359 6509/6465/5319 +f 2900/6511/5363 2899/6508/5360 2904/6510/5362 +f 6406/6195/5079 2900/6511/5363 6407/6196/5080 +f 2904/6510/5362 2903/6509/5361 6519/6486/5338 +f 6407/6196/5080 2904/6510/5362 7805/6197/5081 +f 2899/6508/5360 2900/6511/5363 2901/6513/5365 +f 2901/6513/5365 2905/6512/5364 6529/6515/5367 +f 2900/6511/5363 6406/6195/5079 2905/6512/5364 +f 2905/6512/5364 6405/6191/5075 6528/6514/5366 +f 2902/6517/5369 2899/6508/5360 2906/6516/5368 +f 6481/6396/5253 2902/6517/5369 6482/6397/5254 +f 2906/6516/5368 2901/6513/5365 6530/6518/5370 +f 6482/6397/5254 2906/6516/5368 7823/6372/5229 +f 2899/6508/5360 2902/6517/5369 2903/6509/5361 +f 2903/6509/5361 2907/6519/5371 6520/6487/5339 +f 2902/6517/5369 6481/6396/5253 2907/6519/5371 +f 2907/6519/5371 6480/6410/5267 6521/6490/5342 +f 2909/6523/5375 2908/6520/5372 2913/6522/5374 +f 6532/6529/5379 2909/6523/5375 6531/6524/5376 +f 2913/6522/5374 2912/6521/5373 6495/6426/5283 +f 6531/6524/5376 2913/6522/5374 7824/6392/5249 +f 2908/6520/5372 2909/6523/5375 2910/6526/5378 +f 2910/6526/5378 2914/6525/5377 6415/6528/5091 +f 2909/6523/5375 6532/6529/5379 2914/6525/5377 +f 2914/6525/5377 6533/6530/5380 6416/6527/5098 +f 2911/6532/5382 2908/6520/5372 2915/6531/5381 +f 6523/6537/5352 2911/6532/5382 6524/6533/5353 +f 2915/6531/5381 2910/6526/5378 6414/6534/5090 +f 6524/6533/5353 2915/6531/5381 7806/6535/5094 +f 2908/6520/5372 2911/6532/5382 2912/6521/5373 +f 2912/6521/5373 2916/6536/5383 6496/6427/5284 +f 2911/6532/5382 6523/6537/5352 2916/6536/5383 +f 2916/6536/5383 6522/6538/5349 6497/6431/5288 +f 2918/6542/5387 2917/6539/5384 2922/6541/5386 +f 6388/6151/5040 2918/6542/5387 6389/6152/5041 +f 2922/6541/5386 2921/6540/5385 6528/6514/5366 +f 6389/6152/5041 2922/6541/5386 7807/6153/5042 +f 2917/6539/5384 2918/6542/5387 2919/6544/5389 +f 2919/6544/5389 2923/6543/5388 6535/6546/5391 +f 2918/6542/5387 6388/6151/5040 2923/6543/5388 +f 2923/6543/5388 6387/6144/5033 6534/6545/5390 +f 2920/6548/5393 2917/6539/5384 2924/6547/5392 +f 6463/6353/5214 2920/6548/5393 6464/6354/5215 +f 2924/6547/5392 2919/6544/5389 6536/6549/5394 +f 6464/6354/5215 2924/6547/5392 7817/6355/5189 +f 2917/6539/5384 2920/6548/5393 2921/6540/5385 +f 2921/6540/5385 2925/6550/5395 6529/6515/5367 +f 2920/6548/5393 6463/6353/5214 2925/6550/5395 +f 2925/6550/5395 6462/6369/5226 6530/6518/5370 +f 2927/6554/5399 2926/6551/5396 2931/6553/5398 +f 6538/6558/5403 2927/6554/5399 6537/6555/5400 +f 2931/6553/5398 2930/6552/5397 6477/6387/5244 +f 6537/6555/5400 2931/6553/5398 7818/6347/5209 +f 2926/6551/5396 2927/6554/5399 2928/6557/5402 +f 2928/6557/5402 2932/6556/5401 6397/6162/5051 +f 2927/6554/5399 6538/6558/5403 2932/6556/5401 +f 2932/6556/5401 6539/6559/5404 6398/6170/5059 +f 2929/6561/5406 2926/6551/5396 2933/6560/5405 +f 6532/6529/5379 2929/6561/5406 6533/6530/5380 +f 2933/6560/5405 2928/6557/5402 6396/6161/5050 +f 6533/6530/5380 2933/6560/5405 7808/6165/5054 +f 2926/6551/5396 2929/6561/5406 2930/6552/5397 +f 2930/6552/5397 2934/6562/5407 6478/6390/5247 +f 2929/6561/5406 6532/6529/5379 2934/6562/5407 +f 2934/6562/5407 6531/6524/5376 6479/6391/5248 +f 2936/6566/5411 2935/6563/5408 2940/6565/5410 +f 6541/6575/5417 2936/6566/5411 6540/6567/5412 +f 2940/6565/5410 2939/6564/5409 6534/6569/5390 +f 6540/6567/5412 2940/6565/5410 7809/6570/5035 +f 2935/6563/5408 2936/6566/5411 2937/6572/5414 +f 2937/6572/5414 2941/6571/5413 6544/6574/5416 +f 2936/6566/5411 6541/6575/5417 2941/6571/5413 +f 2941/6571/5413 6542/6576/5418 6543/6573/5415 +f 2938/6580/5422 2935/6563/5408 2942/6578/5420 +f 2938/6580/5422 2942/6578/5420 6445/6308/5174 +f 2942/6578/5420 2937/6572/5414 6545/6579/5421 +f 6446/6309/5175 2942/6578/5420 7815/6310/5149 +f 2935/6563/5408 2938/6580/5422 2939/6564/5409 +f 2939/6564/5409 2943/6581/5423 6535/6568/5391 +f 2943/6581/5423 2938/6580/5422 6444/6324/5186 +f 6536/6582/5394 2943/6581/5423 7817/6327/5189 +f 2945/6586/5427 2944/6583/5424 2949/6585/5426 +f 6547/6592/5433 2945/6586/5427 6546/6587/5428 +f 2948/6584/5425 6460/6345/5207 2949/6585/5426 +f 6546/6587/5428 2949/6585/5426 7816/6302/5169 +f 2944/6583/5424 2945/6586/5427 2946/6589/5430 +f 2946/6589/5430 2950/6588/5429 6550/6591/5432 +f 2945/6586/5427 6547/6592/5433 2950/6588/5429 +f 2950/6588/5429 6548/6593/5434 6549/6590/5431 +f 2947/6596/5437 2944/6583/5424 2951/6595/5436 +f 6538/6558/5403 2947/6596/5437 6539/6559/5404 +f 2951/6595/5436 2946/6589/5430 6551/6597/5438 +f 6539/6559/5404 2951/6595/5436 7810/6171/5060 +f 2944/6583/5424 2947/6596/5437 2948/6584/5425 +f 6460/6345/5207 2948/6584/5425 6461/6346/5208 +f 2947/6596/5437 6538/6558/5403 2952/6598/5439 +f 6461/6346/5208 2952/6598/5439 7818/6347/5209 +f 2953/6604/5445 2957/6599/5440 2954/6601/5442 +f 2954/6601/5442 2958/6600/5441 6058/5062/4164 +f 2957/6599/5440 6553/6602/5443 2958/6600/5441 +f 2958/6600/5441 6554/6603/5444 6059/5067/4169 +f 2955/6606/5447 2953/6604/5445 2959/6605/5446 +f 6427/6267/5134 2955/6606/5447 6428/6268/5135 +f 2959/6605/5446 2954/6601/5442 6057/5061/4163 +f 6428/6268/5135 2959/6605/5446 7743/5065/4167 +f 2953/6604/5445 2955/6606/5447 2956/6608/5449 +f 2956/6608/5449 2960/6607/5448 6544/6610/5416 +f 2955/6606/5447 6427/6267/5134 2960/6607/5448 +f 2960/6607/5448 6426/6279/5146 6545/6609/5421 +f 2957/6599/5440 2953/6604/5445 2961/6611/5450 +f 6553/6602/5443 2957/6599/5440 6552/6612/5451 +f 2961/6611/5450 2956/6608/5449 6543/6613/5415 +f 6552/6612/5451 2961/6611/5450 7811/6614/5419 +f 2962/6618/5455 2966/6615/5452 2963/6617/5454 +f 2963/6617/5454 2967/6616/5453 6442/6300/5167 +f 2966/6615/5452 6547/6592/5433 2967/6616/5453 +f 2967/6616/5453 6546/6587/5428 6443/6301/5168 +f 2964/6620/5457 2962/6618/5455 2968/6619/5456 +f 6061/5073/4175 2964/6620/5457 6062/5074/4176 +f 2968/6619/5456 2963/6617/5454 6441/6297/5164 +f 6062/5074/4176 2968/6619/5456 7744/5075/4177 +f 2962/6618/5455 2964/6620/5457 2965/6622/5459 +f 2965/6622/5459 2969/6621/5458 6556/6624/5461 +f 2964/6620/5457 6061/5073/4175 2969/6621/5458 +f 2969/6621/5458 6060/5083/4185 6555/6623/5460 +f 2966/6615/5452 2962/6618/5455 2970/6625/5462 +f 6547/6592/5433 2966/6615/5452 6548/6593/5434 +f 2970/6625/5462 2965/6622/5459 6557/6626/5463 +f 6548/6593/5434 2970/6625/5462 7812/6594/5435 +f 2971/6635/5472 2975/6627/5464 2972/6629/5466 +f 2972/6629/5466 2976/6628/5465 6562/6631/5468 +f 2975/6627/5464 6559/6632/5469 2976/6628/5465 +f 2976/6628/5465 6560/6633/5470 6561/6630/5467 +f 2973/6637/5474 2971/6635/5472 2977/6636/5473 +f 6541/6575/5417 2973/6637/5474 6542/6576/5418 +f 2977/6636/5473 2972/6629/5466 6563/6638/5475 +f 6542/6576/5418 2977/6636/5473 7811/6577/5419 +f 2971/6635/5472 2973/6637/5474 2974/6640/5477 +f 2974/6640/5477 2978/6639/5476 6385/6642/5026 +f 2973/6637/5474 6541/6575/5417 2978/6639/5476 +f 2978/6639/5476 6540/6567/5412 6386/6641/5034 +f 2975/6627/5464 2971/6635/5472 2979/6643/5478 +f 6559/6632/5469 2975/6627/5464 6558/6644/5479 +f 2979/6643/5478 2974/6640/5477 6384/6645/5025 +f 6558/6644/5479 2979/6643/5478 7753/6646/5029 +f 2980/6655/5483 2984/6647/5480 2981/6649/5482 +f 2981/6649/5482 2985/6648/5481 6550/6651/5432 +f 2984/6647/5480 6400/6652/5063 2985/6648/5481 +f 2985/6648/5481 6399/6653/5058 6551/6650/5438 +f 2982/6657/5485 2980/6655/5483 2986/6656/5484 +f 6565/6663/5489 2982/6657/5485 6564/6658/5486 +f 2986/6656/5484 2981/6649/5482 6549/6659/5431 +f 6564/6658/5486 2986/6656/5484 7812/6660/5435 +f 2980/6655/5483 2982/6657/5485 2983/6662/5488 +f 2983/6662/5488 2987/6661/5487 6559/6632/5469 +f 2982/6657/5485 6565/6663/5489 2987/6661/5487 +f 2987/6661/5487 6566/6664/5490 6560/6633/5470 +f 2984/6647/5480 2980/6655/5483 2988/6665/5491 +f 6400/6652/5063 2984/6647/5480 6401/6666/5064 +f 2988/6665/5491 2983/6662/5488 6558/6644/5479 +f 6401/6666/5064 2988/6665/5491 7753/6646/5029 +f 2989/6675/5497 2993/6667/5492 2990/6669/5494 +f 2990/6669/5494 2994/6668/5493 6022/6671/4068 +f 2993/6667/5492 6568/6672/5495 2994/6668/5493 +f 2994/6668/5493 6569/6673/5496 6023/6670/4073 +f 2991/6677/5499 2989/6675/5497 2995/6676/5498 +f 6034/6684/4100 2991/6677/5499 6035/6678/4105 +f 2995/6676/5498 2990/6669/5494 6021/6679/4067 +f 6035/6678/4105 2995/6676/5498 7737/6680/4071 +f 2992/6682/5501 2989/6675/5497 2996/6681/5500 +f 6046/6689/4136 2992/6682/5501 6047/6683/4137 +f 2996/6681/5500 2991/6677/5499 6033/6685/4099 +f 6047/6683/4137 2996/6681/5500 7739/6686/4103 +f 2989/6675/5497 2992/6682/5501 2993/6667/5492 +f 2993/6667/5492 2997/6687/5502 6568/6672/5495 +f 2992/6682/5501 6046/6689/4136 2997/6687/5502 +f 2997/6687/5502 6045/6690/4131 6567/6688/5503 +f 2999/6695/5507 2998/6692/5504 3003/6694/5506 +f 6037/6703/4111 2999/6695/5507 6038/6696/4112 +f 3003/6694/5506 3002/6693/5505 6048/6698/4153 +f 6038/6696/4112 3003/6694/5506 7740/6699/4113 +f 3000/6701/5509 2998/6692/5504 3004/6700/5508 +f 6025/6710/4078 3000/6701/5509 6026/6702/4079 +f 3004/6700/5508 2999/6695/5507 6036/6704/4121 +f 6026/6702/4079 3004/6700/5508 7738/6705/4080 +f 2998/6692/5504 3000/6701/5509 3001/6707/5511 +f 3001/6707/5511 3005/6706/5510 6571/6709/5513 +f 3000/6701/5509 6025/6710/4078 3005/6706/5510 +f 3005/6706/5510 6024/6711/4089 6570/6708/5512 +f 2998/6692/5504 3001/6707/5511 3002/6693/5505 +f 3002/6693/5505 3006/6713/5514 6049/6697/4143 +f 3001/6707/5511 6571/6709/5513 3006/6713/5514 +f 3006/6713/5514 6572/6715/5515 6050/6714/4144 +f 3008/6720/5519 3007/6717/5516 3012/6719/5518 +f 6568/6729/5495 3008/6720/5519 6569/6721/5496 +f 3012/6719/5518 3011/6718/5517 6575/6723/5521 +f 6569/6721/5496 3012/6719/5518 7735/6724/4037 +f 3007/6717/5516 3008/6720/5519 3009/6726/5523 +f 3009/6726/5523 3013/6725/5522 6553/6728/5443 +f 3008/6720/5519 6568/6729/5495 3013/6725/5522 +f 6554/6727/5444 3013/6725/5522 7741/6731/4134 +f 3010/6733/5525 3007/6717/5516 3014/6732/5524 +f 6562/6739/5468 3010/6733/5525 6563/6734/5475 +f 3009/6726/5523 6553/6728/5443 3014/6732/5524 +f 3014/6732/5524 6552/6735/5451 6563/6734/5475 +f 3007/6717/5516 3010/6733/5525 3011/6718/5517 +f 3011/6718/5517 3015/6737/5526 6574/6722/5520 +f 3010/6733/5525 6562/6739/5468 3015/6737/5526 +f 3015/6737/5526 6561/6740/5467 6573/6738/5527 +f 3017/6747/5531 3016/6742/5528 3021/6744/5530 +f 3017/6747/5531 3021/6744/5530 6556/6624/5461 +f 3021/6744/5530 3020/6743/5529 6564/6746/5486 +f 3021/6744/5530 6564/6746/5486 6557/6626/5463 +f 3016/6742/5528 3017/6747/5531 3018/6749/5533 +f 3018/6749/5533 3022/6748/5532 6571/6751/5513 +f 3017/6747/5531 6556/6624/5461 3022/6748/5532 +f 6572/6750/5515 3022/6748/5532 7742/5035/4145 +f 3019/6753/5535 3016/6742/5528 3023/6752/5534 +f 6577/6759/5538 3019/6753/5535 6576/6754/5536 +f 3023/6752/5534 3018/6749/5533 6570/6755/5512 +f 6576/6754/5536 3023/6752/5534 7736/6756/4049 +f 3016/6742/5528 3019/6753/5535 3020/6743/5529 +f 3020/6743/5529 3024/6757/5537 6565/6745/5489 +f 3019/6753/5535 6577/6759/5538 3024/6757/5537 +f 3024/6757/5537 6578/6760/5539 6566/6758/5490 +f 3026/6770/5545 3025/6762/5540 3029/6764/5542 +f 3026/6770/5545 3029/6764/5542 6010/6766/4034 +f 3029/6764/5542 3028/6763/5541 6581/6768/5544 +f 6011/6765/4040 3029/6764/5542 7524/6769/4041 +f 3027/6776/5547 3025/6762/5540 3030/6771/5546 +f 3027/6776/5547 3030/6771/5546 6574/6773/5520 +f 3030/6771/5546 3026/6770/5545 6009/6774/4033 +f 6575/6772/5521 3030/6771/5546 7735/6775/4037 +f 3028/6763/5541 3025/6762/5540 3031/6777/5548 +f 3028/6763/5541 3031/6777/5548 6580/6767/5543 +f 3031/6777/5548 3027/6776/5547 6573/6779/5527 +f 6579/6778/5549 3031/6777/5548 7525/6780/5471 +f 3033/6784/5553 3032/6781/5550 3036/6783/5552 +f 6577/6789/5538 3033/6784/5553 6578/6785/5539 +f 3035/6782/5551 6580/6767/5543 3036/6783/5552 +f 6578/6785/5539 3036/6783/5552 7525/6780/5471 +f 3034/6787/5555 3032/6781/5550 3037/6786/5554 +f 6013/6793/4047 3034/6787/5555 6014/6788/4048 +f 3033/6784/5553 6577/6789/5538 3037/6786/5554 +f 6014/6788/4048 3037/6786/5554 7736/6791/4049 +f 3035/6782/5551 3032/6781/5550 3038/6792/5556 +f 6580/6767/5543 3035/6782/5551 6581/6768/5544 +f 3034/6787/5555 6013/6793/4047 3038/6792/5556 +f 6581/6768/5544 3038/6792/5556 7524/6769/4041 +f 3039/6798/5560 3043/6795/5557 3040/6797/5559 +f 3040/6797/5559 3044/6796/5558 6082/5134/4228 +f 3043/6795/5557 6430/6275/5142 3044/6796/5558 +f 3044/6796/5558 6429/6271/5138 6083/5139/4233 +f 3041/6800/5562 3039/6798/5560 3045/6799/5561 +f 6349/6806/4950 3041/6800/5562 6350/6801/4951 +f 3045/6799/5561 3040/6797/5559 6081/5133/4227 +f 6350/6801/4951 3045/6799/5561 7747/5137/4231 +f 3039/6798/5560 3041/6800/5562 3042/6803/5564 +f 3042/6803/5564 3046/6802/5563 6583/6805/5566 +f 3041/6800/5562 6349/6806/4950 3046/6802/5563 +f 3046/6802/5563 6348/6807/4962 6582/6804/5565 +f 3043/6795/5557 3039/6798/5560 3047/6809/5567 +f 6430/6275/5142 3043/6795/5557 6431/6276/5143 +f 3047/6809/5567 3042/6803/5564 6584/6810/5568 +f 6431/6276/5143 3047/6809/5567 7813/6277/5144 +f 3048/6819/5574 3052/6811/5569 3049/6813/5571 +f 3049/6813/5571 3053/6812/5570 6364/6815/4983 +f 3052/6811/5569 6586/6816/5572 3053/6812/5570 +f 3053/6812/5570 6587/6817/5573 6365/6814/4984 +f 3050/6821/5576 3048/6819/5574 3054/6820/5575 +f 6085/6827/4239 3050/6821/5576 6086/6822/4240 +f 3054/6820/5575 3049/6813/5571 6363/6823/4980 +f 6086/6822/4240 3054/6820/5575 7748/6824/4241 +f 3048/6819/5574 3050/6821/5576 3051/6826/5578 +f 3051/6826/5578 3055/6825/5577 6439/6287/5154 +f 3050/6821/5576 6085/6827/4239 3055/6825/5577 +f 3055/6825/5577 6084/6828/4249 6440/6294/5161 +f 3052/6811/5569 3048/6819/5574 3056/6829/5579 +f 6586/6816/5572 3052/6811/5569 6585/6830/5580 +f 3056/6829/5579 3051/6826/5578 6438/6286/5153 +f 6585/6830/5580 3056/6829/5579 7814/6290/5157 +f 3058/6834/5584 3057/6831/5581 3062/6833/5583 +f 6589/6843/5590 3058/6834/5584 6588/6835/5585 +f 3062/6833/5583 3061/6832/5582 6582/6837/5565 +f 6588/6835/5585 3062/6833/5583 7761/6838/4965 +f 3057/6831/5581 3058/6834/5584 3059/6840/5587 +f 3059/6840/5587 3063/6839/5586 6592/6842/5589 +f 3058/6834/5584 6589/6843/5590 3063/6839/5586 +f 3063/6839/5586 6590/6844/5591 6591/6841/5588 +f 3060/6847/5594 3057/6831/5581 3064/6846/5593 +f 6448/6314/5179 3060/6847/5594 6449/6321/5183 +f 3064/6846/5593 3059/6840/5587 6593/6848/5595 +f 6449/6321/5183 3064/6846/5593 7819/6322/5184 +f 3057/6831/5581 3060/6847/5594 3061/6832/5582 +f 3061/6832/5582 3065/6849/5596 6583/6836/5566 +f 3060/6847/5594 6448/6314/5179 3065/6849/5596 +f 3065/6849/5596 6447/6313/5178 6584/6850/5568 +f 3067/6854/5600 3066/6851/5597 3071/6853/5599 +f 6595/6860/5606 3067/6854/5600 6594/6855/5601 +f 3071/6853/5599 3070/6852/5598 6456/6332/5194 +f 6594/6855/5601 3071/6853/5599 7820/6335/5197 +f 3066/6851/5597 3067/6854/5600 3068/6857/5603 +f 3068/6857/5603 3072/6856/5602 6598/6859/5605 +f 3067/6854/5600 6595/6860/5606 3072/6856/5602 +f 3072/6856/5602 6596/6861/5607 6597/6858/5604 +f 3069/6864/5610 3066/6851/5597 3073/6863/5609 +f 6586/6816/5572 3069/6864/5610 6587/6817/5573 +f 3073/6863/5609 3068/6857/5603 6599/6865/5611 +f 6587/6817/5573 3073/6863/5609 7762/6818/4985 +f 3066/6851/5597 3069/6864/5610 3070/6852/5598 +f 3070/6852/5598 3074/6866/5612 6457/6338/5200 +f 3069/6864/5610 6586/6816/5572 3074/6866/5612 +f 3074/6866/5612 6585/6830/5580 6458/6339/5201 +f 3076/6870/5616 3075/6867/5613 3080/6869/5615 +f 6601/6879/5622 3076/6870/5616 6600/6871/5617 +f 3080/6869/5615 3079/6868/5614 6591/6873/5588 +f 3080/6869/5615 6591/6873/5588 6600/6871/5617 +f 3075/6867/5613 3076/6870/5616 3077/6876/5619 +f 3077/6876/5619 3081/6875/5618 6604/6878/5621 +f 3076/6870/5616 6601/6879/5622 3081/6875/5618 +f 3081/6875/5618 6602/6880/5623 6603/6877/5620 +f 3078/6883/5626 3075/6867/5613 3082/6882/5625 +f 6466/6359/5219 3078/6883/5626 6467/6366/5223 +f 3082/6882/5625 3077/6876/5619 6605/6884/5627 +f 6467/6366/5223 3082/6882/5625 7821/6367/5224 +f 3075/6867/5613 3078/6883/5626 3079/6868/5614 +f 3079/6868/5614 3083/6885/5628 6592/6872/5589 +f 3078/6883/5626 6466/6359/5219 3083/6885/5628 +f 3083/6885/5628 6465/6358/5218 6593/6886/5595 +f 3085/6890/5632 3084/6887/5629 3089/6889/5631 +f 6607/6896/5638 3085/6890/5632 6606/6891/5633 +f 3089/6889/5631 3088/6888/5630 6474/6377/5234 +f 6606/6891/5633 3089/6889/5631 7822/6380/5237 +f 3084/6887/5629 3085/6890/5632 3086/6893/5635 +f 3086/6893/5635 3090/6892/5634 6610/6895/5637 +f 3085/6890/5632 6607/6896/5638 3090/6892/5634 +f 3090/6892/5634 6608/6897/5639 6609/6894/5636 +f 3087/6900/5642 3084/6887/5629 3091/6899/5641 +f 6595/6860/5606 3087/6900/5642 6596/6861/5607 +f 3091/6899/5641 3086/6893/5635 6611/6901/5643 +f 3091/6899/5641 6611/6901/5643 6596/6861/5607 +f 3084/6887/5629 3087/6900/5642 3088/6888/5630 +f 3088/6888/5630 3092/6902/5644 6475/6383/5240 +f 3087/6900/5642 6595/6860/5606 3092/6902/5644 +f 3092/6902/5644 6594/6855/5601 6476/6384/5241 +f 3093/6911/5650 3097/6903/5645 3094/6905/5647 +f 3094/6905/5647 3098/6904/5646 6499/6907/5294 +f 3097/6903/5645 6613/6908/5648 3098/6904/5646 +f 3098/6904/5646 6614/6909/5649 6500/6906/5295 +f 3095/6913/5652 3093/6911/5650 3099/6912/5651 +f 6484/6921/5262 3095/6913/5652 6485/6914/5263 +f 3099/6912/5651 3094/6905/5647 6498/6915/5309 +f 6485/6914/5263 3099/6912/5651 7827/6916/5264 +f 3093/6911/5650 3095/6913/5652 3096/6918/5654 +f 3096/6918/5654 3100/6917/5653 6604/6920/5621 +f 3095/6913/5652 6484/6921/5262 3100/6917/5653 +f 3100/6917/5653 6483/6922/5257 6605/6919/5627 +f 3097/6903/5645 3093/6911/5650 3101/6924/5655 +f 6613/6908/5648 3097/6903/5645 6612/6925/5656 +f 3101/6924/5655 3096/6918/5654 6603/6926/5620 +f 6612/6925/5656 3101/6924/5655 7833/6927/5624 +f 3102/6931/5660 3106/6928/5657 3103/6930/5659 +f 3103/6930/5659 3107/6929/5658 6493/6417/5274 +f 3106/6928/5657 6607/6896/5638 3107/6929/5658 +f 3107/6929/5658 6606/6891/5633 6494/6423/5280 +f 3104/6933/5662 3102/6931/5660 3108/6932/5661 +f 6514/6939/5328 3104/6933/5662 6515/6934/5329 +f 3108/6932/5661 3103/6930/5659 6492/6416/5273 +f 6515/6934/5329 3108/6932/5661 7828/6420/5277 +f 3102/6931/5660 3104/6933/5662 3105/6936/5664 +f 3105/6936/5664 3109/6935/5663 6616/6938/5666 +f 3104/6933/5662 6514/6939/5328 3109/6935/5663 +f 3109/6935/5663 6513/6940/5323 6615/6937/5665 +f 3106/6928/5657 3102/6931/5660 3110/6942/5667 +f 6607/6896/5638 3106/6928/5657 6608/6897/5639 +f 3110/6942/5667 3105/6936/5664 6617/6943/5668 +f 6608/6897/5639 3110/6942/5667 7834/6898/5640 +f 3111/6949/5674 3115/6944/5669 3112/6946/5671 +f 3112/6946/5671 3116/6945/5670 6322/5941/4868 +f 3115/6944/5669 6619/6947/5672 3116/6945/5670 +f 3116/6945/5670 6620/6948/5673 6323/5945/4872 +f 3113/6951/5676 3111/6949/5674 3117/6950/5675 +f 6622/6957/5682 3113/6951/5676 6621/6952/5677 +f 3117/6950/5675 3112/6946/5671 6321/5940/4867 +f 6621/6952/5677 3117/6950/5675 7801/5907/4838 +f 3111/6949/5674 3113/6951/5676 3114/6954/5679 +f 3114/6954/5679 3118/6953/5678 6625/6956/5681 +f 3113/6951/5676 6622/6957/5682 3118/6953/5678 +f 3118/6953/5678 6623/6958/5683 6624/6955/5680 +f 3115/6944/5669 3111/6949/5674 3119/6960/5685 +f 6619/6947/5672 3115/6944/5669 6618/6961/5686 +f 3119/6960/5685 3114/6954/5679 6626/6962/5687 +f 3119/6960/5685 6626/6962/5687 6618/6961/5686 +f 3120/6972/5697 3124/6964/5689 3121/6966/5691 +f 3121/6966/5691 3125/6965/5690 6631/6968/5693 +f 3124/6964/5689 6628/6969/5694 3125/6965/5690 +f 3125/6965/5690 6629/6970/5695 6630/6967/5692 +f 3122/6974/5699 3120/6972/5697 3126/6973/5698 +f 6331/5963/4889 3122/6974/5699 6332/5964/4890 +f 3126/6973/5698 3121/6966/5691 6632/6975/5700 +f 6332/5964/4890 3126/6973/5698 7802/5965/4849 +f 3120/6972/5697 3122/6974/5699 3123/6977/5702 +f 3123/6977/5702 3127/6976/5701 6634/6979/5704 +f 3122/6974/5699 6331/5963/4889 3127/6976/5701 +f 3127/6976/5701 6330/5956/4883 6633/6978/5703 +f 3124/6964/5689 3120/6972/5697 3128/6980/5705 +f 6628/6969/5694 3124/6964/5689 6627/6981/5706 +f 3128/6980/5705 3123/6977/5702 6635/6982/5707 +f 3128/6980/5705 6635/6982/5707 6627/6981/5706 +f 3129/6998/5723 3133/6984/5709 3130/6986/5711 +f 6640/6992/5717 3130/6986/5711 6639/6987/5712 +f 3133/6984/5709 6637/6988/5713 3134/6985/5710 +f 3134/6985/5710 6638/6989/5714 6639/6987/5712 +f 3129/6998/5723 3130/6986/5711 3131/6991/5716 +f 3131/6991/5716 3135/6990/5715 6625/6956/5681 +f 3130/6986/5711 6640/6992/5717 3135/6990/5715 +f 3135/6990/5715 6641/6993/5718 6626/6962/5687 +f 3129/6998/5723 3131/6991/5716 3132/6995/5720 +f 3132/6995/5720 3136/6994/5719 6643/6997/5722 +f 3136/6994/5719 3131/6991/5716 6624/6955/5680 +f 3136/6994/5719 6624/6955/5680 6642/6996/5721 +f 3133/6984/5709 3129/6998/5723 3137/6999/5724 +f 6637/6988/5713 3133/6984/5709 6636/7000/5725 +f 3137/6999/5724 3132/6995/5720 6644/7001/5726 +f 6636/7000/5725 3137/6999/5724 7771/7002/5727 +f 3138/7017/5742 3142/7003/5728 3139/7005/5730 +f 6628/6969/5694 3139/7005/5730 6629/6970/5695 +f 3142/7003/5728 6646/7006/5731 3143/7004/5729 +f 3143/7004/5729 6647/7007/5732 6629/6970/5695 +f 3138/7017/5742 3139/7005/5730 3140/7009/5734 +f 3140/7009/5734 3144/7008/5733 6649/7011/5736 +f 3139/7005/5730 6628/6969/5694 3144/7008/5733 +f 3144/7008/5733 6627/6981/5706 6648/7010/5735 +f 3138/7017/5742 3140/7009/5734 3141/7013/5738 +f 3141/7013/5738 3145/7012/5737 6652/7015/5740 +f 3145/7012/5737 3140/7009/5734 6650/7016/5741 +f 3145/7012/5737 6650/7016/5741 6651/7014/5739 +f 3142/7003/5728 3138/7017/5742 3146/7018/5743 +f 6646/7006/5731 3142/7003/5728 6645/7019/5744 +f 3146/7018/5743 3141/7013/5738 6653/7020/5745 +f 6645/7019/5744 3146/7018/5743 7772/7021/5746 +f 3148/7025/5750 3147/7022/5747 3152/7024/5749 +f 6106/5227/4296 3148/7025/5750 6107/5228/4297 +f 3152/7024/5749 3151/7023/5748 6351/6056/4954 +f 6107/5228/4297 3152/7024/5749 7749/5184/4262 +f 3147/7022/5747 3148/7025/5750 3149/7027/5752 +f 3149/7027/5752 3153/7026/5751 6655/7029/5754 +f 3148/7025/5750 6106/5227/4296 3153/7026/5751 +f 3153/7026/5751 6105/5223/4292 6654/7028/5753 +f 3150/7031/5756 3147/7022/5747 3154/7030/5755 +f 6640/6992/5717 3150/7031/5756 6641/6993/5718 +f 3154/7030/5755 3149/7027/5752 6656/7032/5757 +f 6641/6993/5718 3154/7030/5755 7835/6963/5688 +f 3147/7022/5747 3150/7031/5756 3151/7023/5748 +f 3151/7023/5748 3155/7033/5758 6352/6057/4955 +f 3150/7031/5756 6640/6992/5717 3155/7033/5758 +f 3155/7033/5758 6639/6987/5712 6353/6061/4959 +f 3157/7037/5762 3156/7034/5759 3161/7036/5761 +f 6658/7043/5766 3157/7037/5762 6657/7038/5763 +f 3161/7036/5761 3160/7035/5760 6648/7010/5735 +f 6657/7038/5763 3161/7036/5761 7836/6983/5708 +f 3156/7034/5759 3157/7037/5762 3158/7040/5765 +f 3158/7040/5765 3162/7039/5764 6109/7042/4303 +f 3157/7037/5762 6658/7043/5766 3162/7039/5764 +f 3162/7039/5764 6659/7044/5767 6110/7041/4304 +f 3159/7047/5769 3156/7034/5759 3163/7046/5768 +f 6361/6080/4976 3159/7047/5769 6362/6081/4977 +f 3163/7046/5768 3158/7040/5765 6108/7048/4313 +f 6362/6081/4977 3163/7046/5768 7750/6082/4273 +f 3156/7034/5759 3159/7047/5769 3160/7035/5760 +f 3160/7035/5760 3164/7049/5770 6649/7011/5736 +f 3159/7047/5769 6361/6080/4976 3164/7049/5770 +f 3164/7049/5770 6360/6072/4970 6650/7016/5741 +f 3166/7053/5774 3165/7050/5771 3170/7052/5773 +f 6661/7059/5778 3166/7053/5774 6660/7054/5775 +f 3170/7052/5773 3169/7051/5772 6654/7028/5753 +f 6660/7054/5775 3170/7052/5773 7751/5225/4294 +f 3165/7050/5771 3166/7053/5774 3167/7056/5777 +f 3167/7056/5777 3171/7055/5776 6244/7058/4667 +f 3166/7053/5774 6661/7059/5778 3171/7055/5776 +f 3171/7055/5776 6662/7060/5779 6245/7057/4673 +f 3168/7063/5781 3165/7050/5771 3172/7062/5780 +f 6664/7067/5784 3168/7063/5781 6663/7064/5782 +f 3172/7062/5780 3167/7056/5777 6243/7065/4666 +f 6663/7064/5782 3172/7062/5780 7793/6027/4669 +f 3165/7050/5771 3168/7063/5781 3169/7051/5772 +f 3169/7051/5772 3173/7066/5783 6655/7029/5754 +f 3168/7063/5781 6664/7067/5784 3173/7066/5783 +f 3173/7066/5783 6665/7068/5785 6656/7032/5757 +f 3175/7072/5789 3174/7069/5786 3179/7071/5788 +f 6253/7081/4690 3175/7072/5789 6254/7073/4691 +f 3179/7071/5788 3178/7070/5787 6668/7075/5791 +f 6254/7073/4691 3179/7071/5788 7794/7076/4692 +f 3174/7069/5786 3175/7072/5789 3176/7078/5793 +f 3176/7078/5793 3180/7077/5792 6670/7080/5795 +f 3175/7072/5789 6253/7081/4690 3180/7077/5792 +f 3180/7077/5792 6252/7082/4682 6669/7079/5794 +f 3177/7085/5797 3174/7069/5786 3181/7084/5796 +f 6658/7043/5766 3177/7085/5797 6659/7044/5767 +f 3181/7084/5796 3176/7078/5793 6671/7086/5798 +f 6659/7044/5767 3181/7084/5796 7752/7045/4305 +f 3174/7069/5786 3177/7085/5797 3178/7070/5787 +f 3178/7070/5787 3182/7087/5799 6667/7074/5790 +f 3177/7085/5797 6658/7043/5766 3182/7087/5799 +f 3182/7087/5799 6657/7038/5763 6666/7088/5800 +f 3183/7092/5804 3186/7089/5801 3184/7091/5803 +f 3184/7091/5803 3187/7090/5802 6343/6023/4931 +f 3186/7089/5801 6664/7067/5784 3187/7090/5802 +f 3187/7090/5802 6663/7064/5782 6344/6026/4933 +f 3185/7094/5806 3183/7092/5804 3188/7093/5805 +f 6619/6947/5672 3185/7094/5806 6620/6948/5673 +f 3188/7093/5805 3184/7091/5803 6342/6022/4930 +f 6620/6948/5673 3188/7093/5805 7799/5946/4873 +f 3186/7089/5801 3183/7092/5804 3189/7095/5807 +f 6664/7067/5784 3186/7089/5801 6665/7068/5785 +f 3185/7094/5806 6619/6947/5672 3189/7095/5807 +f 3189/7095/5807 6618/6961/5686 6665/7068/5785 +f 3191/7099/5811 3190/7096/5808 3194/7098/5810 +f 3191/7099/5811 3194/7098/5810 6634/6979/5704 +f 3194/7098/5810 3193/7097/5809 6666/7088/5800 +f 3194/7098/5810 6666/7088/5800 6635/6982/5707 +f 3192/7101/5813 3190/7096/5808 3195/7100/5812 +f 6346/7104/4937 3192/7101/5813 6347/7102/4938 +f 3195/7100/5812 3191/7099/5811 6633/6978/5703 +f 6347/7102/4938 3195/7100/5812 7800/5959/4886 +f 3190/7096/5808 3192/7101/5813 3193/7097/5809 +f 3193/7097/5809 3196/7103/5814 6667/7074/5790 +f 3192/7101/5813 6346/7104/4937 3196/7103/5814 +f 3196/7103/5814 6345/7105/4945 6668/7075/5791 +f 3197/7114/5818 3200/7106/5815 3198/7108/5817 +f 3198/7108/5817 3201/7107/5816 6277/7110/4760 +f 3200/7106/5815 6661/7111/5778 3201/7107/5816 +f 3201/7107/5816 6660/7112/5775 6278/7109/4764 +f 3199/7116/5820 3197/7114/5818 3202/7115/5819 +f 6268/7122/4735 3199/7116/5820 6269/7117/4736 +f 3202/7115/5819 3198/7108/5817 6276/7118/4759 +f 6269/7117/4736 3202/7115/5819 7797/7119/4737 +f 3200/7106/5815 3197/7114/5818 3203/7120/5821 +f 6661/7111/5778 3200/7106/5815 6662/7121/5779 +f 3203/7120/5821 3199/7116/5820 6267/7123/4732 +f 6662/7121/5779 3203/7120/5821 7795/7124/4674 +f 3205/7128/5825 3204/7125/5822 3208/7127/5824 +f 6271/7133/4741 3205/7128/5825 6272/7129/4742 +f 3208/7127/5824 3207/7126/5823 6669/7079/5794 +f 6272/7129/4742 3208/7127/5824 7796/7083/4685 +f 3206/7131/5827 3204/7125/5822 3209/7130/5826 +f 6280/7137/4773 3206/7131/5827 6281/7132/4774 +f 3209/7130/5826 3205/7128/5825 6270/7134/4750 +f 6281/7132/4774 3209/7130/5826 7798/7135/4753 +f 3204/7125/5822 3206/7131/5827 3207/7126/5823 +f 3207/7126/5823 3210/7136/5828 6670/7080/5795 +f 3206/7131/5827 6280/7137/4773 3210/7136/5828 +f 3210/7136/5828 6279/7138/4769 6671/7086/5798 +f 3211/7147/5834 3215/7139/5829 3212/7141/5831 +f 3212/7141/5831 3216/7140/5830 6673/7143/5833 +f 3215/7139/5829 6502/7144/5301 3216/7140/5830 +f 3216/7140/5830 6501/7145/5293 6672/7142/5832 +f 3213/7153/5840 3211/7147/5834 3217/7148/5835 +f 3213/7153/5840 3217/7148/5835 6676/7150/5837 +f 3217/7148/5835 3212/7141/5831 6674/7151/5838 +f 3217/7148/5835 6674/7151/5838 6675/7149/5836 +f 3211/7147/5834 3213/7153/5840 3214/7155/5842 +f 6679/7161/5847 3214/7155/5842 6678/7156/5843 +f 3218/7154/5841 3213/7153/5840 6677/7157/5844 +f 3218/7154/5841 6677/7157/5844 6678/7156/5843 +f 3215/7139/5829 3211/7147/5834 3219/7159/5846 +f 3215/7139/5829 3219/7159/5846 6502/7144/5301 +f 3214/7155/5842 6679/7161/5847 3219/7159/5846 +f 3219/7159/5846 6680/7162/5848 6503/7160/5302 +f 3220/7171/5856 3224/7164/5849 3221/7166/5851 +f 6685/7175/5860 3221/7166/5851 6684/7167/5852 +f 3225/7165/5850 3224/7164/5849 6683/7169/5854 +f 3225/7165/5850 6683/7169/5854 6684/7167/5852 +f 3222/7173/5858 3220/7171/5856 3226/7172/5857 +f 6688/7180/5865 3222/7173/5858 6687/7174/5859 +f 3221/7166/5851 6685/7175/5860 3226/7172/5857 +f 3226/7172/5857 6686/7176/5861 6687/7174/5859 +f 3220/7171/5856 3222/7173/5858 3223/7179/5864 +f 3223/7179/5864 3227/7178/5863 6511/6470/5324 +f 3222/7173/5858 6688/7180/5865 3227/7178/5863 +f 3227/7178/5863 6689/7181/5866 6512/6471/5325 +f 3224/7164/5849 3220/7171/5856 3228/7182/5867 +f 3224/7164/5849 3228/7182/5867 6682/7168/5853 +f 3223/7179/5864 6511/6470/5324 3228/7182/5867 +f 3228/7182/5867 6510/6464/5318 6681/7183/5868 +f 3229/7192/5872 3233/7184/5869 3230/7186/5871 +f 3230/7186/5871 3234/7185/5870 6517/7188/5334 +f 3233/7184/5869 6373/7189/5002 3234/7185/5870 +f 3234/7185/5870 6372/7190/4997 6518/7187/5335 +f 3231/7194/5874 3229/7192/5872 3235/7193/5873 +f 6679/7161/5847 3231/7194/5874 6680/7162/5848 +f 3235/7193/5873 3230/7186/5871 6516/7195/5344 +f 6680/7162/5848 3235/7193/5873 7829/7163/5303 +f 3229/7192/5872 3231/7194/5874 3232/7197/5876 +f 3232/7197/5876 3236/7196/5875 6691/7199/5878 +f 3231/7194/5874 6679/7161/5847 3236/7196/5875 +f 3236/7196/5875 6678/7156/5843 6690/7198/5877 +f 3233/7184/5869 3229/7192/5872 3237/7200/5879 +f 6373/7189/5002 3233/7184/5869 6374/7201/5003 +f 3237/7200/5879 3232/7197/5876 6692/7202/5880 +f 3237/7200/5879 6692/7202/5880 6374/7201/5003 +f 3238/7208/5886 3242/7203/5881 3239/7205/5883 +f 3239/7205/5883 3243/7204/5882 6682/7168/5853 +f 3242/7203/5881 6694/7206/5884 3243/7204/5882 +f 3243/7204/5882 6695/7207/5885 6683/7169/5854 +f 3240/7210/5888 3238/7208/5886 3244/7209/5887 +f 6526/6506/5358 3240/7210/5888 6527/6507/5359 +f 3244/7209/5887 3239/7205/5883 6681/7183/5868 +f 6527/6507/5359 3244/7209/5887 7830/6466/5320 +f 3238/7208/5886 3240/7210/5888 3241/7212/5890 +f 3241/7212/5890 3245/7211/5889 6376/6121/5010 +f 3240/7210/5888 6526/6506/5358 3245/7211/5889 +f 3245/7211/5889 6525/6504/5356 6377/6126/5015 +f 3242/7203/5881 3238/7208/5886 3246/7213/5891 +f 6694/7206/5884 3242/7203/5881 6693/7214/5892 +f 3246/7213/5891 3241/7212/5890 6375/6120/5009 +f 3246/7213/5891 6375/6120/5009 6693/7214/5892 +f 3247/7220/5898 3251/7215/5893 3248/7217/5895 +f 3248/7217/5895 3252/7216/5894 6643/6997/5722 +f 3251/7215/5893 6697/7218/5896 3252/7216/5894 +f 3252/7216/5894 6698/7219/5897 6644/7001/5726 +f 3249/7224/5902 3247/7220/5898 3253/7221/5899 +f 3249/7224/5902 3253/7221/5899 6700/7223/5901 +f 3253/7221/5899 3248/7217/5895 6642/6996/5721 +f 3253/7221/5899 6642/6996/5721 6699/7222/5900 +f 3247/7220/5898 3249/7224/5902 3250/7226/5904 +f 3250/7226/5904 3254/7225/5903 6676/7150/5837 +f 3254/7225/5903 3249/7224/5902 6701/7227/5905 +f 3254/7225/5903 6701/7227/5905 6677/7157/5844 +f 3251/7215/5893 3247/7220/5898 3255/7228/5906 +f 6697/7218/5896 3251/7215/5893 6696/7229/5907 +f 3255/7228/5906 3250/7226/5904 6675/7149/5836 +f 6696/7229/5907 3255/7228/5906 7773/7152/5839 +f 3256/7234/5912 3260/7230/5908 3257/7232/5910 +f 6703/7238/5915 3257/7232/5910 6702/7233/5911 +f 3260/7230/5908 6685/7175/5860 3261/7231/5909 +f 3261/7231/5909 6684/7167/5852 6702/7233/5911 +f 3258/7236/5914 3256/7234/5912 3262/7235/5913 +f 6646/7245/5731 3258/7236/5914 6647/7237/5732 +f 3257/7232/5910 6703/7238/5915 3262/7235/5913 +f 3262/7235/5913 6704/7239/5916 6647/7237/5732 +f 3256/7234/5912 3258/7236/5914 3259/7242/5918 +f 3259/7242/5918 3263/7241/5917 6706/7244/5920 +f 3258/7236/5914 6646/7245/5731 3263/7241/5917 +f 3263/7241/5917 6645/7246/5744 6705/7243/5919 +f 3260/7230/5908 3256/7234/5912 3264/7248/5921 +f 6685/7175/5860 3260/7230/5908 6686/7176/5861 +f 3264/7248/5921 3259/7242/5918 6707/7249/5922 +f 6686/7176/5861 3264/7248/5921 7774/7177/5862 +f 3265/7253/5926 3269/7250/5923 3266/7252/5925 +f 3266/7252/5925 3270/7251/5924 6307/5900/4831 +f 3269/7250/5923 6622/6957/5682 3270/7251/5924 +f 3270/7251/5924 6621/6952/5677 6308/5906/4837 +f 3267/7255/5928 3265/7253/5926 3271/7254/5927 +f 6709/7259/5932 3267/7255/5928 6708/7256/5929 +f 3271/7254/5927 3266/7252/5925 6306/5899/4830 +f 6708/7256/5929 3271/7254/5927 7803/5862/4798 +f 3265/7253/5926 3267/7255/5928 3268/7258/5931 +f 3268/7258/5931 3272/7257/5930 6700/7223/5901 +f 3267/7255/5928 6709/7259/5932 3272/7257/5930 +f 3272/7257/5930 6710/7260/5933 6701/7227/5905 +f 3269/7250/5923 3265/7253/5926 3273/7261/5934 +f 6622/6957/5682 3269/7250/5923 6623/6958/5683 +f 3273/7261/5934 3268/7258/5931 6699/7222/5900 +f 6623/6958/5683 3273/7261/5934 7837/6959/5684 +f 3274/7267/5940 3278/7262/5935 3275/7264/5937 +f 3275/7264/5937 3279/7263/5936 6712/7266/5939 +f 3278/7262/5935 6703/7238/5915 3279/7263/5936 +f 3279/7263/5936 6702/7233/5911 6711/7265/5938 +f 3276/7269/5942 3274/7267/5940 3280/7268/5941 +f 6316/5922/4853 3276/7269/5942 6317/5923/4854 +f 3280/7268/5941 3275/7264/5937 6713/7270/5943 +f 6317/5923/4854 3280/7268/5941 7804/5874/4810 +f 3274/7267/5940 3276/7269/5942 3277/7272/5945 +f 3277/7272/5945 3281/7271/5944 6631/7274/5693 +f 3276/7269/5942 6316/5922/4853 3281/7271/5944 +f 3281/7271/5944 6315/5915/4846 6632/7273/5700 +f 3278/7262/5935 3274/7267/5940 3282/7275/5946 +f 6703/7238/5915 3278/7262/5935 6704/7239/5916 +f 3282/7275/5946 3277/7272/5945 6630/7276/5692 +f 6704/7239/5916 3282/7275/5946 7838/7240/5696 +f 3284/7280/5950 3283/7277/5947 3287/7279/5949 +f 3284/7280/5950 3287/7279/5949 6691/7199/5878 +f 3287/7279/5949 3286/7278/5948 6288/5853/4789 +f 6692/7202/5880 3287/7279/5949 7775/5855/4791 +f 3285/7282/5952 3283/7277/5947 3288/7281/5951 +f 6709/7259/5932 3285/7282/5952 6710/7260/5933 +f 3288/7281/5951 3284/7280/5950 6690/7198/5877 +f 6710/7260/5933 3288/7281/5951 7831/7158/5845 +f 3286/7278/5948 3283/7277/5947 3289/7283/5953 +f 6289/5860/4796 3286/7278/5948 6290/5861/4797 +f 3285/7282/5952 6709/7259/5932 3289/7283/5953 +f 3289/7283/5953 6708/7256/5929 6290/5861/4797 +f 3291/7287/5957 3290/7284/5954 3294/7286/5956 +f 3291/7287/5957 3294/7286/5956 6712/7266/5939 +f 3294/7286/5956 3293/7285/5955 6297/5870/4806 +f 3294/7286/5956 6297/5870/4806 6713/7270/5943 +f 3292/7289/5959 3290/7284/5954 3295/7288/5958 +f 6694/7206/5884 3292/7289/5959 6695/7207/5885 +f 3295/7288/5958 3291/7287/5957 6711/7265/5938 +f 6695/7207/5885 3295/7288/5958 7832/7170/5855 +f 3293/7285/5955 3290/7284/5954 3296/7290/5960 +f 6298/5871/4807 3293/7285/5955 6299/5879/4815 +f 3292/7289/5959 6694/7206/5884 3296/7290/5960 +f 6299/5879/4815 3296/7290/5960 7776/5880/4816 +f 3297/7299/5969 3301/7291/5961 3298/7293/5963 +f 3298/7293/5963 3302/7292/5962 6718/7295/5965 +f 3301/7291/5961 6715/7296/5966 3302/7292/5962 +f 3302/7292/5962 6716/7297/5967 6717/7294/5964 +f 3299/7301/5971 3297/7299/5969 3303/7300/5970 +f 6721/7309/5979 3299/7301/5971 6720/7302/5972 +f 3303/7300/5970 3298/7293/5963 6719/7303/5973 +f 6720/7302/5972 3303/7300/5970 7839/7304/5974 +f 3297/7299/5969 3299/7301/5971 3300/7306/5976 +f 3300/7306/5976 3304/7305/5975 6724/7308/5978 +f 3299/7301/5971 6721/7309/5979 3304/7305/5975 +f 3304/7305/5975 6722/7310/5980 6723/7307/5977 +f 3301/7291/5961 3297/7299/5969 3305/7312/5982 +f 6715/7296/5966 3301/7291/5961 6714/7313/5983 +f 3305/7312/5982 3300/7306/5976 6725/7314/5984 +f 6714/7313/5983 3305/7312/5982 7853/7315/5985 +f 3306/7324/5994 3310/7316/5986 3307/7318/5988 +f 3307/7318/5988 3311/7317/5987 6730/7320/5990 +f 3310/7316/5986 6727/7321/5991 3311/7317/5987 +f 3311/7317/5987 6728/7322/5992 6729/7319/5989 +f 3308/7326/5996 3306/7324/5994 3312/7325/5995 +f 6733/7334/6004 3308/7326/5996 6732/7327/5997 +f 3312/7325/5995 3307/7318/5988 6731/7328/5998 +f 6732/7327/5997 3312/7325/5995 7840/7329/5999 +f 3306/7324/5994 3308/7326/5996 3309/7331/6001 +f 3309/7331/6001 3313/7330/6000 6736/7333/6003 +f 3308/7326/5996 6733/7334/6004 3313/7330/6000 +f 3313/7330/6000 6734/7335/6005 6735/7332/6002 +f 3310/7316/5986 3306/7324/5994 3314/7337/6007 +f 6727/7321/5991 3310/7316/5986 6726/7338/6008 +f 3314/7337/6007 3309/7331/6001 6737/7339/6009 +f 6726/7338/6008 3314/7337/6007 7854/7340/6010 +f 3316/7344/6014 3315/7341/6011 3320/7343/6013 +f 6715/7296/5966 3316/7344/6014 6716/7297/5967 +f 3320/7343/6013 3319/7342/6012 6740/7346/6016 +f 6716/7297/5967 3320/7343/6013 7851/7298/5968 +f 3315/7341/6011 3316/7344/6014 3317/7348/6018 +f 3317/7348/6018 3321/7347/6017 6742/7350/6020 +f 3316/7344/6014 6715/7296/5966 3321/7347/6017 +f 3321/7347/6017 6714/7313/5983 6741/7349/6019 +f 3318/7352/6022 3315/7341/6011 3322/7351/6021 +f 6745/7358/6028 3318/7352/6022 6744/7353/6023 +f 3322/7351/6021 3317/7348/6018 6743/7354/6024 +f 6744/7353/6023 3322/7351/6021 7855/7355/6025 +f 3315/7341/6011 3318/7352/6022 3319/7342/6012 +f 3319/7342/6012 3323/7356/6026 6739/7345/6015 +f 3318/7352/6022 6745/7358/6028 3323/7356/6026 +f 3323/7356/6026 6746/7359/6029 6738/7357/6027 +f 3325/7364/6034 3324/7361/6031 3329/7363/6033 +f 6751/7373/6041 3325/7364/6034 6750/7365/6035 +f 3329/7363/6033 3328/7362/6032 6749/7367/6037 +f 6750/7365/6035 3329/7363/6033 7856/7368/6038 +f 3324/7361/6031 3325/7364/6034 3326/7370/6040 +f 3326/7370/6040 3330/7369/6039 6736/7372/6003 +f 3325/7364/6034 6751/7373/6041 3330/7369/6039 +f 3330/7369/6039 6752/7374/6042 6737/7371/6009 +f 3327/7377/6044 3324/7361/6031 3331/7376/6043 +f 6754/7383/6048 3327/7377/6044 6753/7378/6045 +f 3331/7376/6043 3326/7370/6040 6735/7379/6002 +f 6753/7378/6045 3331/7376/6043 7852/7380/6006 +f 3324/7361/6031 3327/7377/6044 3328/7362/6032 +f 3328/7362/6032 3332/7381/6046 6748/7366/6036 +f 3327/7377/6044 6754/7383/6048 3332/7381/6046 +f 3332/7381/6046 6755/7384/6049 6747/7382/6047 +f 3334/7389/6054 3333/7386/6051 3338/7388/6053 +f 6745/7358/6028 3334/7389/6054 6746/7359/6029 +f 3338/7388/6053 3337/7387/6052 6758/7391/6056 +f 6746/7359/6029 3338/7388/6053 7849/7360/6030 +f 3335/7393/6058 3333/7386/6051 3339/7392/6057 +f 6760/7399/6064 3335/7393/6058 6759/7394/6059 +f 3334/7389/6054 6745/7358/6028 3339/7392/6057 +f 6759/7394/6059 3339/7392/6057 7855/7355/6025 +f 3333/7386/6051 3335/7393/6058 3336/7396/6061 +f 3336/7396/6061 3340/7395/6060 6763/7398/6063 +f 3335/7393/6058 6760/7399/6064 3340/7395/6060 +f 3340/7395/6060 6761/7400/6065 6762/7397/6062 +f 3337/7387/6052 3333/7386/6051 3341/7402/6067 +f 3337/7387/6052 3341/7402/6067 6757/7390/6055 +f 3341/7402/6067 3336/7396/6061 6764/7404/6069 +f 6756/7403/6068 3341/7402/6067 7847/7405/6070 +f 3342/7414/6079 3346/7406/6071 3343/7408/6073 +f 3343/7408/6073 3347/7407/6072 6769/7410/6075 +f 3346/7406/6071 6766/7411/6076 3347/7407/6072 +f 3347/7407/6072 6767/7412/6077 6768/7409/6074 +f 3344/7420/6082 3342/7414/6079 3348/7415/6080 +f 3344/7420/6082 3348/7415/6080 6748/7417/6036 +f 3348/7415/6080 3343/7408/6073 6770/7418/6081 +f 6749/7416/6037 3348/7415/6080 7856/7419/6038 +f 3345/7422/6084 3342/7414/6079 3349/7421/6083 +f 6772/7428/6088 3345/7422/6084 6771/7423/6085 +f 3349/7421/6083 3344/7420/6082 6747/7424/6047 +f 6771/7423/6085 3349/7421/6083 7850/7425/6050 +f 3346/7406/6071 3342/7414/6079 3350/7426/6086 +f 6766/7411/6076 3346/7406/6071 6765/7427/6087 +f 3345/7422/6084 6772/7428/6088 3350/7426/6086 +f 6765/7427/6087 3350/7426/6086 7848/7430/6090 +f 3351/7436/6096 3355/7431/6091 3352/7433/6093 +f 3352/7433/6093 3356/7432/6092 6763/7398/6063 +f 3355/7431/6091 6775/7434/6094 3356/7432/6092 +f 3356/7432/6092 6776/7435/6095 6764/7404/6069 +f 3353/7438/6098 3351/7436/6096 3357/7437/6097 +f 6778/7444/6104 3353/7438/6098 6777/7439/6099 +f 3357/7437/6097 3352/7433/6093 6762/7397/6062 +f 6777/7439/6099 3357/7437/6097 7857/7401/6066 +f 3351/7436/6096 3353/7438/6098 3354/7441/6101 +f 3354/7441/6101 3358/7440/6100 6781/7443/6103 +f 3353/7438/6098 6778/7444/6104 3358/7440/6100 +f 3358/7440/6100 6779/7445/6105 6780/7442/6102 +f 3355/7431/6091 3351/7436/6096 3359/7447/6107 +f 6775/7434/6094 3355/7431/6091 6774/7448/6108 +f 3359/7447/6107 3354/7441/6101 6782/7449/6109 +f 6774/7448/6108 3359/7447/6107 7845/7450/6110 +f 3360/7459/6119 3364/7451/6111 3361/7453/6113 +f 3361/7453/6113 3365/7452/6112 6787/7455/6115 +f 3364/7451/6111 6784/7456/6116 3365/7452/6112 +f 3365/7452/6112 6785/7457/6117 6786/7454/6114 +f 3362/7461/6121 3360/7459/6119 3366/7460/6120 +f 6766/7411/6076 3362/7461/6121 6767/7412/6077 +f 3366/7460/6120 3361/7453/6113 6788/7462/6122 +f 6767/7412/6077 3366/7460/6120 7858/7413/6078 +f 3360/7459/6119 3362/7461/6121 3363/7464/6124 +f 3363/7464/6124 3367/7463/6123 6790/7466/6126 +f 3362/7461/6121 6766/7411/6076 3367/7463/6123 +f 3367/7463/6123 6765/7427/6087 6789/7465/6125 +f 3364/7451/6111 3360/7459/6119 3368/7467/6127 +f 6784/7456/6116 3364/7451/6111 6783/7468/6128 +f 3368/7467/6127 3363/7464/6124 6791/7469/6129 +f 6783/7468/6128 3368/7467/6127 7846/7470/6130 +f 3369/7476/6136 3373/7471/6131 3370/7473/6133 +f 3370/7473/6133 3374/7472/6132 6781/7443/6103 +f 3373/7471/6131 6793/7474/6134 3374/7472/6132 +f 3374/7472/6132 6794/7475/6135 6782/7449/6109 +f 3371/7478/6138 3369/7476/6136 3375/7477/6137 +f 6796/7483/6143 3371/7478/6138 6795/7479/6139 +f 3375/7477/6137 3370/7473/6133 6780/7442/6102 +f 6795/7479/6139 3375/7477/6137 7859/7446/6106 +f 3369/7476/6136 3371/7478/6138 3372/7481/6141 +f 6799/7488/6148 3372/7481/6141 6798/7482/6142 +f 3376/7480/6140 3371/7478/6138 6797/7484/6144 +f 6798/7482/6142 3376/7480/6140 7861/7485/6145 +f 3373/7471/6131 3369/7476/6136 3377/7486/6146 +f 6793/7474/6134 3373/7471/6131 6792/7487/6147 +f 3377/7486/6146 3372/7481/6141 6800/7489/6149 +f 6792/7487/6147 3377/7486/6146 7843/7490/6150 +f 3378/7498/6158 3382/7491/6151 3379/7493/6153 +f 6805/7502/6161 3379/7493/6153 6804/7494/6154 +f 3383/7492/6152 3382/7491/6151 6803/7496/6156 +f 6804/7494/6154 3383/7492/6152 7862/7497/6157 +f 3380/7500/6160 3378/7498/6158 3384/7499/6159 +f 6784/7509/6116 3380/7500/6160 6785/7501/6117 +f 3384/7499/6159 3379/7493/6153 6806/7503/6162 +f 6785/7501/6117 3384/7499/6159 7860/7504/6118 +f 3378/7498/6158 3380/7500/6160 3381/7506/6164 +f 3381/7506/6164 3385/7505/6163 6808/7508/6166 +f 3380/7500/6160 6784/7509/6116 3385/7505/6163 +f 3385/7505/6163 6783/7510/6128 6807/7507/6165 +f 3382/7491/6151 3378/7498/6158 3386/7512/6167 +f 6802/7495/6155 3382/7491/6151 6801/7513/6168 +f 3386/7512/6167 3381/7506/6164 6809/7514/6169 +f 6801/7513/6168 3386/7512/6167 7844/7515/6170 +f 3387/7525/6180 3391/7516/6171 3388/7518/6173 +f 3388/7518/6173 3392/7517/6172 6799/7488/6148 +f 3391/7516/6171 6811/7519/6174 3392/7517/6172 +f 3392/7517/6172 6812/7520/6175 6800/7489/6149 +f 3387/7525/6180 3388/7518/6173 3389/7522/6177 +f 3389/7522/6177 3393/7521/6176 6814/7524/6179 +f 3388/7518/6173 6799/7488/6148 3393/7521/6176 +f 3393/7521/6176 6798/7482/6142 6813/7523/6178 +f 3390/7527/6182 3387/7525/6180 3394/7526/6181 +f 6817/7533/6188 3390/7527/6182 6816/7528/6183 +f 3394/7526/6181 3389/7522/6177 6815/7529/6184 +f 6816/7528/6183 3394/7526/6181 7863/7530/6185 +f 3387/7525/6180 3390/7527/6182 3391/7516/6171 +f 6811/7519/6174 3391/7516/6171 6810/7532/6187 +f 3390/7527/6182 6817/7533/6188 3395/7531/6186 +f 6810/7532/6187 3395/7531/6186 7841/7535/6190 +f 3397/7539/6194 3396/7536/6191 3401/7538/6193 +f 6823/7546/6201 3397/7539/6194 6822/7540/6195 +f 3401/7538/6193 3400/7537/6192 6821/7542/6197 +f 6822/7540/6195 3401/7538/6193 7864/7543/6198 +f 3396/7536/6191 3397/7539/6194 3398/7545/6200 +f 3398/7545/6200 3402/7544/6199 6802/7495/6155 +f 3397/7539/6194 6823/7546/6201 3402/7544/6199 +f 3402/7544/6199 6824/7547/6202 6803/7496/6156 +f 3396/7536/6191 3398/7545/6200 3399/7549/6204 +f 3399/7549/6204 3403/7548/6203 6826/7551/6206 +f 3398/7545/6200 6802/7495/6155 3403/7548/6203 +f 3403/7548/6203 6801/7513/6168 6825/7550/6205 +f 3396/7536/6191 3399/7549/6204 3400/7537/6192 +f 3400/7537/6192 3404/7552/6207 6820/7541/6196 +f 3404/7552/6207 3399/7549/6204 6827/7554/6209 +f 6819/7553/6208 3404/7552/6207 7842/7555/6210 +f 3406/7559/6214 3405/7556/6211 3410/7558/6213 +f 6829/7565/6220 3406/7559/6214 6828/7560/6215 +f 3410/7558/6213 3409/7557/6212 6813/7523/6178 +f 6828/7560/6215 3410/7558/6213 7861/7485/6145 +f 3405/7556/6211 3406/7559/6214 3407/7562/6217 +f 3407/7562/6217 3411/7561/6216 6832/7564/6219 +f 3406/7559/6214 6829/7565/6220 3411/7561/6216 +f 3411/7561/6216 6830/7566/6221 6831/7563/6218 +f 3408/7569/6224 3405/7556/6211 3412/7568/6223 +f 6835/7574/6229 3408/7569/6224 6834/7570/6225 +f 3412/7568/6223 3407/7562/6217 6833/7571/6226 +f 6834/7570/6225 3412/7568/6223 7869/7572/6227 +f 3405/7556/6211 3408/7569/6224 3409/7557/6212 +f 3409/7557/6212 3413/7573/6228 6814/7524/6179 +f 3408/7569/6224 6835/7574/6229 3413/7573/6228 +f 3413/7573/6228 6836/7575/6230 6815/7529/6184 +f 3415/7579/6234 3414/7576/6231 3419/7578/6233 +f 6841/7588/6243 3415/7579/6234 6840/7580/6235 +f 3419/7578/6233 3418/7577/6232 6839/7582/6237 +f 6840/7580/6235 3419/7578/6233 7870/7583/6238 +f 3414/7576/6231 3415/7579/6234 3416/7585/6240 +f 3416/7585/6240 3420/7584/6239 6844/7587/6242 +f 3415/7579/6234 6841/7588/6243 3420/7584/6239 +f 3420/7584/6239 6842/7589/6244 6843/7586/6241 +f 3417/7592/6247 3414/7576/6231 3421/7591/6246 +f 6823/7598/6201 3417/7592/6247 6824/7593/6202 +f 3421/7591/6246 3416/7585/6240 6845/7594/6248 +f 6824/7593/6202 3421/7591/6246 7862/7595/6157 +f 3414/7576/6231 3417/7592/6247 3418/7577/6232 +f 3418/7577/6232 3422/7596/6249 6838/7581/6236 +f 3417/7592/6247 6823/7598/6201 3422/7596/6249 +f 3422/7596/6249 6822/7599/6195 6837/7597/6250 +f 3424/7604/6254 3423/7601/6251 3428/7603/6253 +f 6847/7613/6260 3424/7604/6254 6846/7605/6255 +f 3428/7603/6253 3427/7602/6252 6795/7607/6139 +f 3428/7603/6253 6795/7607/6139 6846/7605/6255 +f 3423/7601/6251 3424/7604/6254 3425/7610/6257 +f 3425/7610/6257 3429/7609/6256 6850/7612/6259 +f 3424/7604/6254 6847/7613/6260 3429/7609/6256 +f 3429/7609/6256 6848/7614/6261 6849/7611/6258 +f 3426/7617/6264 3423/7601/6251 3430/7616/6263 +f 6829/7623/6220 3426/7617/6264 6830/7618/6221 +f 3430/7616/6263 3425/7610/6257 6851/7619/6265 +f 6830/7618/6221 3430/7616/6263 7871/7620/6222 +f 3423/7601/6251 3426/7617/6264 3427/7602/6252 +f 3427/7602/6252 3431/7621/6266 6796/7606/6143 +f 3426/7617/6264 6829/7623/6220 3431/7621/6266 +f 3431/7621/6266 6828/7624/6215 6797/7622/6144 +f 3433/7629/6270 3432/7626/6267 3437/7628/6269 +f 6853/7638/6276 3433/7629/6270 6852/7630/6271 +f 3437/7628/6269 3436/7627/6268 6843/7632/6241 +f 6852/7630/6271 3437/7628/6269 7872/7633/6245 +f 3432/7626/6267 3433/7629/6270 3434/7635/6273 +f 3434/7635/6273 3438/7634/6272 6856/7637/6275 +f 3433/7629/6270 6853/7638/6276 3438/7634/6272 +f 3438/7634/6272 6854/7639/6277 6855/7636/6274 +f 3435/7642/6280 3432/7626/6267 3439/7641/6279 +f 6805/7647/6161 3435/7642/6280 6806/7643/6162 +f 3439/7641/6279 3434/7635/6273 6857/7644/6281 +f 3439/7641/6279 6857/7644/6281 6806/7643/6162 +f 3432/7626/6267 3435/7642/6280 3436/7627/6268 +f 3436/7627/6268 3440/7645/6282 6844/7631/6242 +f 3435/7642/6280 6805/7647/6161 3440/7645/6282 +f 3440/7645/6282 6804/7648/6154 6845/7646/6248 +f 3441/7658/6288 3445/7650/6283 3442/7652/6285 +f 3442/7652/6285 3446/7651/6284 6859/7654/6287 +f 3445/7650/6283 6778/7655/6104 3446/7651/6284 +f 3446/7651/6284 6777/7656/6099 6858/7653/6286 +f 3443/7660/6290 3441/7658/6288 3447/7659/6289 +f 6862/7666/6296 3443/7660/6290 6861/7661/6291 +f 3447/7659/6289 3442/7652/6285 6860/7662/6292 +f 6861/7661/6291 3447/7659/6289 7875/7663/6293 +f 3441/7658/6288 3443/7660/6290 3444/7665/6295 +f 6847/7613/6260 3444/7665/6295 6848/7614/6261 +f 3443/7660/6290 6862/7666/6296 3448/7664/6294 +f 6848/7614/6261 3448/7664/6294 7873/7615/6262 +f 3445/7650/6283 3441/7658/6288 3449/7668/6298 +f 6778/7655/6104 3445/7650/6283 6779/7669/6105 +f 3449/7668/6298 3444/7665/6295 6846/7605/6255 +f 6779/7669/6105 3449/7668/6298 7859/7608/6106 +f 3450/7675/6304 3454/7670/6299 3451/7672/6301 +f 3451/7672/6301 3455/7671/6300 6865/7674/6303 +f 3455/7671/6300 3454/7670/6299 6855/7636/6274 +f 6864/7673/6302 3455/7671/6300 7874/7640/6278 +f 3452/7677/6306 3450/7675/6304 3456/7676/6305 +f 6868/7683/6312 3452/7677/6306 6867/7678/6307 +f 3456/7676/6305 3451/7672/6301 6866/7679/6308 +f 6867/7678/6307 3456/7676/6305 7876/7680/6309 +f 3450/7675/6304 3452/7677/6306 3453/7682/6311 +f 3453/7682/6311 3457/7681/6310 6787/7455/6115 +f 3452/7677/6306 6868/7683/6312 3457/7681/6310 +f 3457/7681/6310 6869/7684/6313 6788/7462/6122 +f 3454/7670/6299 3450/7675/6304 3458/7685/6314 +f 6856/7637/6275 3454/7670/6299 6857/7644/6281 +f 3458/7685/6314 3453/7682/6311 6786/7454/6114 +f 6857/7644/6281 3458/7685/6314 7860/7458/6118 +f 3459/7694/6320 3463/7686/6315 3460/7688/6317 +f 3460/7688/6317 3464/7687/6316 6871/7690/6319 +f 3463/7686/6315 6760/7691/6064 3464/7687/6316 +f 3464/7687/6316 6759/7692/6059 6870/7689/6318 +f 3461/7696/6322 3459/7694/6320 3465/7695/6321 +f 6874/7704/6328 3461/7696/6322 6873/7697/6323 +f 3465/7695/6321 3460/7688/6317 6872/7698/6324 +f 6873/7697/6323 3465/7695/6321 7877/7699/6325 +f 3459/7694/6320 3461/7696/6322 3462/7701/6327 +f 3462/7701/6327 3466/7700/6326 6859/7703/6287 +f 3461/7696/6322 6874/7704/6328 3466/7700/6326 +f 3466/7700/6326 6875/7705/6329 6860/7702/6292 +f 3463/7686/6315 3459/7694/6320 3467/7707/6330 +f 6760/7691/6064 3463/7686/6315 6761/7708/6065 +f 3467/7707/6330 3462/7701/6327 6858/7709/6286 +f 6761/7708/6065 3467/7707/6330 7857/7710/6066 +f 3468/7716/6336 3472/7711/6331 3469/7713/6333 +f 3469/7713/6333 3473/7712/6332 6877/7715/6335 +f 3472/7711/6331 6868/7683/6312 3473/7712/6332 +f 3473/7712/6332 6867/7678/6307 6876/7714/6334 +f 3470/7718/6338 3468/7716/6336 3474/7717/6337 +f 6880/7724/6344 3470/7718/6338 6879/7719/6339 +f 3474/7717/6337 3469/7713/6333 6878/7720/6340 +f 6879/7719/6339 3474/7717/6337 7878/7721/6341 +f 3468/7716/6336 3470/7718/6338 3471/7723/6343 +f 3471/7723/6343 3475/7722/6342 6769/7410/6075 +f 3470/7718/6338 6880/7724/6344 3475/7722/6342 +f 3475/7722/6342 6881/7725/6345 6770/7418/6081 +f 3472/7711/6331 3468/7716/6336 3476/7726/6346 +f 6868/7683/6312 3472/7711/6331 6869/7684/6313 +f 3476/7726/6346 3471/7723/6343 6768/7409/6074 +f 6869/7684/6313 3476/7726/6346 7858/7413/6078 +f 3477/7732/6352 3481/7727/6347 3478/7729/6349 +f 3478/7729/6349 3482/7728/6348 6883/7731/6351 +f 3482/7728/6348 3481/7727/6347 6741/7349/6019 +f 3482/7728/6348 6741/7349/6019 6882/7730/6350 +f 3479/7734/6354 3477/7732/6352 3483/7733/6353 +f 6886/7742/6360 3479/7734/6354 6885/7735/6355 +f 3483/7733/6353 3478/7729/6349 6884/7736/6356 +f 6885/7735/6355 3483/7733/6353 7879/7737/6357 +f 3477/7732/6352 3479/7734/6354 3480/7739/6359 +f 3480/7739/6359 3484/7738/6358 6871/7741/6319 +f 3479/7734/6354 6886/7742/6360 3484/7738/6358 +f 3484/7738/6358 6887/7743/6361 6872/7740/6324 +f 3481/7727/6347 3477/7732/6352 3485/7745/6362 +f 6742/7350/6020 3481/7727/6347 6743/7354/6024 +f 3485/7745/6362 3480/7739/6359 6870/7746/6318 +f 6743/7354/6024 3485/7745/6362 7855/7355/6025 +f 3486/7755/6368 3490/7747/6363 3487/7749/6365 +f 3487/7749/6365 3491/7748/6364 6889/7751/6367 +f 3490/7747/6363 6880/7752/6344 3491/7748/6364 +f 3491/7748/6364 6879/7753/6339 6888/7750/6366 +f 3488/7757/6370 3486/7755/6368 3492/7756/6369 +f 6892/7764/6376 3488/7757/6370 6891/7758/6371 +f 3492/7756/6369 3487/7749/6365 6890/7759/6372 +f 6891/7758/6371 3492/7756/6369 7880/7760/6373 +f 3486/7755/6368 3488/7757/6370 3489/7762/6375 +f 6751/7769/6041 3489/7762/6375 6752/7763/6042 +f 3488/7757/6370 6892/7764/6376 3493/7761/6374 +f 3493/7761/6374 6893/7765/6377 6752/7763/6042 +f 3490/7747/6363 3486/7755/6368 3494/7767/6378 +f 6880/7752/6344 3490/7747/6363 6881/7768/6345 +f 3494/7767/6378 3489/7762/6375 6750/7770/6035 +f 6881/7768/6345 3494/7767/6378 7856/7771/6038 +f 3496/7775/6382 3495/7772/6379 3500/7774/6381 +f 6724/7308/5978 3496/7775/6382 6725/7314/5984 +f 3500/7774/6381 3499/7773/6380 6882/7730/6350 +f 6725/7314/5984 3500/7774/6381 7853/7315/5985 +f 3495/7772/6379 3496/7775/6382 3497/7777/6384 +f 3497/7777/6384 3501/7776/6383 6895/7779/6386 +f 3496/7775/6382 6724/7308/5978 3501/7776/6383 +f 3501/7776/6383 6723/7307/5977 6894/7778/6385 +f 3498/7781/6388 3495/7772/6379 3502/7780/6387 +f 6898/7786/6393 3498/7781/6388 6897/7782/6389 +f 3502/7780/6387 3497/7777/6384 6896/7783/6390 +f 6897/7782/6389 3502/7780/6387 7867/7784/6391 +f 3495/7772/6379 3498/7781/6388 3499/7773/6380 +f 3499/7773/6380 3503/7785/6392 6883/7731/6351 +f 3498/7781/6388 6898/7786/6393 3503/7785/6392 +f 3503/7785/6392 6899/7787/6394 6884/7736/6356 +f 3505/7791/6398 3504/7788/6395 3509/7790/6397 +f 6904/7800/6405 3505/7791/6398 6903/7792/6399 +f 3509/7790/6397 3508/7789/6396 6902/7794/6401 +f 6903/7792/6399 3509/7790/6397 7868/7795/6402 +f 3504/7788/6395 3505/7791/6398 3506/7797/6404 +f 3506/7797/6404 3510/7796/6403 6727/7799/5991 +f 3505/7791/6398 6904/7800/6405 3510/7796/6403 +f 3510/7796/6403 6905/7801/6406 6728/7798/5992 +f 3507/7804/6408 3504/7788/6395 3511/7803/6407 +f 6892/7764/6376 3507/7804/6408 6893/7765/6377 +f 3511/7803/6407 3506/7797/6404 6726/7805/6008 +f 6893/7765/6377 3511/7803/6407 7854/7766/6010 +f 3504/7788/6395 3507/7804/6408 3508/7789/6396 +f 3508/7789/6396 3512/7806/6409 6901/7793/6400 +f 3507/7804/6408 6892/7764/6376 3512/7806/6409 +f 3512/7806/6409 6891/7758/6371 6900/7807/6410 +f 3513/7823/6426 3517/7808/6411 3514/7810/6413 +f 6637/6988/5713 3514/7810/6413 6638/6989/5714 +f 3517/7808/6411 6907/7811/6414 3518/7809/6412 +f 3518/7809/6412 6908/7812/6415 6638/6989/5714 +f 3513/7823/6426 3514/7810/6413 3515/7814/6417 +f 3515/7814/6417 3519/7813/6416 6910/7816/6419 +f 3514/7810/6413 6637/6988/5713 3519/7813/6416 +f 3519/7813/6416 6636/7000/5725 6909/7815/6418 +f 3513/7823/6426 3515/7814/6417 3516/7818/6421 +f 3516/7818/6421 3520/7817/6420 6913/7820/6423 +f 3515/7814/6417 6910/7816/6419 3520/7817/6420 +f 3520/7817/6420 6911/7821/6424 6912/7819/6422 +f 3517/7808/6411 3513/7823/6426 3521/7824/6427 +f 3517/7808/6411 3521/7824/6427 6907/7811/6414 +f 3521/7824/6427 3516/7818/6421 6914/7826/6429 +f 6906/7825/6428 3521/7824/6427 7893/7827/6430 +f 3522/7843/6446 3526/7828/6431 3523/7830/6433 +f 3523/7830/6433 3527/7829/6432 6919/7832/6435 +f 3526/7828/6431 6916/7833/6436 3527/7829/6432 +f 3527/7829/6432 6917/7834/6437 6918/7831/6434 +f 3522/7843/6446 3523/7830/6433 3524/7837/6440 +f 3524/7837/6440 3528/7836/6439 6652/7015/5740 +f 3523/7830/6433 6919/7832/6435 3528/7836/6439 +f 3528/7836/6439 6920/7838/6441 6653/7020/5745 +f 3522/7843/6446 3524/7837/6440 3525/7840/6443 +f 3525/7840/6443 3529/7839/6442 6922/7842/6445 +f 3529/7839/6442 3524/7837/6440 6651/7014/5739 +f 3529/7839/6442 6651/7014/5739 6921/7841/6444 +f 3526/7828/6431 3522/7843/6446 3530/7844/6447 +f 6916/7833/6436 3526/7828/6431 6915/7845/6448 +f 3525/7840/6443 6922/7842/6445 3530/7844/6447 +f 6915/7845/6448 3530/7844/6447 7894/7847/6450 +f 3531/7856/6456 3535/7848/6451 3532/7850/6453 +f 3532/7850/6453 3536/7849/6452 6925/7852/6455 +f 3535/7848/6451 6910/7853/6419 3536/7849/6452 +f 3536/7849/6452 6909/7854/6418 6924/7851/6454 +f 3533/7858/6458 3531/7856/6456 3537/7857/6457 +f 6817/7533/6188 3533/7858/6458 6818/7534/6189 +f 3537/7857/6457 3532/7850/6453 6926/7859/6459 +f 6818/7534/6189 3537/7857/6457 7841/7535/6190 +f 3531/7856/6456 3533/7858/6458 3534/7861/6461 +f 3534/7861/6461 3538/7860/6460 6928/7863/6463 +f 3533/7858/6458 6817/7533/6188 3538/7860/6460 +f 3538/7860/6460 6816/7528/6183 6927/7862/6462 +f 3535/7848/6451 3531/7856/6456 3539/7864/6464 +f 6910/7853/6419 3535/7848/6451 6911/7865/6424 +f 3539/7864/6464 3534/7861/6461 6929/7866/6465 +f 6911/7865/6424 3539/7864/6464 7895/7867/6425 +f 3540/7876/6471 3544/7868/6466 3541/7870/6468 +f 3541/7870/6468 3545/7869/6467 6820/7872/6196 +f 3544/7868/6466 6931/7873/6469 3545/7869/6467 +f 3545/7869/6467 6932/7874/6470 6821/7871/6197 +f 3542/7878/6473 3540/7876/6471 3546/7877/6472 +f 6934/7886/6477 3542/7878/6473 6933/7879/6474 +f 3546/7877/6472 3541/7870/6468 6819/7880/6208 +f 6933/7879/6474 3546/7877/6472 7842/7881/6210 +f 3540/7876/6471 3542/7878/6473 3543/7883/6476 +f 3543/7883/6476 3547/7882/6475 6919/7885/6435 +f 3542/7878/6473 6934/7886/6477 3547/7882/6475 +f 3547/7882/6475 6935/7887/6478 6920/7884/6441 +f 3544/7868/6466 3540/7876/6471 3548/7889/6479 +f 6931/7873/6469 3544/7868/6466 6930/7890/6480 +f 3548/7889/6479 3543/7883/6476 6918/7891/6434 +f 6930/7890/6480 3548/7889/6479 7896/7892/6438 +f 3550/7896/6484 3549/7893/6481 3554/7895/6483 +f 6355/6065/4963 3550/7896/6484 6356/6066/4964 +f 3554/7895/6483 3553/7894/6482 6588/7898/5585 +f 6356/6066/4964 3554/7895/6483 7761/6067/4965 +f 3549/7893/6481 3550/7896/6484 3551/7900/6486 +f 3551/7900/6486 3555/7899/6485 6907/7811/6414 +f 3550/7896/6484 6355/6065/4963 3555/7899/6485 +f 3555/7899/6485 6354/6060/4958 6908/7812/6415 +f 3552/7902/6488 3549/7893/6481 3556/7901/6487 +f 6937/7906/6491 3552/7902/6488 6936/7903/6489 +f 3556/7901/6487 3551/7900/6486 6906/7825/6428 +f 6936/7903/6489 3556/7901/6487 7893/7827/6430 +f 3549/7893/6481 3552/7902/6488 3553/7894/6482 +f 3553/7894/6482 3557/7904/6490 6589/7897/5590 +f 3552/7902/6488 6937/7906/6491 3557/7904/6490 +f 3557/7904/6490 6938/7907/6492 6590/7905/5591 +f 3559/7912/6496 3558/7909/6493 3563/7911/6495 +f 6922/7921/6445 3559/7912/6496 6923/7913/6449 +f 3563/7911/6495 3562/7910/6494 6941/7915/6498 +f 6923/7913/6449 3563/7911/6495 7894/7916/6450 +f 3558/7909/6493 3559/7912/6496 3560/7918/6500 +f 3560/7918/6500 3564/7917/6499 6358/7920/4971 +f 3559/7912/6496 6922/7921/6445 3564/7917/6499 +f 3564/7917/6499 6921/7922/6444 6359/7919/4972 +f 3561/7925/6502 3558/7909/6493 3565/7924/6501 +f 6598/7931/5605 3561/7925/6502 6599/7926/5611 +f 3565/7924/6501 3560/7918/6500 6357/7927/4982 +f 6599/7926/5611 3565/7924/6501 7762/7928/4985 +f 3558/7909/6493 3561/7925/6502 3562/7910/6494 +f 3562/7910/6494 3566/7929/6503 6940/7914/6497 +f 3561/7925/6502 6598/7931/5605 3566/7929/6503 +f 3566/7929/6503 6597/7932/5604 6939/7930/6504 +f 3568/7937/6508 3567/7934/6505 3572/7936/6507 +f 6946/7946/6516 3568/7937/6508 6945/7938/6509 +f 3572/7936/6507 3571/7935/6506 6944/7940/6511 +f 6945/7938/6509 3572/7936/6507 7833/7941/5624 +f 3567/7934/6505 3568/7937/6508 3569/7943/6513 +f 3569/7943/6513 3573/7942/6512 6949/7945/6515 +f 3568/7937/6508 6946/7946/6516 3573/7942/6512 +f 3573/7942/6512 6947/7947/6517 6948/7944/6514 +f 3570/7950/6520 3567/7934/6505 3574/7949/6519 +f 6721/7309/5979 3570/7950/6520 6722/7310/5980 +f 3574/7949/6519 3569/7943/6513 6950/7951/6521 +f 6722/7310/5980 3574/7949/6519 7865/7311/5981 +f 3567/7934/6505 3570/7950/6520 3571/7935/6506 +f 3571/7935/6506 3575/7952/6522 6943/7939/6510 +f 3570/7950/6520 6721/7309/5979 3575/7952/6522 +f 3575/7952/6522 6720/7302/5972 6942/7953/6523 +f 3577/7957/6527 3576/7954/6524 3581/7956/6526 +f 6952/7963/6533 3577/7957/6527 6951/7958/6528 +f 3581/7956/6526 3580/7955/6525 6729/7319/5989 +f 6951/7958/6528 3581/7956/6526 7866/7323/5993 +f 3576/7954/6524 3577/7957/6527 3578/7960/6530 +f 3578/7960/6530 3582/7959/6529 6955/7962/6532 +f 3577/7957/6527 6952/7963/6533 3582/7959/6529 +f 3582/7959/6529 6953/7964/6534 6954/7961/6531 +f 3579/7967/6537 3576/7954/6524 3583/7966/6536 +f 6958/7972/6541 3579/7967/6537 6957/7968/6538 +f 3583/7966/6536 3578/7960/6530 6956/7969/6539 +f 6957/7968/6538 3583/7966/6536 7834/7970/5640 +f 3576/7954/6524 3579/7967/6537 3580/7955/6525 +f 3580/7955/6525 3584/7971/6540 6730/7320/5990 +f 3579/7967/6537 6958/7972/6541 3584/7971/6540 +f 3584/7971/6540 6959/7973/6542 6731/7328/5998 +f 3586/7977/6546 3585/7974/6543 3590/7976/6545 +f 6835/7986/6229 3586/7977/6546 6836/7978/6230 +f 3590/7976/6545 3589/7975/6544 6927/7980/6462 +f 6836/7978/6230 3590/7976/6545 7863/7981/6185 +f 3585/7974/6543 3586/7977/6546 3587/7983/6548 +f 3587/7983/6548 3591/7982/6547 6961/7985/6550 +f 3586/7977/6546 6835/7986/6229 3591/7982/6547 +f 3591/7982/6547 6834/7987/6225 6960/7984/6549 +f 3588/7990/6552 3585/7974/6543 3592/7989/6551 +f 6964/7996/6557 3588/7990/6552 6963/7991/6553 +f 3592/7989/6551 3587/7983/6548 6962/7992/6554 +f 3592/7989/6551 6962/7992/6554 6963/7991/6553 +f 3585/7974/6543 3588/7990/6552 3589/7975/6544 +f 3589/7975/6544 3593/7994/6556 6928/7979/6463 +f 3588/7990/6552 6964/7996/6557 3593/7994/6556 +f 3593/7994/6556 6965/7997/6558 6929/7995/6465 +f 3595/8002/6562 3594/7999/6559 3599/8001/6561 +f 6970/8009/6569 3595/8002/6562 6969/8003/6563 +f 3599/8001/6561 3598/8000/6560 6968/8005/6565 +f 3599/8001/6561 6968/8005/6565 6969/8003/6563 +f 3594/7999/6559 3595/8002/6562 3596/8008/6568 +f 3596/8008/6568 3600/8007/6567 6838/7581/6236 +f 3595/8002/6562 6970/8009/6569 3600/8007/6567 +f 3600/8007/6567 6971/8010/6570 6839/7582/6237 +f 3597/8012/6572 3594/7999/6559 3601/8011/6571 +f 6931/8016/6469 3597/8012/6572 6932/8013/6470 +f 3601/8011/6571 3596/8008/6568 6837/7597/6250 +f 6932/8013/6470 3601/8011/6571 7864/7600/6198 +f 3594/7999/6559 3597/8012/6572 3598/8000/6560 +f 3598/8000/6560 3602/8014/6573 6967/8004/6564 +f 3597/8012/6572 6931/8016/6469 3602/8014/6573 +f 3602/8014/6573 6930/8017/6480 6966/8015/6574 +f 3603/8027/6583 3607/8019/6575 3604/8021/6577 +f 3604/8021/6577 3608/8020/6576 6976/8023/6579 +f 3607/8019/6575 6973/8024/6580 3608/8020/6576 +f 3608/8020/6576 6974/8025/6581 6975/8022/6578 +f 3605/8029/6585 3603/8027/6583 3609/8028/6584 +f 6979/8037/6591 3605/8029/6585 6978/8030/6586 +f 3609/8028/6584 3604/8021/6577 6977/8031/6587 +f 6978/8030/6586 3609/8028/6584 7897/8032/6588 +f 3603/8027/6583 3605/8029/6585 3606/8034/6590 +f 3606/8034/6590 3610/8033/6589 6964/8036/6557 +f 3605/8029/6585 6979/8037/6591 3610/8033/6589 +f 3610/8033/6589 6980/8038/6592 6965/8035/6558 +f 3607/8019/6575 3603/8027/6583 3611/8040/6593 +f 6973/8024/6580 3607/8019/6575 6972/8041/6594 +f 3611/8040/6593 3606/8034/6590 6963/8042/6553 +f 6972/8041/6594 3611/8040/6593 7891/8043/6555 +f 3612/8052/6600 3616/8044/6595 3613/8046/6597 +f 3613/8046/6597 3617/8045/6596 6982/8048/6599 +f 3616/8044/6595 6967/8049/6564 3617/8045/6596 +f 3617/8045/6596 6966/8050/6574 6981/8047/6598 +f 3614/8054/6602 3612/8052/6600 3618/8053/6601 +f 6985/8062/6610 3614/8054/6602 6984/8055/6603 +f 3618/8053/6601 3613/8046/6597 6983/8056/6604 +f 6984/8055/6603 3618/8053/6601 7898/8057/6605 +f 3612/8052/6600 3614/8054/6602 3615/8059/6607 +f 3615/8059/6607 3619/8058/6606 6988/8061/6609 +f 3614/8054/6602 6985/8062/6610 3619/8058/6606 +f 3619/8058/6606 6986/8063/6611 6987/8060/6608 +f 3616/8044/6595 3612/8052/6600 3620/8065/6613 +f 6967/8049/6564 3616/8044/6595 6968/8066/6565 +f 3620/8065/6613 3615/8059/6607 6989/8067/6614 +f 6968/8066/6565 3620/8065/6613 7892/8068/6566 +f 3622/8077/6623 3621/8069/6615 3626/8071/6617 +f 3622/8077/6623 3626/8071/6617 6994/8073/6619 +f 3626/8071/6617 3625/8070/6616 6992/8075/6621 +f 3626/8071/6617 6992/8075/6621 6993/8072/6618 +f 3621/8069/6615 3622/8077/6623 3623/8079/6625 +f 3623/8079/6625 3627/8078/6624 6976/8023/6579 +f 3622/8077/6623 6994/8073/6619 3627/8078/6624 +f 3627/8078/6624 6995/8080/6626 6977/8031/6587 +f 3624/8082/6628 3621/8069/6615 3628/8081/6627 +f 6997/8086/6632 3624/8082/6628 6996/8083/6629 +f 3628/8081/6627 3623/8079/6625 6975/8022/6578 +f 6996/8083/6629 3628/8081/6627 7889/8026/6582 +f 3621/8069/6615 3624/8082/6628 3625/8070/6616 +f 3625/8070/6616 3629/8084/6630 6991/8074/6620 +f 3629/8084/6630 3624/8082/6628 6998/8087/6633 +f 6990/8085/6631 3629/8084/6630 7887/8088/6634 +f 3631/8092/6638 3630/8089/6635 3635/8091/6637 +f 6985/8101/6610 3631/8092/6638 6986/8093/6611 +f 3635/8091/6637 3634/8090/6636 7001/8095/6640 +f 6986/8093/6611 3635/8091/6637 7890/8096/6612 +f 3630/8089/6635 3631/8092/6638 3632/8098/6642 +f 3632/8098/6642 3636/8097/6641 7003/8100/6644 +f 3631/8092/6638 6985/8101/6610 3636/8097/6641 +f 3636/8097/6641 6984/8102/6603 7002/8099/6643 +f 3633/8105/6646 3630/8089/6635 3637/8104/6645 +f 7006/8111/6652 3633/8105/6646 7005/8106/6647 +f 3632/8098/6642 7003/8100/6644 3637/8104/6645 +f 3637/8104/6645 7004/8107/6648 7005/8106/6647 +f 3630/8089/6635 3633/8105/6646 3634/8090/6636 +f 7000/8094/6639 3634/8090/6636 6999/8110/6651 +f 3633/8105/6646 7006/8111/6652 3638/8109/6650 +f 6999/8110/6651 3638/8109/6650 7888/8113/6654 +f 3639/8122/6663 3643/8114/6655 3640/8116/6657 +f 3640/8116/6657 3644/8115/6656 7012/8118/6659 +f 3643/8114/6655 7009/8119/6660 3644/8115/6656 +f 3644/8115/6656 7010/8120/6661 7011/8117/6658 +f 3641/8128/6669 3639/8122/6663 3645/8123/6664 +f 3641/8128/6669 3645/8123/6664 7015/8125/6666 +f 3640/8116/6657 7012/8118/6659 3645/8123/6664 +f 3645/8123/6664 7013/8126/6667 7014/8124/6665 +f 3639/8122/6663 3641/8128/6669 3642/8130/6671 +f 3642/8130/6671 3646/8129/6670 6994/8132/6619 +f 3641/8128/6669 7015/8125/6666 3646/8129/6670 +f 3646/8129/6670 7016/8133/6672 6995/8131/6626 +f 3643/8114/6655 3639/8122/6663 3647/8135/6673 +f 7009/8119/6660 3643/8114/6655 7008/8136/6674 +f 3647/8135/6673 3642/8130/6671 6993/8137/6618 +f 7008/8136/6674 3647/8135/6673 7885/8138/6622 +f 3648/8147/6680 3652/8139/6675 3649/8141/6677 +f 3649/8141/6677 3653/8140/6676 7018/8143/6679 +f 3652/8139/6675 7003/8144/6644 3653/8140/6676 +f 3653/8140/6676 7002/8145/6643 7017/8142/6678 +f 3650/8153/6686 3648/8147/6680 3654/8148/6681 +f 3650/8153/6686 3654/8148/6681 7021/8150/6683 +f 3649/8141/6677 7018/8143/6679 3654/8148/6681 +f 3654/8148/6681 7019/8151/6684 7020/8149/6682 +f 3648/8147/6680 3650/8153/6686 3651/8155/6688 +f 3651/8155/6688 3655/8154/6687 7024/8157/6690 +f 3650/8153/6686 7021/8150/6683 3655/8154/6687 +f 3655/8154/6687 7022/8158/6691 7023/8156/6689 +f 3652/8139/6675 3648/8147/6680 3656/8160/6693 +f 7003/8144/6644 3652/8139/6675 7004/8161/6648 +f 3656/8160/6693 3651/8155/6688 7025/8162/6694 +f 7004/8161/6648 3656/8160/6693 7886/8163/6649 +f 3657/8171/6702 3661/8164/6695 3658/8166/6697 +f 3658/8166/6697 3662/8165/6696 7030/8168/6699 +f 3661/8164/6695 7027/8169/6700 3662/8165/6696 +f 3662/8165/6696 7028/8170/6701 7029/8167/6698 +f 3659/8173/6704 3657/8171/6702 3663/8172/6703 +f 7012/8181/6659 3659/8173/6704 7013/8174/6667 +f 3663/8172/6703 3658/8166/6697 7031/8175/6705 +f 7013/8174/6667 3663/8172/6703 7899/8176/6668 +f 3657/8171/6702 3659/8173/6704 3660/8178/6707 +f 3660/8178/6707 3664/8177/6706 7033/8180/6709 +f 3659/8173/6704 7012/8181/6659 3664/8177/6706 +f 7032/8179/6708 3664/8177/6706 7883/8183/6662 +f 3661/8164/6695 3657/8171/6702 3665/8184/6710 +f 7027/8169/6700 3661/8164/6695 7026/8185/6711 +f 3665/8184/6710 3660/8178/6707 7034/8186/6712 +f 7026/8185/6711 3665/8184/6710 7901/8187/6713 +f 3666/8193/6719 3670/8188/6714 3667/8190/6716 +f 3667/8190/6716 3671/8189/6715 7021/8150/6683 +f 3670/8188/6714 7036/8191/6717 3671/8189/6715 +f 7022/8158/6691 3671/8189/6715 7884/8159/6692 +f 3668/8195/6721 3666/8193/6719 3672/8194/6720 +f 7039/8201/6727 3668/8195/6721 7038/8196/6722 +f 3672/8194/6720 3667/8190/6716 7020/8149/6682 +f 7038/8196/6722 3672/8194/6720 7900/8152/6685 +f 3666/8193/6719 3668/8195/6721 3669/8198/6724 +f 3669/8198/6724 3673/8197/6723 7042/8200/6726 +f 3668/8195/6721 7039/8201/6727 3673/8197/6723 +f 3673/8197/6723 7040/8202/6728 7041/8199/6725 +f 3670/8188/6714 3666/8193/6719 3674/8204/6729 +f 7036/8191/6717 3670/8188/6714 7035/8205/6730 +f 3674/8204/6729 3669/8198/6724 7043/8206/6731 +f 7035/8205/6730 3674/8204/6729 7902/8207/6732 +f 3676/8211/6736 3675/8208/6733 3680/8210/6735 +f 3676/8211/6736 3680/8210/6735 6949/7945/6515 +f 3680/8210/6735 3679/8209/6734 6894/7778/6385 +f 6950/7951/6521 3680/8210/6735 7865/7311/5981 +f 3677/8213/6738 3675/8208/6733 3681/8212/6737 +f 7027/8169/6700 3677/8213/6738 7028/8170/6701 +f 3681/8212/6737 3676/8211/6736 6948/7944/6514 +f 7028/8170/6701 3681/8212/6737 7881/7948/6518 +f 3678/8215/6740 3675/8208/6733 3682/8214/6739 +f 7045/8218/6743 3678/8215/6740 7044/8216/6741 +f 3677/8213/6738 7027/8169/6700 3682/8214/6739 +f 3682/8214/6739 7026/8185/6711 7044/8216/6741 +f 3675/8208/6733 3678/8215/6740 3679/8209/6734 +f 3679/8209/6734 3683/8217/6742 6895/7779/6386 +f 3678/8215/6740 7045/8218/6743 3683/8217/6742 +f 3683/8217/6742 7046/8219/6744 6896/7783/6390 +f 3685/8225/6750 3684/8220/6745 3689/8222/6747 +f 3685/8225/6750 3689/8222/6747 7042/8200/6726 +f 3689/8222/6747 3688/8221/6746 7049/8224/6749 +f 3689/8222/6747 7049/8224/6749 7043/8206/6731 +f 3686/8227/6752 3684/8220/6745 3690/8226/6751 +f 6952/8231/6533 3686/8227/6752 6953/8228/6534 +f 3690/8226/6751 3685/8225/6750 7041/8199/6725 +f 6953/8228/6534 3690/8226/6751 7882/8203/6535 +f 3687/8230/6754 3684/8220/6745 3691/8229/6753 +f 6904/7800/6405 3687/8230/6754 6905/7801/6406 +f 3686/8227/6752 6952/8231/6533 3691/8229/6753 +f 6905/7801/6406 3691/8229/6753 7866/7802/5993 +f 3684/8220/6745 3687/8230/6754 3688/8221/6746 +f 3688/8221/6746 3692/8233/6755 7048/8223/6748 +f 3687/8230/6754 6904/7800/6405 3692/8233/6755 +f 3692/8233/6755 6903/7792/6399 7047/8234/6756 +f 3694/8238/6760 3693/8235/6757 3698/8237/6759 +f 7051/8246/6764 3694/8238/6760 7050/8239/6761 +f 3697/8236/6758 6601/8240/5622 3698/8237/6759 +f 7050/8239/6761 3698/8237/6759 7763/7908/5592 +f 3693/8235/6757 3694/8238/6760 3695/8243/6763 +f 3695/8243/6763 3699/8242/6762 7030/8245/6699 +f 3694/8238/6760 7051/8246/6764 3699/8242/6762 +f 3699/8242/6762 7052/8247/6765 7031/8244/6705 +f 3696/8249/6767 3693/8235/6757 3700/8248/6766 +f 6946/8255/6516 3696/8249/6767 6947/8250/6517 +f 3700/8248/6766 3695/8243/6763 7029/8251/6698 +f 6947/8250/6517 3700/8248/6766 7881/8252/6518 +f 3697/8236/6758 3693/8235/6757 3701/8253/6768 +f 6601/8240/5622 3697/8236/6758 6602/8254/5623 +f 3696/8249/6767 6946/8255/6516 3701/8253/6768 +f 3701/8253/6768 6945/8256/6509 6602/8254/5623 +f 3703/8261/6772 3702/8258/6769 3707/8260/6771 +f 7039/8270/6727 3703/8261/6772 7040/8262/6728 +f 3707/8260/6771 3706/8259/6770 6954/8264/6531 +f 7040/8262/6728 3707/8260/6771 7882/8265/6535 +f 3702/8258/6769 3703/8261/6772 3704/8267/6774 +f 3704/8267/6774 3708/8266/6773 7054/8269/6776 +f 3703/8261/6772 7039/8270/6727 3708/8266/6773 +f 3708/8266/6773 7038/8271/6722 7053/8268/6775 +f 3705/8275/6779 3702/8258/6769 3709/8273/6777 +f 3705/8275/6779 3709/8273/6777 6610/6895/5637 +f 3709/8273/6777 3704/8267/6774 7055/8274/6778 +f 6611/6901/5643 3709/8273/6777 7764/6862/5608 +f 3706/8259/6770 3702/8258/6769 3710/8276/6780 +f 3706/8259/6770 3710/8276/6780 6955/8263/6532 +f 3710/8276/6780 3705/8275/6779 6609/6894/5636 +f 3710/8276/6780 6609/6894/5636 6956/8277/6539 +f 3711/8281/6784 3715/8278/6781 3712/8280/6783 +f 3712/8280/6783 3716/8279/6782 6937/7906/6491 +f 3715/8278/6781 7051/8246/6764 3716/8279/6782 +f 3716/8279/6782 7050/8239/6761 6938/7907/6492 +f 3713/8283/6786 3711/8281/6784 3717/8282/6785 +f 7057/8287/6790 3713/8283/6786 7056/8284/6787 +f 3717/8282/6785 3712/8280/6783 6936/7903/6489 +f 7056/8284/6787 3717/8282/6785 7893/7827/6430 +f 3711/8281/6784 3713/8283/6786 3714/8286/6789 +f 3714/8286/6789 3718/8285/6788 7015/8125/6666 +f 3713/8283/6786 7057/8287/6790 3718/8285/6788 +f 3718/8285/6788 7058/8288/6791 7016/8133/6672 +f 3715/8278/6781 3711/8281/6784 3719/8289/6792 +f 7051/8246/6764 3715/8278/6781 7052/8247/6765 +f 3719/8289/6792 3714/8286/6789 7014/8124/6665 +f 7052/8247/6765 3719/8289/6792 7899/8127/6668 +f 3720/8295/6798 3724/8290/6793 3721/8292/6795 +f 3721/8292/6795 3725/8291/6794 7060/8294/6797 +f 3724/8290/6793 7018/8143/6679 3725/8291/6794 +f 3725/8291/6794 7017/8142/6678 7059/8293/6796 +f 3722/8297/6800 3720/8295/6798 3726/8296/6799 +f 6940/8305/6497 3722/8297/6800 6941/8298/6498 +f 3726/8296/6799 3721/8292/6795 7061/8299/6801 +f 6941/8298/6498 3726/8296/6799 7894/8300/6450 +f 3720/8295/6798 3722/8297/6800 3723/8302/6803 +f 3723/8302/6803 3727/8301/6802 7054/8304/6776 +f 3722/8297/6800 6940/8305/6497 3727/8301/6802 +f 3727/8301/6802 6939/8306/6504 7055/8303/6778 +f 3724/8290/6793 3720/8295/6798 3728/8308/6804 +f 7018/8143/6679 3724/8290/6793 7019/8151/6684 +f 3728/8308/6804 3723/8302/6803 7053/8309/6775 +f 7019/8151/6684 3728/8308/6804 7900/8152/6685 +f 3730/8313/6808 3729/8310/6805 3733/8312/6807 +f 3730/8313/6808 3733/8312/6807 6913/7820/6423 +f 3732/8311/6806 7057/8287/6790 3733/8312/6807 +f 3733/8312/6807 7056/8284/6787 6914/7826/6429 +f 3731/8315/6810 3729/8310/6805 3734/8314/6809 +f 6979/8318/6591 3731/8315/6810 6980/8316/6592 +f 3734/8314/6809 3730/8313/6808 6912/7819/6422 +f 6980/8316/6592 3734/8314/6809 7895/7822/6425 +f 3732/8311/6806 3729/8310/6805 3735/8317/6811 +f 3732/8311/6806 3735/8317/6811 7057/8287/6790 +f 3731/8315/6810 6979/8318/6591 3735/8317/6811 +f 3735/8317/6811 6978/8319/6586 7058/8288/6791 +f 3737/8328/6815 3736/8320/6812 3740/8322/6814 +f 3737/8328/6815 3740/8322/6814 6982/8324/6599 +f 3739/8321/6813 7060/8325/6797 3740/8322/6814 +f 3740/8322/6814 7059/8326/6796 6983/8323/6604 +f 3738/8330/6817 3736/8320/6812 3741/8329/6816 +f 6916/8336/6436 3738/8330/6817 6917/8331/6437 +f 3741/8329/6816 3737/8328/6815 6981/8332/6598 +f 6917/8331/6437 3741/8329/6816 7896/8333/6438 +f 3739/8321/6813 3736/8320/6812 3742/8334/6818 +f 3739/8321/6813 3742/8334/6818 7060/8325/6797 +f 3738/8330/6817 6916/8336/6436 3742/8334/6818 +f 3742/8334/6818 6915/8337/6448 7061/8335/6801 +f 3744/8342/6822 3743/8339/6819 3748/8341/6821 +f 7045/8351/6743 3744/8342/6822 7046/8343/6744 +f 3748/8341/6821 3747/8340/6820 7064/8345/6824 +f 7046/8343/6744 3748/8341/6821 7867/8346/6391 +f 3743/8339/6819 3744/8342/6822 3745/8348/6826 +f 3745/8348/6826 3749/8347/6825 7066/8350/6828 +f 3744/8342/6822 7045/8351/6743 3749/8347/6825 +f 3749/8347/6825 7044/8352/6741 7065/8349/6827 +f 3746/8355/6830 3743/8339/6819 3750/8354/6829 +f 7069/8361/6836 3746/8355/6830 7068/8356/6831 +f 3750/8354/6829 3745/8348/6826 7067/8357/6832 +f 7068/8356/6831 3750/8354/6829 7903/8358/6833 +f 3743/8339/6819 3746/8355/6830 3747/8340/6820 +f 3747/8340/6820 3751/8359/6834 7063/8344/6823 +f 3746/8355/6830 7069/8361/6836 3751/8359/6834 +f 3751/8359/6834 7070/8362/6837 7062/8360/6835 +f 3753/8367/6842 3752/8364/6839 3757/8366/6841 +f 7075/8374/6849 3753/8367/6842 7074/8368/6843 +f 3757/8366/6841 3756/8365/6840 7073/8370/6845 +f 7074/8368/6843 3757/8366/6841 7904/8371/6846 +f 3752/8364/6839 3753/8367/6842 3754/8373/6848 +f 3754/8373/6848 3758/8372/6847 7048/8223/6748 +f 3753/8367/6842 7075/8374/6849 3758/8372/6847 +f 3758/8372/6847 7076/8375/6850 7049/8224/6749 +f 3755/8377/6852 3752/8364/6839 3759/8376/6851 +f 7078/8381/6856 3755/8377/6852 7077/8378/6853 +f 3759/8376/6851 3754/8373/6848 7047/8234/6756 +f 7077/8378/6853 3759/8376/6851 7868/7795/6402 +f 3752/8364/6839 3755/8377/6852 3756/8365/6840 +f 3756/8365/6840 3760/8379/6854 7072/8369/6844 +f 3755/8377/6852 7078/8381/6856 3760/8379/6854 +f 3760/8379/6854 7079/8382/6857 7071/8380/6855 +f 3761/8389/6862 3765/8384/6859 3762/8386/6861 +f 3762/8386/6861 3766/8385/6860 7033/8388/6709 +f 3765/8384/6859 7066/8350/6828 3766/8385/6860 +f 3766/8385/6860 7065/8349/6827 7034/8387/6712 +f 3763/8394/6866 3761/8389/6862 3767/8390/6863 +f 3763/8394/6866 3767/8390/6863 7081/8392/6865 +f 3767/8390/6863 3762/8386/6861 7032/8393/6708 +f 3767/8390/6863 7032/8393/6708 7080/8391/6864 +f 3761/8389/6862 3763/8394/6866 3764/8396/6868 +f 3764/8396/6868 3768/8395/6867 7084/8398/6870 +f 3768/8395/6867 3763/8394/6866 7082/8399/6871 +f 3768/8395/6867 7082/8399/6871 7083/8397/6869 +f 3765/8384/6859 3761/8389/6862 3769/8401/6873 +f 3765/8384/6859 3769/8401/6873 7066/8350/6828 +f 3769/8401/6873 3764/8396/6868 7085/8402/6874 +f 3769/8401/6873 7085/8402/6874 7067/8357/6832 +f 3770/8410/6882 3774/8403/6875 3771/8405/6877 +f 7090/8413/6885 3771/8405/6877 7089/8406/6878 +f 3774/8403/6875 7087/8407/6879 3775/8404/6876 +f 3775/8404/6876 7088/8408/6880 7089/8406/6878 +f 3772/8412/6884 3770/8410/6882 3776/8411/6883 +f 7036/8191/6717 3772/8412/6884 7037/8192/6718 +f 3771/8405/6877 7090/8413/6885 3776/8411/6883 +f 3776/8411/6883 7091/8414/6886 7037/8192/6718 +f 3770/8410/6882 3772/8412/6884 3773/8416/6888 +f 3773/8416/6888 3777/8415/6887 7075/8374/6849 +f 3772/8412/6884 7036/8191/6717 3777/8415/6887 +f 3777/8415/6887 7035/8205/6730 7076/8375/6850 +f 3774/8403/6875 3770/8410/6882 3778/8417/6889 +f 7087/8407/6879 3774/8403/6875 7086/8418/6890 +f 3773/8416/6888 7075/8374/6849 3778/8417/6889 +f 3778/8417/6889 7074/8368/6843 7086/8418/6890 +f 3780/8422/6894 3779/8419/6891 3784/8421/6893 +f 7009/8119/6660 3780/8422/6894 7010/8120/6661 +f 3784/8421/6893 3783/8420/6892 7080/8391/6864 +f 7010/8120/6661 3784/8421/6893 7883/8121/6662 +f 3779/8419/6891 3780/8422/6894 3781/8424/6896 +f 3781/8424/6896 3785/8423/6895 7093/8426/6898 +f 3780/8422/6894 7009/8119/6660 3785/8423/6895 +f 3785/8423/6895 7008/8136/6674 7092/8425/6897 +f 3782/8428/6900 3779/8419/6891 3786/8427/6899 +f 7096/8433/6905 3782/8428/6900 7095/8429/6901 +f 3786/8427/6899 3781/8424/6896 7094/8430/6902 +f 7095/8429/6901 3786/8427/6899 7911/8431/6903 +f 3779/8419/6891 3782/8428/6900 3783/8420/6892 +f 3783/8420/6892 3787/8432/6904 7081/8392/6865 +f 3782/8428/6900 7096/8433/6905 3787/8432/6904 +f 3787/8432/6904 7097/8434/6906 7082/8399/6871 +f 3789/8438/6910 3788/8435/6907 3793/8437/6909 +f 7102/8445/6917 3789/8438/6910 7101/8439/6911 +f 3793/8437/6909 3792/8436/6908 7100/8441/6913 +f 7101/8439/6911 3793/8437/6909 7912/8442/6914 +f 3788/8435/6907 3789/8438/6910 3790/8444/6916 +f 3790/8444/6916 3794/8443/6915 7024/8157/6690 +f 3789/8438/6910 7102/8445/6917 3794/8443/6915 +f 3794/8443/6915 7103/8446/6918 7025/8162/6694 +f 3791/8448/6920 3788/8435/6907 3795/8447/6919 +f 7090/8413/6885 3791/8448/6920 7091/8414/6886 +f 3795/8447/6919 3790/8444/6916 7023/8156/6689 +f 7091/8414/6886 3795/8447/6919 7884/8159/6692 +f 3788/8435/6907 3791/8448/6920 3792/8436/6908 +f 3792/8436/6908 3796/8449/6921 7099/8440/6912 +f 3791/8448/6920 7090/8413/6885 3796/8449/6921 +f 3796/8449/6921 7089/8406/6878 7098/8450/6922 +f 3797/8459/6926 3801/8451/6923 3798/8453/6925 +f 3798/8453/6925 3802/8452/6924 6991/8455/6620 +f 3802/8452/6924 3801/8451/6923 7092/8457/6897 +f 6992/8454/6621 3802/8452/6924 7885/8458/6622 +f 3799/8461/6928 3797/8459/6926 3803/8460/6927 +f 7105/8469/6934 3799/8461/6928 7104/8462/6929 +f 3803/8460/6927 3798/8453/6925 6990/8463/6631 +f 7104/8462/6929 3803/8460/6927 7887/8464/6634 +f 3797/8459/6926 3799/8461/6928 3800/8466/6931 +f 3800/8466/6931 3804/8465/6930 7108/8468/6933 +f 3799/8461/6928 7105/8469/6934 3804/8465/6930 +f 3804/8465/6930 7106/8470/6935 7107/8467/6932 +f 3801/8451/6923 3797/8459/6926 3805/8472/6937 +f 3801/8451/6923 3805/8472/6937 7093/8456/6898 +f 3805/8472/6937 3800/8466/6931 7109/8474/6938 +f 7094/8473/6902 3805/8472/6937 7911/8475/6903 +f 3806/8484/6947 3810/8476/6939 3807/8478/6941 +f 3807/8478/6941 3811/8477/6940 7114/8480/6943 +f 3810/8476/6939 7111/8481/6944 3811/8477/6940 +f 3811/8477/6940 7112/8482/6945 7113/8479/6942 +f 3808/8486/6949 3806/8484/6947 3812/8485/6948 +f 7006/8111/6652 3808/8486/6949 7007/8112/6653 +f 3812/8485/6948 3807/8478/6941 7115/8487/6950 +f 7007/8112/6653 3812/8485/6948 7888/8113/6654 +f 3806/8484/6947 3808/8486/6949 3809/8489/6952 +f 7102/8493/6917 3809/8489/6952 7103/8490/6918 +f 3808/8486/6949 7006/8111/6652 3813/8488/6951 +f 7103/8490/6918 3813/8488/6951 7886/8108/6649 +f 3810/8476/6939 3806/8484/6947 3814/8491/6953 +f 7111/8481/6944 3810/8476/6939 7110/8492/6954 +f 3809/8489/6952 7102/8493/6917 3814/8491/6953 +f 7110/8492/6954 3814/8491/6953 7912/8495/6914 +f 3815/8501/6958 3819/8496/6955 3816/8498/6957 +f 3816/8498/6957 3820/8497/6956 6997/8086/6632 +f 3819/8496/6955 7105/8499/6934 3820/8497/6956 +f 3820/8497/6956 7104/8500/6929 6998/8087/6633 +f 3817/8503/6960 3815/8501/6958 3821/8502/6959 +f 7117/8508/6965 3817/8503/6960 7116/8504/6961 +f 3821/8502/6959 3816/8498/6957 6996/8083/6629 +f 7116/8504/6961 3821/8502/6959 7889/8026/6582 +f 3815/8501/6958 3817/8503/6960 3818/8506/6963 +f 7120/8513/6969 3818/8506/6963 7119/8507/6964 +f 3822/8505/6962 3817/8503/6960 7118/8509/6966 +f 7119/8507/6964 3822/8505/6962 7907/8510/6967 +f 3819/8496/6955 3815/8501/6958 3823/8511/6968 +f 7105/8499/6934 3819/8496/6955 7106/8512/6935 +f 3823/8511/6968 3818/8506/6963 7121/8514/6970 +f 7106/8512/6935 3823/8511/6968 7909/8515/6936 +f 3824/8523/6978 3828/8516/6971 3825/8518/6973 +f 7126/8526/6981 3825/8518/6973 7125/8519/6974 +f 3829/8517/6972 3828/8516/6971 7124/8521/6976 +f 7125/8519/6974 3829/8517/6972 7908/8522/6977 +f 3826/8525/6980 3824/8523/6978 3830/8524/6979 +f 7000/8094/6639 3826/8525/6980 7001/8095/6640 +f 3830/8524/6979 3825/8518/6973 7127/8527/6982 +f 7001/8095/6640 3830/8524/6979 7890/8096/6612 +f 3824/8523/6978 3826/8525/6980 3827/8529/6984 +f 3827/8529/6984 3831/8528/6983 7114/8480/6943 +f 3826/8525/6980 7000/8094/6639 3831/8528/6983 +f 3831/8528/6983 6999/8110/6651 7115/8487/6950 +f 3828/8516/6971 3824/8523/6978 3832/8530/6985 +f 7123/8520/6975 3828/8516/6971 7122/8531/6986 +f 3832/8530/6985 3827/8529/6984 7113/8479/6942 +f 7122/8531/6986 3832/8530/6985 7910/8483/6946 +f 3834/8535/6990 3833/8532/6987 3838/8534/6989 +f 6973/8544/6580 3834/8535/6990 6974/8536/6581 +f 3838/8534/6989 3837/8533/6988 7116/8538/6961 +f 6974/8536/6581 3838/8534/6989 7889/8539/6582 +f 3833/8532/6987 3834/8535/6990 3835/8541/6992 +f 3835/8541/6992 3839/8540/6991 7129/8543/6994 +f 3834/8535/6990 6973/8544/6580 3839/8540/6991 +f 3839/8540/6991 6972/8545/6594 7128/8542/6993 +f 3836/8548/6996 3833/8532/6987 3840/8547/6995 +f 7132/8554/7001 3836/8548/6996 7131/8549/6997 +f 3840/8547/6995 3835/8541/6992 7130/8550/6998 +f 7131/8549/6997 3840/8547/6995 7905/8551/6999 +f 3833/8532/6987 3836/8548/6996 3837/8533/6988 +f 3837/8533/6988 3841/8552/7000 7117/8537/6965 +f 3836/8548/6996 7132/8554/7001 3841/8552/7000 +f 3841/8552/7000 7133/8555/7002 7118/8553/6966 +f 3843/8560/7006 3842/8557/7003 3847/8559/7005 +f 7138/8567/7013 3843/8560/7006 7137/8561/7007 +f 3847/8559/7005 3846/8558/7004 7136/8563/7009 +f 7137/8561/7007 3847/8559/7005 7906/8564/7010 +f 3842/8557/7003 3843/8560/7006 3844/8566/7012 +f 3844/8566/7012 3848/8565/7011 6988/8061/6609 +f 3843/8560/7006 7138/8567/7013 3848/8565/7011 +f 3848/8565/7011 7139/8568/7014 6989/8067/6614 +f 3845/8570/7016 3842/8557/7003 3849/8569/7015 +f 7126/8574/6981 3845/8570/7016 7127/8571/6982 +f 3849/8569/7015 3844/8566/7012 6987/8060/6608 +f 7127/8571/6982 3849/8569/7015 7890/8064/6612 +f 3842/8557/7003 3845/8570/7016 3846/8558/7004 +f 3846/8558/7004 3850/8572/7017 7135/8562/7008 +f 3845/8570/7016 7126/8574/6981 3850/8572/7017 +f 3850/8572/7017 7125/8575/6974 7134/8573/7018 +f 3852/8580/7022 3851/8577/7019 3856/8579/7021 +f 6961/7985/6550 3852/8580/7022 6962/7992/6554 +f 3856/8579/7021 3855/8578/7020 7128/8582/6993 +f 6962/7992/6554 3856/8579/7021 7891/7993/6555 +f 3853/8584/7024 3851/8577/7019 3857/8583/7023 +f 7141/8590/7030 3853/8584/7024 7140/8585/7025 +f 3857/8583/7023 3852/8580/7022 6960/7984/6549 +f 7140/8585/7025 3857/8583/7023 7869/7988/6227 +f 3851/8577/7019 3853/8584/7024 3854/8587/7027 +f 3854/8587/7027 3858/8586/7026 7144/8589/7029 +f 3853/8584/7024 7141/8590/7030 3858/8586/7026 +f 3858/8586/7026 7142/8591/7031 7143/8588/7028 +f 3851/8577/7019 3854/8587/7027 3855/8578/7020 +f 3855/8578/7020 3859/8593/7033 7129/8581/6994 +f 3859/8593/7033 3854/8587/7027 7145/8595/7034 +f 7130/8594/6998 3859/8593/7033 7905/8596/6999 +f 3860/8605/7043 3864/8597/7035 3861/8599/7037 +f 3861/8599/7037 3865/8598/7036 7150/8601/7039 +f 3864/8597/7035 7147/8602/7040 3865/8598/7036 +f 3865/8598/7036 7148/8603/7041 7149/8600/7038 +f 3862/8607/7045 3860/8605/7043 3866/8606/7044 +f 6970/8614/6569 3862/8607/7045 6971/8608/6570 +f 3866/8606/7044 3861/8599/7037 7151/8609/7046 +f 6971/8608/6570 3866/8606/7044 7870/8610/6238 +f 3863/8612/7048 3860/8605/7043 3867/8611/7047 +f 7138/8619/7013 3863/8612/7048 7139/8613/7014 +f 3867/8611/7047 3862/8607/7045 6969/8615/6563 +f 7139/8613/7014 3867/8611/7047 7892/8616/6566 +f 3860/8605/7043 3863/8612/7048 3864/8597/7035 +f 7147/8602/7040 3864/8597/7035 7146/8618/7050 +f 3863/8612/7048 7138/8619/7013 3868/8617/7049 +f 7146/8618/7050 3868/8617/7049 7906/8621/7010 +f 3870/8625/7054 3869/8622/7051 3874/8624/7053 +f 6898/8632/6393 3870/8625/7054 6899/8626/6394 +f 3874/8624/7053 3873/8623/7052 7154/8628/7056 +f 6899/8626/6394 3874/8624/7053 7879/8629/6357 +f 3869/8622/7051 3870/8625/7054 3871/8631/7058 +f 3871/8631/7058 3875/8630/7057 7063/8344/6823 +f 3870/8625/7054 6898/8632/6393 3875/8630/7057 +f 3875/8630/7057 6897/8633/6389 7064/8345/6824 +f 3872/8635/7060 3869/8622/7051 3876/8634/7059 +f 7156/8639/7064 3872/8635/7060 7155/8636/7061 +f 3876/8634/7059 3871/8631/7058 7062/8360/6835 +f 7155/8636/7061 3876/8634/7059 7927/8363/6838 +f 3869/8622/7051 3872/8635/7060 3873/8623/7052 +f 3873/8623/7052 3877/8637/7062 7153/8627/7055 +f 3872/8635/7060 7156/8639/7064 3877/8637/7062 +f 3877/8637/7062 7157/8640/7065 7152/8638/7063 +f 3879/8645/7070 3878/8642/7067 3883/8644/7069 +f 7078/8381/6856 3879/8645/7070 7079/8382/6857 +f 3883/8644/7069 3882/8643/7068 7160/8647/7072 +f 7079/8382/6857 3883/8644/7069 7928/8383/6858 +f 3878/8642/7067 3879/8645/7070 3880/8649/7074 +f 3880/8649/7074 3884/8648/7073 6901/7793/6400 +f 3879/8645/7070 7078/8381/6856 3884/8648/7073 +f 3884/8648/7073 7077/8378/6853 6902/7794/6401 +f 3881/8651/7076 3878/8642/7067 3885/8650/7075 +f 7162/8655/7080 3881/8651/7076 7161/8652/7077 +f 3885/8650/7075 3880/8649/7074 6900/7807/6410 +f 7161/8652/7077 3885/8650/7075 7880/7760/6373 +f 3878/8642/7067 3881/8651/7076 3882/8643/7068 +f 3882/8643/7068 3886/8653/7078 7159/8646/7071 +f 3881/8651/7076 7162/8655/7080 3886/8653/7078 +f 3886/8653/7078 7163/8656/7081 7158/8654/7079 +f 3888/8661/7086 3887/8658/7083 3892/8660/7085 +f 6886/8668/6360 3888/8661/7086 6887/8662/6361 +f 3892/8660/7085 3891/8659/7084 7166/8664/7088 +f 6887/8662/6361 3892/8660/7085 7877/8665/6325 +f 3887/8658/7083 3888/8661/7086 3889/8667/7090 +f 3889/8667/7090 3893/8666/7089 7153/8627/7055 +f 3888/8661/7086 6886/8668/6360 3893/8666/7089 +f 3893/8666/7089 6885/8669/6355 7154/8628/7056 +f 3890/8671/7092 3887/8658/7083 3894/8670/7091 +f 7168/8675/7096 3890/8671/7092 7167/8672/7093 +f 3894/8670/7091 3889/8667/7090 7152/8638/7063 +f 7167/8672/7093 3894/8670/7091 7915/8641/7066 +f 3887/8658/7083 3890/8671/7092 3891/8659/7084 +f 3891/8659/7084 3895/8673/7094 7165/8663/7087 +f 3890/8671/7092 7168/8675/7096 3895/8673/7094 +f 7164/8674/7095 3895/8673/7094 7917/8677/7098 +f 3897/8681/7102 3896/8678/7099 3901/8680/7101 +f 7162/8655/7080 3897/8681/7102 7163/8656/7081 +f 3901/8680/7101 3900/8679/7100 7172/8683/7104 +f 7163/8656/7081 3901/8680/7101 7916/8657/7082 +f 3896/8678/7099 3897/8681/7102 3898/8685/7106 +f 3898/8685/7106 3902/8684/7105 6889/7751/6367 +f 3897/8681/7102 7162/8655/7080 3902/8684/7105 +f 3902/8684/7105 7161/8652/7077 6890/7759/6372 +f 3899/8687/7108 3896/8678/7099 3903/8686/7107 +f 7174/8691/7112 3899/8687/7108 7173/8688/7109 +f 3903/8686/7107 3898/8685/7106 6888/7750/6366 +f 7173/8688/7109 3903/8686/7107 7878/7754/6341 +f 3896/8678/7099 3899/8687/7108 3900/8679/7100 +f 3900/8679/7100 3904/8689/7110 7171/8682/7103 +f 3899/8687/7108 7174/8691/7112 3904/8689/7110 +f 7170/8690/7111 3904/8689/7110 7918/8693/7114 +f 3906/8697/7118 3905/8694/7115 3910/8696/7117 +f 6874/8706/6328 3906/8697/7118 6875/8698/6329 +f 3910/8696/7117 3909/8695/7116 7178/8700/7120 +f 6875/8698/6329 3910/8696/7117 7875/8701/6293 +f 3905/8694/7115 3906/8697/7118 3907/8703/7122 +f 3907/8703/7122 3911/8702/7121 7165/8705/7087 +f 3906/8697/7118 6874/8706/6328 3911/8702/7121 +f 3911/8702/7121 6873/8707/6323 7166/8704/7088 +f 3908/8714/7126 3905/8694/7115 3912/8709/7123 +f 3908/8714/7126 3912/8709/7123 7180/8711/7125 +f 3912/8709/7123 3907/8703/7122 7164/8712/7095 +f 3912/8709/7123 7164/8712/7095 7179/8710/7124 +f 3905/8694/7115 3908/8714/7126 3909/8695/7116 +f 3909/8695/7116 3913/8715/7127 7177/8699/7119 +f 3908/8714/7126 7180/8711/7125 3913/8715/7127 +f 3913/8715/7127 7181/8717/7129 7176/8716/7128 +f 3915/8722/7134 3914/8719/7131 3919/8721/7133 +f 7174/8731/7112 3915/8722/7134 7175/8723/7113 +f 3918/8720/7132 7183/8724/7135 3919/8721/7133 +f 3919/8721/7133 7184/8725/7136 7175/8723/7113 +f 3914/8719/7131 3915/8722/7134 3916/8728/7138 +f 3916/8728/7138 3920/8727/7137 6877/8730/6335 +f 3915/8722/7134 7174/8731/7112 3920/8727/7137 +f 3920/8727/7137 7173/8732/7109 6878/8729/6340 +f 3917/8735/7140 3914/8719/7131 3921/8734/7139 +f 7186/8741/7144 3917/8735/7140 7185/8736/7141 +f 3921/8734/7139 3916/8728/7138 6876/8737/6334 +f 7185/8736/7141 3921/8734/7139 7876/8738/6309 +f 3914/8719/7131 3917/8735/7140 3918/8720/7132 +f 3918/8720/7132 3922/8739/7142 7183/8724/7135 +f 3917/8735/7140 7186/8741/7144 3922/8739/7142 +f 3922/8739/7142 7187/8742/7145 7182/8740/7143 +f 3923/8755/7154 3927/8744/7147 3924/8746/7149 +f 6862/8753/6296 3924/8746/7149 6863/8747/6297 +f 3927/8744/7147 7189/8748/7150 3928/8745/7148 +f 3928/8745/7148 7190/8749/7151 6863/8747/6297 +f 3923/8755/7154 3924/8746/7149 3925/8752/7153 +f 3925/8752/7153 3929/8751/7152 7177/8699/7119 +f 3924/8746/7149 6862/8753/6296 3929/8751/7152 +f 3929/8751/7152 6861/8754/6291 7178/8700/7120 +f 3926/8757/7156 3923/8755/7154 3930/8756/7155 +f 7192/8761/7160 3926/8757/7156 7191/8758/7157 +f 3930/8756/7155 3925/8752/7153 7176/8716/7128 +f 7191/8758/7157 3930/8756/7155 7919/8718/7130 +f 3923/8755/7154 3926/8757/7156 3927/8744/7147 +f 7189/8748/7150 3927/8744/7147 7188/8760/7159 +f 3926/8757/7156 7192/8761/7160 3931/8759/7158 +f 3931/8759/7158 7193/8762/7161 7188/8760/7159 +f 3933/8767/7166 3932/8764/7163 3937/8766/7165 +f 7186/8776/7144 3933/8767/7166 7187/8768/7145 +f 3937/8766/7165 3936/8765/7164 7196/8770/7168 +f 7187/8768/7145 3937/8766/7165 7920/8771/7146 +f 3932/8764/7163 3933/8767/7166 3934/8773/7170 +f 3934/8773/7170 3938/8772/7169 6865/8775/6303 +f 3933/8767/7166 7186/8776/7144 3938/8772/7169 +f 3938/8772/7169 7185/8777/7141 6866/8774/6308 +f 3932/8764/7163 3934/8773/7170 3935/8780/7172 +f 3935/8780/7172 3939/8779/7171 7198/8782/7174 +f 3939/8779/7171 3934/8773/7170 6864/8783/6302 +f 3939/8779/7171 6864/8783/6302 7197/8781/7173 +f 3932/8764/7163 3935/8780/7172 3936/8765/7164 +f 3936/8765/7164 3940/8785/7175 7195/8769/7167 +f 3940/8785/7175 3935/8780/7172 7199/8787/7177 +f 3940/8785/7175 7199/8787/7177 7194/8786/7176 +f 3941/8794/7184 3945/8789/7179 3942/8791/7181 +f 3942/8791/7181 3946/8790/7180 6850/7612/6259 +f 3945/8789/7179 7201/8792/7182 3946/8790/7180 +f 3946/8790/7180 7202/8793/7183 6851/7619/6265 +f 3943/8796/7186 3941/8794/7184 3947/8795/7185 +f 7189/8801/7150 3943/8796/7186 7190/8797/7151 +f 3947/8795/7185 3942/8791/7181 6849/7611/6258 +f 7190/8797/7151 3947/8795/7185 7873/7615/6262 +f 3941/8794/7184 3943/8796/7186 3944/8799/7188 +f 7204/8806/7192 3944/8799/7188 7203/8800/7189 +f 3943/8796/7186 7189/8801/7150 3948/8798/7187 +f 7203/8800/7189 3948/8798/7187 7921/8803/7162 +f 3945/8789/7179 3941/8794/7184 3949/8804/7190 +f 7201/8792/7182 3945/8789/7179 7200/8805/7191 +f 3949/8804/7190 3944/8799/7188 7205/8807/7193 +f 7200/8805/7191 3949/8804/7190 7923/8808/7194 +f 3950/8814/7200 3954/8809/7195 3951/8811/7197 +f 3951/8811/7197 3955/8810/7196 7198/8782/7174 +f 3955/8810/7196 3954/8809/7195 7208/8813/7199 +f 7199/8787/7177 3955/8810/7196 7922/8788/7178 +f 3952/8816/7202 3950/8814/7200 3956/8815/7201 +f 6853/8822/6276 3952/8816/7202 6854/8817/6277 +f 3956/8815/7201 3951/8811/7197 7197/8781/7173 +f 6854/8817/6277 3956/8815/7201 7874/8784/6278 +f 3950/8814/7200 3952/8816/7202 3953/8819/7204 +f 3953/8819/7204 3957/8818/7203 7210/8821/7206 +f 3952/8816/7202 6853/8822/6276 3957/8818/7203 +f 3957/8818/7203 6852/8823/6271 7209/8820/7205 +f 3954/8809/7195 3950/8814/7200 3958/8825/7207 +f 7207/8812/7198 3954/8809/7195 7206/8826/7208 +f 3958/8825/7207 3953/8819/7204 7211/8827/7209 +f 7206/8826/7208 3958/8825/7207 7924/8828/7210 +f 3959/8834/7214 3963/8829/7211 3960/8831/7213 +f 3960/8831/7213 3964/8830/7212 6832/8833/6219 +f 3963/8829/7211 7141/8590/7030 3964/8830/7212 +f 3964/8830/7212 7140/8585/7025 6833/8832/6226 +f 3961/8836/7216 3959/8834/7214 3965/8835/7215 +f 7201/8844/7182 3961/8836/7216 7202/8837/7183 +f 3965/8835/7215 3960/8831/7213 6831/8838/6218 +f 7202/8837/7183 3965/8835/7215 7871/8839/6222 +f 3959/8834/7214 3961/8836/7216 3962/8841/7218 +f 3962/8841/7218 3966/8840/7217 7213/8843/7220 +f 3961/8836/7216 7201/8844/7182 3966/8840/7217 +f 3966/8840/7217 7200/8845/7191 7212/8842/7219 +f 3963/8829/7211 3959/8834/7214 3967/8847/7221 +f 7141/8590/7030 3963/8829/7211 7142/8591/7031 +f 3967/8847/7221 3962/8841/7218 7214/8848/7222 +f 7142/8591/7031 3967/8847/7221 7925/8592/7032 +f 3968/8854/7228 3972/8849/7223 3969/8851/7225 +f 3969/8851/7225 3973/8850/7224 7210/8821/7206 +f 3972/8849/7223 7216/8852/7226 3973/8850/7224 +f 3973/8850/7224 7217/8853/7227 7211/8827/7209 +f 3970/8856/7230 3968/8854/7228 3974/8855/7229 +f 6841/8860/6243 3970/8856/7230 6842/8857/6244 +f 3974/8855/7229 3969/8851/7225 7209/8820/7205 +f 6842/8857/6244 3974/8855/7229 7872/8824/6245 +f 3968/8854/7228 3970/8856/7230 3971/8859/7232 +f 3971/8859/7232 3975/8858/7231 7150/8601/7039 +f 3970/8856/7230 6841/8860/6243 3975/8858/7231 +f 3975/8858/7231 6840/8861/6235 7151/8609/7046 +f 3972/8849/7223 3968/8854/7228 3976/8862/7233 +f 7216/8852/7226 3972/8849/7223 7215/8863/7234 +f 3976/8862/7233 3971/8859/7232 7149/8600/7038 +f 7215/8863/7234 3976/8862/7233 7926/8604/7042 +f 3977/8873/7244 3981/8864/7235 3978/8866/7237 +f 3978/8866/7237 3982/8865/7236 7120/8513/6969 +f 3981/8864/7235 7219/8867/7238 3982/8865/7236 +f 3982/8865/7236 7220/8868/7239 7121/8514/6970 +f 3977/8873/7244 3978/8866/7237 3979/8870/7241 +f 3979/8870/7241 3983/8869/7240 7222/8872/7243 +f 3978/8866/7237 7120/8513/6969 3983/8869/7240 +f 3983/8869/7240 7119/8507/6964 7221/8871/7242 +f 3980/8875/7246 3977/8873/7244 3984/8874/7245 +f 7225/8881/7252 3980/8875/7246 7224/8876/7247 +f 3984/8874/7245 3979/8870/7241 7223/8877/7248 +f 7224/8876/7247 3984/8874/7245 7931/8878/7249 +f 3981/8864/7235 3977/8873/7244 3985/8879/7250 +f 7219/8867/7238 3981/8864/7235 7218/8880/7251 +f 3985/8879/7250 3980/8875/7246 7226/8882/7253 +f 7218/8880/7251 3985/8879/7250 7929/8883/7254 +f 3987/8887/7258 3986/8884/7255 3991/8886/7257 +f 7231/8896/7265 3987/8887/7258 7230/8888/7259 +f 3991/8886/7257 3990/8885/7256 7229/8890/7261 +f 7230/8888/7259 3991/8886/7257 7932/8891/7262 +f 3986/8884/7255 3987/8887/7258 3988/8893/7264 +f 3988/8893/7264 3992/8892/7263 7123/8895/6975 +f 3987/8887/7258 7231/8896/7265 3992/8892/7263 +f 3992/8892/7263 7232/8897/7266 7124/8894/6976 +f 3986/8884/7255 3988/8893/7264 3989/8900/7268 +f 3989/8900/7268 3993/8899/7267 7234/8902/7270 +f 3988/8893/7264 7123/8895/6975 3993/8899/7267 +f 3993/8899/7267 7122/8903/6986 7233/8901/7269 +f 3990/8885/7256 3986/8884/7255 3994/8905/7271 +f 7228/8889/7260 3990/8885/7256 7227/8906/7272 +f 3994/8905/7271 3989/8900/7268 7235/8907/7273 +f 7227/8906/7272 3994/8905/7271 7930/8908/7274 +f 3995/8918/7284 3999/8909/7275 3996/8911/7277 +f 3996/8911/7277 4000/8910/7276 7225/8881/7252 +f 3999/8909/7275 7237/8912/7278 4000/8910/7276 +f 4000/8910/7276 7238/8913/7279 7226/8882/7253 +f 3995/8918/7284 3996/8911/7277 3997/8915/7281 +f 3997/8915/7281 4001/8914/7280 7240/8917/7283 +f 3996/8911/7277 7225/8881/7252 4001/8914/7280 +f 4001/8914/7280 7224/8876/7247 7239/8916/7282 +f 3998/8920/7286 3995/8918/7284 4002/8919/7285 +f 7243/8926/7292 3998/8920/7286 7242/8921/7287 +f 4002/8919/7285 3997/8915/7281 7241/8922/7288 +f 7242/8921/7287 4002/8919/7285 7933/8923/7289 +f 3995/8918/7284 3998/8920/7286 3999/8909/7275 +f 7237/8912/7278 3999/8909/7275 7236/8925/7291 +f 3998/8920/7286 7243/8926/7292 4003/8924/7290 +f 7236/8925/7291 4003/8924/7290 7935/8928/7294 +f 4005/8932/7298 4004/8929/7295 4009/8931/7297 +f 7249/8939/7305 4005/8932/7298 7248/8933/7299 +f 4009/8931/7297 4008/8930/7296 7247/8935/7301 +f 7248/8933/7299 4009/8931/7297 7934/8936/7302 +f 4004/8929/7295 4005/8932/7298 4006/8938/7304 +f 4006/8938/7304 4010/8937/7303 7228/8889/7260 +f 4005/8932/7298 7249/8939/7305 4010/8937/7303 +f 4010/8937/7303 7250/8940/7306 7229/8890/7261 +f 4004/8929/7295 4006/8938/7304 4007/8942/7308 +f 4007/8942/7308 4011/8941/7307 7252/8944/7310 +f 4006/8938/7304 7228/8889/7260 4011/8941/7307 +f 4011/8941/7307 7227/8906/7272 7251/8943/7309 +f 4004/8929/7295 4007/8942/7308 4008/8930/7296 +f 4008/8930/7296 4012/8945/7311 7246/8934/7300 +f 4012/8945/7311 4007/8942/7308 7253/8947/7313 +f 7245/8946/7312 4012/8945/7311 7936/8948/7314 +f 4014/8952/7318 4013/8949/7315 4018/8951/7317 +f 7243/8926/7292 4014/8952/7318 7244/8927/7293 +f 4017/8950/7316 7255/8953/7319 4018/8951/7317 +f 4018/8951/7317 7256/8954/7320 7244/8927/7293 +f 4013/8949/7315 4014/8952/7318 4015/8956/7322 +f 7258/8962/7328 4015/8956/7322 7257/8957/7323 +f 4014/8952/7318 7243/8926/7292 4019/8955/7321 +f 4019/8955/7321 7242/8921/7287 7257/8957/7323 +f 4013/8949/7315 4015/8956/7322 4016/8959/7325 +f 4016/8959/7325 4020/8958/7324 7261/8961/7327 +f 4015/8956/7322 7258/8962/7328 4020/8958/7324 +f 4020/8958/7324 7259/8963/7329 7260/8960/7326 +f 4017/8950/7316 4013/8949/7315 4021/8965/7331 +f 7255/8953/7319 4017/8950/7316 7254/8966/7332 +f 4021/8965/7331 4016/8959/7325 7262/8967/7333 +f 7254/8966/7332 4021/8965/7331 7937/8968/7334 +f 4022/8980/7346 4026/8969/7335 4023/8971/7337 +f 4023/8971/7337 4027/8970/7336 7267/8973/7339 +f 4026/8969/7335 7264/8974/7340 4027/8970/7336 +f 4027/8970/7336 7265/8975/7341 7266/8972/7338 +f 4022/8980/7346 4023/8971/7337 4024/8978/7344 +f 4024/8978/7344 4028/8977/7343 7246/8934/7300 +f 4028/8977/7343 4023/8971/7337 7268/8979/7345 +f 4028/8977/7343 7268/8979/7345 7247/8935/7301 +f 4025/8984/7350 4022/8980/7346 4029/8981/7347 +f 4025/8984/7350 4029/8981/7347 7270/8983/7349 +f 4029/8981/7347 4024/8978/7344 7245/8946/7312 +f 4029/8981/7347 7245/8946/7312 7269/8982/7348 +f 4026/8969/7335 4022/8980/7346 4030/8985/7351 +f 7264/8974/7340 4026/8969/7335 7263/8986/7352 +f 4030/8985/7351 4025/8984/7350 7271/8987/7353 +f 7263/8986/7352 4030/8985/7351 7938/8988/7354 +f 4031/8994/7360 4035/8989/7355 4032/8991/7357 +f 4032/8991/7357 4036/8990/7356 7261/8961/7327 +f 4035/8989/7355 7273/8992/7358 4036/8990/7356 +f 4036/8990/7356 7274/8993/7359 7262/8967/7333 +f 4033/8996/7362 4031/8994/7360 4037/8995/7361 +f 7276/9002/7368 4033/8996/7362 7275/8997/7363 +f 4037/8995/7361 4032/8991/7357 7260/8960/7326 +f 7275/8997/7363 4037/8995/7361 7939/8964/7330 +f 4031/8994/7360 4033/8996/7362 4034/8999/7365 +f 4034/8999/7365 4038/8998/7364 7279/9001/7367 +f 4033/8996/7362 7276/9002/7368 4038/8998/7364 +f 4038/8998/7364 7277/9003/7369 7278/9000/7366 +f 4035/8989/7355 4031/8994/7360 4039/9005/7371 +f 7273/8992/7358 4035/8989/7355 7272/9006/7372 +f 4039/9005/7371 4034/8999/7365 7280/9007/7373 +f 7272/9006/7372 4039/9005/7371 7943/9008/7374 +f 4040/9017/7383 4044/9009/7375 4041/9011/7377 +f 4041/9011/7377 4045/9010/7376 7285/9013/7379 +f 4044/9009/7375 7282/9014/7380 4045/9010/7376 +f 4045/9010/7376 7283/9015/7381 7284/9012/7378 +f 4042/9019/7385 4040/9017/7383 4046/9018/7384 +f 7264/8974/7340 4042/9019/7385 7265/8975/7341 +f 4046/9018/7384 4041/9011/7377 7286/9020/7386 +f 7265/8975/7341 4046/9018/7384 7940/8976/7342 +f 4040/9017/7383 4042/9019/7385 4043/9022/7388 +f 4043/9022/7388 4047/9021/7387 7288/9024/7390 +f 4042/9019/7385 7264/8974/7340 4047/9021/7387 +f 4047/9021/7387 7263/8986/7352 7287/9023/7389 +f 4044/9009/7375 4040/9017/7383 4048/9025/7391 +f 7282/9014/7380 4044/9009/7375 7281/9026/7392 +f 4048/9025/7391 4043/9022/7388 7289/9027/7393 +f 7281/9026/7392 4048/9025/7391 7944/9028/7394 +f 4050/9032/7398 4049/9029/7395 4054/9031/7397 +f 7156/9041/7064 4050/9032/7398 7157/9033/7065 +f 4054/9031/7397 4053/9030/7396 7292/9035/7400 +f 7157/9033/7065 4054/9031/7397 7915/9036/7066 +f 4049/9029/7395 4050/9032/7398 4051/9038/7402 +f 4051/9038/7402 4055/9037/7401 7294/9040/7404 +f 4050/9032/7398 7156/9041/7064 4055/9037/7401 +f 4055/9037/7401 7155/9042/7061 7293/9039/7403 +f 4052/9045/7406 4049/9029/7395 4056/9044/7405 +f 7273/8992/7358 4052/9045/7406 7274/8993/7359 +f 4056/9044/7405 4051/9038/7402 7295/9046/7407 +f 7274/8993/7359 4056/9044/7405 7937/8968/7334 +f 4049/9029/7395 4052/9045/7406 4053/9030/7396 +f 4053/9030/7396 4057/9047/7408 7291/9034/7399 +f 4052/9045/7406 7273/8992/7358 4057/9047/7408 +f 4057/9047/7408 7272/9006/7372 7290/9048/7409 +f 4059/9052/7413 4058/9049/7410 4063/9051/7412 +f 7297/9058/7417 4059/9052/7413 7296/9053/7414 +f 4063/9051/7412 4062/9050/7411 7287/9023/7389 +f 7296/9053/7414 4063/9051/7412 7938/8988/7354 +f 4058/9049/7410 4059/9052/7413 4060/9055/7416 +f 4060/9055/7416 4064/9054/7415 7159/9057/7071 +f 4059/9052/7413 7297/9058/7417 4064/9054/7415 +f 4064/9054/7415 7298/9059/7418 7160/9056/7072 +f 4061/9062/7420 4058/9049/7410 4065/9061/7419 +f 7300/9067/7423 4061/9062/7420 7299/9063/7421 +f 4065/9061/7419 4060/9055/7416 7158/9064/7079 +f 7299/9063/7421 4065/9061/7419 7916/9065/7082 +f 4058/9049/7410 4061/9062/7420 4062/9050/7411 +f 4062/9050/7411 4066/9066/7422 7288/9024/7390 +f 4061/9062/7420 7300/9067/7423 4066/9066/7422 +f 4066/9066/7422 7301/9068/7424 7289/9027/7393 +f 4067/9077/7430 4071/9069/7425 4068/9071/7427 +f 4068/9071/7427 4072/9070/7426 7303/9073/7429 +f 4071/9069/7425 7069/9074/6836 4072/9070/7426 +f 4072/9070/7426 7068/9075/6831 7302/9072/7428 +f 4069/9079/7432 4067/9077/7430 4073/9078/7431 +f 7255/8953/7319 4069/9079/7432 7256/8954/7320 +f 4073/9078/7431 4068/9071/7427 7304/9080/7433 +f 7256/8954/7320 4073/9078/7431 7935/8928/7294 +f 4067/9077/7430 4069/9079/7432 4070/9082/7435 +f 4070/9082/7435 4074/9081/7434 7294/9040/7404 +f 4069/9079/7432 7255/8953/7319 4074/9081/7434 +f 4074/9081/7434 7254/8966/7332 7295/9046/7407 +f 4071/9069/7425 4067/9077/7430 4075/9083/7436 +f 7069/9074/6836 4071/9069/7425 7070/9084/6837 +f 4075/9083/7436 4070/9082/7435 7293/9039/7403 +f 7070/9084/6837 4075/9083/7436 7927/9043/6838 +f 4076/9093/7440 4080/9085/7437 4077/9087/7439 +f 4077/9087/7439 4081/9086/7438 7270/9089/7349 +f 4080/9085/7437 7297/9090/7417 4081/9086/7438 +f 4081/9086/7438 7296/9091/7414 7271/9088/7353 +f 4078/9095/7442 4076/9093/7440 4082/9094/7441 +f 7306/9101/7446 4078/9095/7442 7305/9096/7443 +f 4082/9094/7441 4077/9087/7439 7269/9097/7348 +f 7305/9096/7443 4082/9094/7441 7936/9098/7314 +f 4076/9093/7440 4078/9095/7442 4079/9100/7445 +f 4079/9100/7445 4083/9099/7444 7072/8369/6844 +f 4078/9095/7442 7306/9101/7446 4083/9099/7444 +f 4083/9099/7444 7307/9102/7447 7073/8370/6845 +f 4080/9085/7437 4076/9093/7440 4084/9103/7448 +f 7297/9090/7417 4080/9085/7437 7298/9104/7418 +f 4084/9103/7448 4079/9100/7445 7071/8380/6855 +f 7298/9104/7418 4084/9103/7448 7928/8383/6858 +f 4086/9110/7452 4085/9105/7449 4090/9107/7451 +f 4086/9110/7452 4090/9107/7451 7084/9109/6870 +f 4090/9107/7451 4089/9106/7450 7302/9072/7428 +f 7085/9108/6874 4090/9107/7451 7903/9076/6833 +f 4087/9116/7456 4085/9105/7449 4091/9111/7453 +f 4087/9116/7456 4091/9111/7453 7309/9113/7455 +f 4091/9111/7453 4086/9110/7452 7083/9114/6869 +f 7308/9112/7454 4091/9111/7453 7913/9115/6872 +f 4088/9118/7458 4085/9105/7449 4092/9117/7457 +f 7237/8912/7278 4088/9118/7458 7238/8913/7279 +f 4092/9117/7457 4087/9116/7456 7310/9119/7459 +f 7238/8913/7279 4092/9117/7457 7929/8883/7254 +f 4085/9105/7449 4088/9118/7458 4089/9106/7450 +f 4089/9106/7450 4093/9120/7460 7303/9073/7429 +f 4088/9118/7458 7237/8912/7278 4093/9120/7460 +f 4093/9120/7460 7236/8925/7291 7304/9080/7433 +f 4095/9124/7464 4094/9121/7461 4099/9123/7463 +f 7312/9129/7468 4095/9124/7464 7311/9125/7465 +f 4099/9123/7463 4098/9122/7462 7251/8943/7309 +f 7311/9125/7465 4099/9123/7463 7930/8908/7274 +f 4096/9127/7467 4094/9121/7461 4100/9126/7466 +f 7087/9135/6879 4096/9127/7467 7088/9128/6880 +f 4095/9124/7464 7312/9129/7468 4100/9126/7466 +f 7088/9128/6880 4100/9126/7466 7914/9131/6881 +f 4097/9133/7471 4094/9121/7461 4101/9132/7470 +f 7306/9139/7446 4097/9133/7471 7307/9134/7447 +f 4096/9127/7467 7087/9135/6879 4101/9132/7470 +f 7307/9134/7447 4101/9132/7470 7904/9137/6846 +f 4094/9121/7461 4097/9133/7471 4098/9122/7462 +f 4098/9122/7462 4102/9138/7472 7252/8944/7310 +f 4097/9133/7471 7306/9139/7446 4102/9138/7472 +f 4102/9138/7472 7305/9140/7443 7253/8947/7313 +f 4104/9144/7476 4103/9141/7473 4108/9143/7475 +f 7219/8867/7238 4104/9144/7476 7220/8868/7239 +f 4108/9143/7475 4107/9142/7474 7107/9146/6932 +f 7220/8868/7239 4108/9143/7475 7909/8515/6936 +f 4103/9141/7473 4104/9144/7476 4105/9148/7478 +f 4105/9148/7478 4109/9147/7477 7309/9113/7455 +f 4104/9144/7476 7219/8867/7238 4109/9147/7477 +f 4109/9147/7477 7218/8880/7251 7310/9119/7459 +f 4106/9150/7480 4103/9141/7473 4110/9149/7479 +f 7096/9154/6905 4106/9150/7480 7097/9151/6906 +f 4110/9149/7479 4105/9148/7478 7308/9112/7454 +f 7097/9151/6906 4110/9149/7479 7913/9115/6872 +f 4103/9141/7473 4106/9150/7480 4107/9142/7474 +f 4107/9142/7474 4111/9152/7481 7108/9145/6933 +f 4106/9150/7480 7096/9154/6905 4111/9152/7481 +f 7109/9153/6938 4111/9152/7481 7911/9156/6903 +f 4113/9160/7485 4112/9157/7482 4117/9159/7484 +f 7312/9129/7468 4113/9160/7485 7313/9130/7469 +f 4117/9159/7484 4116/9158/7483 7098/9162/6922 +f 7313/9130/7469 4117/9159/7484 7914/9131/6881 +f 4112/9157/7482 4113/9160/7485 4114/9164/7487 +f 4114/9164/7487 4118/9163/7486 7234/8902/7270 +f 4113/9160/7485 7312/9129/7468 4118/9163/7486 +f 4118/9163/7486 7311/9125/7465 7235/8907/7273 +f 4115/9166/7489 4112/9157/7482 4119/9165/7488 +f 7111/9170/6944 4115/9166/7489 7112/9167/6945 +f 4119/9165/7488 4114/9164/7487 7233/8901/7269 +f 7112/9167/6945 4119/9165/7488 7910/8904/6946 +f 4112/9157/7482 4115/9166/7489 4116/9158/7483 +f 4116/9158/7483 4120/9168/7490 7099/9161/6912 +f 4115/9166/7489 7111/9170/6944 4120/9168/7490 +f 7100/9169/6913 4120/9168/7490 7912/9172/6914 +f 4121/9181/7494 4125/9173/7491 4122/9175/7493 +f 4122/9175/7493 4126/9174/7492 7144/9177/7029 +f 4125/9173/7491 7132/9178/7001 4126/9174/7492 +f 4126/9174/7492 7131/9179/6997 7145/9176/7034 +f 4123/9183/7496 4121/9181/7494 4127/9182/7495 +f 7315/9191/7500 4123/9183/7496 7314/9184/7497 +f 4127/9182/7495 4122/9175/7493 7143/9185/7028 +f 7314/9184/7497 4127/9182/7495 7925/9186/7032 +f 4121/9181/7494 4123/9183/7496 4124/9188/7499 +f 4124/9188/7499 4128/9187/7498 7222/9190/7243 +f 4123/9183/7496 7315/9191/7500 4128/9187/7498 +f 4128/9187/7498 7316/9192/7501 7223/9189/7248 +f 4125/9173/7491 4121/9181/7494 4129/9194/7502 +f 7132/9178/7001 4125/9173/7491 7133/9195/7002 +f 4129/9194/7502 4124/9188/7499 7221/9196/7242 +f 7133/9195/7002 4129/9194/7502 7907/9197/6967 +f 4130/9203/7508 4134/9198/7503 4131/9200/7505 +f 4131/9200/7505 4135/9199/7504 7318/9202/7507 +f 4134/9198/7503 7231/8896/7265 4135/9199/7504 +f 4135/9199/7504 7230/8888/7259 7317/9201/7506 +f 4132/9205/7510 4130/9203/7508 4136/9204/7509 +f 7147/9213/7040 4132/9205/7510 7148/9206/7041 +f 4136/9204/7509 4131/9200/7505 7319/9207/7511 +f 7148/9206/7041 4136/9204/7509 7926/9208/7042 +f 4130/9203/7508 4132/9205/7510 4133/9210/7513 +f 4133/9210/7513 4137/9209/7512 7135/9212/7008 +f 4132/9205/7510 7147/9213/7040 4137/9209/7512 +f 4137/9209/7512 7146/9214/7050 7136/9211/7009 +f 4134/9198/7503 4130/9203/7508 4138/9216/7514 +f 7231/8896/7265 4134/9198/7503 7232/8897/7266 +f 4138/9216/7514 4133/9210/7513 7134/9217/7018 +f 7232/8897/7266 4138/9216/7514 7908/8898/6977 +f 4140/9221/7518 4139/9218/7515 4144/9220/7517 +f 7321/9228/7522 4140/9221/7518 7320/9222/7519 +f 4144/9220/7517 4143/9219/7516 7212/9224/7219 +f 7320/9222/7519 4144/9220/7517 7923/9225/7194 +f 4139/9218/7515 4140/9221/7518 4141/9227/7521 +f 4141/9227/7521 4145/9226/7520 7240/8917/7283 +f 4140/9221/7518 7321/9228/7522 4145/9226/7520 +f 4145/9226/7520 7322/9229/7523 7241/8922/7288 +f 4142/9231/7525 4139/9218/7515 4146/9230/7524 +f 7315/9235/7500 4142/9231/7525 7316/9232/7501 +f 4146/9230/7524 4141/9227/7521 7239/8916/7282 +f 7316/9232/7501 4146/9230/7524 7931/8878/7249 +f 4139/9218/7515 4142/9231/7525 4143/9219/7516 +f 4143/9219/7516 4147/9233/7526 7213/9223/7220 +f 4142/9231/7525 7315/9235/7500 4147/9233/7526 +f 4147/9233/7526 7314/9236/7497 7214/9234/7222 +f 4149/9241/7530 4148/9238/7527 4153/9240/7529 +f 7249/9250/7305 4149/9241/7530 7250/9242/7306 +f 4153/9240/7529 4152/9239/7528 7317/9244/7506 +f 7250/9242/7306 4153/9240/7529 7932/9245/7262 +f 4148/9238/7527 4149/9241/7530 4150/9247/7532 +f 4150/9247/7532 4154/9246/7531 7324/9249/7534 +f 4149/9241/7530 7249/9250/7305 4154/9246/7531 +f 4154/9246/7531 7248/9251/7299 7323/9248/7533 +f 4151/9254/7536 4148/9238/7527 4155/9253/7535 +f 7216/9260/7226 4151/9254/7536 7217/9255/7227 +f 4155/9253/7535 4150/9247/7532 7325/9256/7537 +f 7217/9255/7227 4155/9253/7535 7924/9257/7210 +f 4148/9238/7527 4151/9254/7536 4152/9239/7528 +f 4152/9239/7528 4156/9258/7538 7318/9243/7507 +f 4151/9254/7536 7216/9260/7226 4156/9258/7538 +f 4156/9258/7538 7215/9261/7234 7319/9259/7511 +f 4157/9271/7544 4161/9263/7539 4158/9265/7541 +f 4158/9265/7541 4162/9264/7540 7327/9267/7543 +f 4161/9263/7539 7204/9268/7192 4162/9264/7540 +f 4162/9264/7540 7203/9269/7189 7326/9266/7542 +f 4159/9273/7546 4157/9271/7544 4163/9272/7545 +f 7258/8962/7328 4159/9273/7546 7259/8963/7329 +f 4163/9272/7545 4158/9265/7541 7328/9274/7547 +f 7259/8963/7329 4163/9272/7545 7939/8964/7330 +f 4157/9271/7544 4159/9273/7546 4160/9276/7549 +f 4160/9276/7549 4164/9275/7548 7321/9228/7522 +f 4159/9273/7546 7258/8962/7328 4164/9275/7548 +f 4164/9275/7548 7257/8957/7323 7322/9229/7523 +f 4161/9263/7539 4157/9271/7544 4165/9277/7550 +f 7204/9268/7192 4161/9263/7539 7205/9278/7193 +f 4165/9277/7550 4160/9276/7549 7320/9222/7519 +f 7205/9278/7193 4165/9277/7550 7923/9225/7194 +f 4166/9284/7554 4170/9279/7551 4167/9281/7553 +f 4167/9281/7553 4171/9280/7552 7267/9283/7339 +f 4170/9279/7551 7324/9249/7534 4171/9280/7552 +f 4171/9280/7552 7323/9248/7533 7268/9282/7345 +f 4168/9286/7556 4166/9284/7554 4172/9285/7555 +f 7330/9294/7560 4168/9286/7556 7329/9287/7557 +f 4172/9285/7555 4167/9281/7553 7266/9288/7338 +f 7329/9287/7557 4172/9285/7555 7940/9289/7342 +f 4166/9284/7554 4168/9286/7556 4169/9291/7559 +f 4169/9291/7559 4173/9290/7558 7207/9293/7198 +f 4168/9286/7556 7330/9294/7560 4173/9290/7558 +f 4173/9290/7558 7331/9295/7561 7208/9292/7199 +f 4170/9279/7551 4166/9284/7554 4174/9297/7562 +f 7324/9249/7534 4170/9279/7551 7325/9256/7537 +f 4174/9297/7562 4169/9291/7559 7206/9298/7208 +f 7325/9256/7537 4174/9297/7562 7924/9257/7210 +f 4175/9307/7568 4179/9299/7563 4176/9301/7565 +f 4176/9301/7565 4180/9300/7564 7333/9303/7567 +f 4179/9299/7563 7192/9304/7160 4180/9300/7564 +f 4180/9300/7564 7191/9305/7157 7332/9302/7566 +f 4177/9309/7570 4175/9307/7568 4181/9308/7569 +f 7276/9002/7368 4177/9309/7570 7277/9003/7369 +f 4181/9308/7569 4176/9301/7565 7334/9310/7571 +f 7277/9003/7369 4181/9308/7569 7941/9004/7370 +f 4175/9307/7568 4177/9309/7570 4178/9312/7573 +f 4178/9312/7573 4182/9311/7572 7327/9267/7543 +f 4177/9309/7570 7276/9002/7368 4182/9311/7572 +f 4182/9311/7572 7275/8997/7363 7328/9274/7547 +f 4179/9299/7563 4175/9307/7568 4183/9313/7574 +f 7192/9304/7160 4179/9299/7563 7193/9314/7161 +f 4183/9313/7574 4178/9312/7573 7326/9266/7542 +f 7193/9314/7161 4183/9313/7574 7921/9270/7162 +f 4184/9320/7578 4188/9315/7575 4185/9317/7577 +f 4185/9317/7577 4189/9316/7576 7285/9319/7379 +f 4188/9315/7575 7330/9294/7560 4189/9316/7576 +f 4189/9316/7576 7329/9287/7557 7286/9318/7386 +f 4186/9322/7580 4184/9320/7578 4190/9321/7579 +f 7336/9330/7584 4186/9322/7580 7335/9323/7581 +f 4190/9321/7579 4185/9317/7577 7284/9324/7378 +f 7335/9323/7581 4190/9321/7579 7942/9325/7382 +f 4184/9320/7578 4186/9322/7580 4187/9327/7583 +f 4187/9327/7583 4191/9326/7582 7195/9329/7167 +f 4186/9322/7580 7336/9330/7584 4191/9326/7582 +f 4191/9326/7582 7337/9331/7585 7196/9328/7168 +f 4188/9315/7575 4184/9320/7578 4192/9333/7586 +f 7330/9294/7560 4188/9315/7575 7331/9295/7561 +f 4192/9333/7586 4187/9327/7583 7194/9334/7176 +f 7331/9295/7561 4192/9333/7586 7922/9296/7178 +f 4194/9338/7590 4193/9335/7587 4198/9337/7589 +f 7339/9345/7594 4194/9338/7590 7338/9339/7591 +f 4198/9337/7589 4197/9336/7588 7179/9341/7124 +f 7338/9339/7591 4198/9337/7589 7917/9342/7098 +f 4193/9335/7587 4194/9338/7590 4195/9344/7593 +f 4195/9344/7593 4199/9343/7592 7279/9001/7367 +f 4194/9338/7590 7339/9345/7594 4199/9343/7592 +f 4199/9343/7592 7340/9346/7595 7280/9007/7373 +f 4196/9348/7597 4193/9335/7587 4200/9347/7596 +f 7333/9303/7567 4196/9348/7597 7334/9310/7571 +f 4200/9347/7596 4195/9344/7593 7278/9000/7366 +f 7334/9310/7571 4200/9347/7596 7941/9004/7370 +f 4193/9335/7587 4196/9348/7597 4197/9336/7588 +f 4197/9336/7588 4201/9349/7598 7180/9340/7125 +f 4201/9349/7598 4196/9348/7597 7332/9302/7566 +f 7181/9350/7129 4201/9349/7598 7919/9306/7130 +f 4203/9354/7602 4202/9351/7599 4207/9353/7601 +f 7282/9363/7380 4203/9354/7602 7283/9355/7381 +f 4207/9353/7601 4206/9352/7600 7335/9357/7581 +f 7283/9355/7381 4207/9353/7601 7942/9358/7382 +f 4202/9351/7599 4203/9354/7602 4204/9360/7604 +f 4204/9360/7604 4208/9359/7603 7342/9362/7606 +f 4203/9354/7602 7282/9363/7380 4208/9359/7603 +f 4208/9359/7603 7281/9364/7392 7341/9361/7605 +f 4205/9367/7608 4202/9351/7599 4209/9366/7607 +f 7183/9373/7135 4205/9367/7608 7184/9368/7136 +f 4209/9366/7607 4204/9360/7604 7343/9369/7609 +f 7184/9368/7136 4209/9366/7607 7918/9370/7114 +f 4202/9351/7599 4205/9367/7608 4206/9352/7600 +f 7336/9356/7584 4206/9352/7600 7337/9372/7585 +f 4205/9367/7608 7183/9373/7135 4210/9371/7610 +f 7337/9372/7585 4210/9371/7610 7920/9375/7146 +f 4212/9381/7614 4211/9376/7611 4215/9378/7613 +f 4212/9381/7614 4215/9378/7613 7291/9034/7399 +f 4215/9378/7613 4214/9377/7612 7167/9380/7093 +f 4215/9378/7613 7167/9380/7093 7292/9035/7400 +f 4213/9383/7616 4211/9376/7611 4216/9382/7615 +f 7339/9345/7594 4213/9383/7616 7340/9346/7595 +f 4216/9382/7615 4212/9381/7614 7290/9048/7409 +f 7340/9346/7595 4216/9382/7615 7943/9008/7374 +f 4211/9376/7611 4213/9383/7616 4214/9377/7612 +f 4214/9377/7612 4217/9384/7617 7168/9379/7096 +f 4213/9383/7616 7339/9345/7594 4217/9384/7617 +f 4217/9384/7617 7338/9339/7591 7169/9385/7097 +f 4218/9391/7621 4221/9386/7618 4219/9388/7620 +f 4219/9388/7620 4222/9387/7619 7342/9362/7606 +f 4221/9386/7618 7171/9389/7103 4222/9387/7619 +f 4222/9387/7619 7170/9390/7111 7343/9369/7609 +f 4220/9393/7623 4218/9391/7621 4223/9392/7622 +f 7300/9397/7423 4220/9393/7623 7301/9394/7424 +f 4223/9392/7622 4219/9388/7620 7341/9361/7605 +f 7301/9394/7424 4223/9392/7622 7944/9365/7394 +f 4221/9386/7618 4218/9391/7621 4224/9395/7624 +f 7171/9389/7103 4221/9386/7618 7172/9396/7104 +f 4220/9393/7623 7300/9397/7423 4224/9395/7624 +f 4224/9395/7624 7299/9398/7421 7172/9396/7104 +f 4226/9403/7628 4225/9400/7625 4230/9402/7627 +f 6811/9412/6174 4226/9403/7628 6812/9404/6175 +f 4230/9402/7627 4229/9401/7626 7346/9406/7630 +f 6812/9404/6175 4230/9402/7627 7843/9407/6150 +f 4225/9400/7625 4226/9403/7628 4227/9409/7632 +f 4227/9409/7632 4231/9408/7631 7348/9411/7634 +f 4226/9403/7628 6811/9412/6174 4231/9408/7631 +f 4231/9408/7631 6810/9413/6187 7347/9410/7633 +f 4228/9416/7636 4225/9400/7625 4232/9415/7635 +f 7351/9422/7642 4228/9416/7636 7350/9417/7637 +f 4232/9415/7635 4227/9409/7632 7349/9418/7638 +f 7350/9417/7637 4232/9415/7635 7955/9419/7639 +f 4225/9400/7625 4228/9416/7636 4229/9401/7626 +f 4229/9401/7626 4233/9420/7640 7345/9405/7629 +f 4228/9416/7636 7351/9422/7642 4233/9420/7640 +f 4233/9420/7640 7352/9423/7643 7344/9421/7641 +f 4235/9428/7648 4234/9425/7645 4239/9427/7647 +f 7357/9435/7655 4235/9428/7648 7356/9429/7649 +f 4239/9427/7647 4238/9426/7646 7355/9431/7651 +f 7356/9429/7649 4239/9427/7647 7956/9432/7652 +f 4234/9425/7645 4235/9428/7648 4236/9434/7654 +f 4236/9434/7654 4240/9433/7653 6826/7551/6206 +f 4235/9428/7648 7357/9435/7655 4240/9433/7653 +f 4240/9433/7653 7358/9436/7656 6827/7554/6209 +f 4237/9438/7658 4234/9425/7645 4241/9437/7657 +f 7360/9442/7662 4237/9438/7658 7359/9439/7659 +f 4241/9437/7657 4236/9434/7654 6825/7550/6205 +f 7359/9439/7659 4241/9437/7657 7844/7515/6170 +f 4234/9425/7645 4237/9438/7658 4238/9426/7646 +f 4238/9426/7646 4242/9440/7660 7354/9430/7650 +f 4237/9438/7658 7360/9442/7662 4242/9440/7660 +f 4242/9440/7660 7361/9443/7663 7353/9441/7661 +f 4244/9448/7668 4243/9445/7665 4248/9447/7667 +f 6793/9455/6134 4244/9448/7668 6794/9449/6135 +f 4248/9447/7667 4247/9446/7666 7364/9451/7670 +f 6794/9449/6135 4248/9447/7667 7845/9452/6110 +f 4243/9445/7665 4244/9448/7668 4245/9454/7672 +f 4245/9454/7672 4249/9453/7671 7345/9405/7629 +f 4244/9448/7668 6793/9455/6134 4249/9453/7671 +f 4249/9453/7671 6792/9456/6147 7346/9406/7630 +f 4246/9458/7674 4243/9445/7665 4250/9457/7673 +f 7366/9462/7678 4246/9458/7674 7365/9459/7675 +f 4250/9457/7673 4245/9454/7672 7344/9421/7641 +f 7365/9459/7675 4250/9457/7673 7953/9424/7644 +f 4243/9445/7665 4246/9458/7674 4247/9446/7666 +f 4247/9446/7666 4251/9460/7676 7363/9450/7669 +f 4246/9458/7674 7366/9462/7678 4251/9460/7676 +f 4251/9460/7676 7367/9463/7679 7362/9461/7677 +f 4253/9468/7684 4252/9465/7681 4257/9467/7683 +f 7360/9442/7662 4253/9468/7684 7361/9443/7663 +f 4257/9467/7683 4256/9466/7682 7370/9470/7686 +f 7361/9443/7663 4257/9467/7683 7954/9444/7664 +f 4252/9465/7681 4253/9468/7684 4254/9472/7688 +f 4254/9472/7688 4258/9471/7687 6808/7508/6166 +f 4253/9468/7684 7360/9442/7662 4258/9471/7687 +f 4258/9471/7687 7359/9439/7659 6809/7514/6169 +f 4255/9474/7690 4252/9465/7681 4259/9473/7689 +f 7372/9478/7694 4255/9474/7690 7371/9475/7691 +f 4259/9473/7689 4254/9472/7688 6807/7507/6165 +f 7371/9475/7691 4259/9473/7689 7846/7511/6130 +f 4252/9465/7681 4255/9474/7690 4256/9466/7682 +f 4256/9466/7682 4260/9476/7692 7369/9469/7685 +f 4255/9474/7690 7372/9478/7694 4260/9476/7692 +f 4260/9476/7692 7373/9479/7695 7368/9477/7693 +f 4262/9484/7700 4261/9481/7697 4266/9483/7699 +f 6775/9491/6094 4262/9484/7700 6776/9485/6095 +f 4265/9482/7698 7375/9486/7701 4266/9483/7699 +f 4266/9483/7699 7376/9487/7702 6776/9485/6095 +f 4261/9481/7697 4262/9484/7700 4263/9490/7704 +f 4263/9490/7704 4267/9489/7703 7363/9450/7669 +f 4262/9484/7700 6775/9491/6094 4267/9489/7703 +f 4267/9489/7703 6774/9492/6108 7364/9451/7670 +f 4264/9494/7706 4261/9481/7697 4268/9493/7705 +f 7378/9498/7710 4264/9494/7706 7377/9495/7707 +f 4268/9493/7705 4263/9490/7704 7362/9461/7677 +f 7377/9495/7707 4268/9493/7705 7951/9464/7680 +f 4261/9481/7697 4264/9494/7706 4265/9482/7698 +f 7375/9486/7701 4265/9482/7698 7374/9497/7709 +f 4264/9494/7706 7378/9498/7710 4269/9496/7708 +f 4269/9496/7708 7379/9499/7711 7374/9497/7709 +f 4271/9504/7716 4270/9501/7713 4275/9503/7715 +f 7372/9478/7694 4271/9504/7716 7373/9479/7695 +f 4275/9503/7715 4274/9502/7714 7382/9506/7718 +f 7373/9479/7695 4275/9503/7715 7952/9480/7696 +f 4270/9501/7713 4271/9504/7716 4272/9508/7720 +f 4272/9508/7720 4276/9507/7719 6790/9510/6126 +f 4271/9504/7716 7372/9478/7694 4276/9507/7719 +f 4276/9507/7719 7371/9475/7691 6791/9509/6129 +f 4273/9516/7724 4270/9501/7713 4277/9511/7721 +f 4273/9516/7724 4277/9511/7721 7384/9513/7723 +f 4277/9511/7721 4272/9508/7720 6789/9514/6125 +f 4277/9511/7721 6789/9514/6125 7383/9512/7722 +f 4270/9501/7713 4273/9516/7724 4274/9502/7714 +f 4274/9502/7714 4278/9517/7725 7381/9505/7717 +f 4278/9517/7725 4273/9516/7724 7385/9519/7727 +f 4278/9517/7725 7385/9519/7727 7380/9518/7726 +f 4279/9529/7734 4283/9521/7729 4280/9523/7731 +f 4280/9523/7731 4284/9522/7730 6757/9525/6055 +f 4283/9521/7729 7387/9526/7732 4284/9522/7730 +f 4284/9522/7730 7388/9527/7733 6758/9524/6056 +f 4281/9531/7736 4279/9529/7734 4285/9530/7735 +f 7375/9539/7701 4281/9531/7736 7376/9532/7702 +f 4285/9530/7735 4280/9523/7731 6756/9533/6068 +f 7376/9532/7702 4285/9530/7735 7847/9534/6070 +f 4279/9529/7734 4281/9531/7736 4282/9536/7738 +f 4282/9536/7738 4286/9535/7737 7390/9538/7740 +f 4281/9531/7736 7375/9539/7701 4286/9535/7737 +f 4286/9535/7737 7374/9540/7709 7389/9537/7739 +f 4283/9521/7729 4279/9529/7734 4287/9542/7741 +f 7387/9526/7732 4283/9521/7729 7386/9543/7742 +f 4287/9542/7741 4282/9536/7738 7391/9544/7743 +f 7386/9543/7742 4287/9542/7741 7947/9545/7744 +f 4288/9554/7750 4292/9546/7745 4289/9548/7747 +f 4289/9548/7747 4293/9547/7746 7384/9550/7723 +f 4292/9546/7745 7393/9551/7748 4293/9547/7746 +f 4293/9547/7746 7394/9552/7749 7385/9549/7727 +f 4290/9556/7752 4288/9554/7750 4294/9555/7751 +f 6772/9564/6088 4290/9556/7752 6773/9557/6089 +f 4294/9555/7751 4289/9548/7747 7383/9558/7722 +f 6773/9557/6089 4294/9555/7751 7848/9559/6090 +f 4288/9554/7750 4290/9556/7752 4291/9561/7754 +f 4291/9561/7754 4295/9560/7753 7396/9563/7756 +f 4290/9556/7752 6772/9564/6088 4295/9560/7753 +f 4295/9560/7753 6771/9565/6085 7395/9562/7755 +f 4292/9546/7745 4288/9554/7750 4296/9567/7757 +f 7393/9551/7748 4292/9546/7745 7392/9568/7758 +f 4296/9567/7757 4291/9561/7754 7397/9569/7759 +f 7392/9568/7758 4296/9567/7757 7948/9570/7760 +f 4297/9579/7766 4301/9571/7761 4298/9573/7763 +f 4298/9573/7763 4302/9572/7762 6739/9575/6015 +f 4301/9571/7761 7399/9576/7764 4302/9572/7762 +f 4302/9572/7762 7400/9577/7765 6740/9574/6016 +f 4299/9581/7768 4297/9579/7766 4303/9580/7767 +f 7387/9526/7732 4299/9581/7768 7388/9527/7733 +f 4303/9580/7767 4298/9573/7763 6738/9582/6027 +f 7388/9527/7733 4303/9580/7767 7849/9528/6030 +f 4297/9579/7766 4299/9581/7768 4300/9584/7770 +f 4300/9584/7770 4304/9583/7769 7402/9586/7772 +f 4299/9581/7768 7387/9526/7732 4304/9583/7769 +f 4304/9583/7769 7386/9543/7742 7401/9585/7771 +f 4301/9571/7761 4297/9579/7766 4305/9587/7773 +f 7399/9576/7764 4301/9571/7761 7398/9588/7774 +f 4305/9587/7773 4300/9584/7770 7403/9589/7775 +f 4305/9587/7773 7403/9589/7775 7398/9588/7774 +f 4306/9599/7782 4310/9591/7777 4307/9593/7779 +f 4307/9593/7779 4311/9592/7778 7396/9595/7756 +f 4310/9591/7777 7405/9596/7780 4311/9592/7778 +f 4311/9592/7778 7406/9597/7781 7397/9594/7759 +f 4308/9601/7784 4306/9599/7782 4312/9600/7783 +f 6754/9609/6048 4308/9601/7784 6755/9602/6049 +f 4312/9600/7783 4307/9593/7779 7395/9603/7755 +f 6755/9602/6049 4312/9600/7783 7850/9604/6050 +f 4306/9599/7782 4308/9601/7784 4309/9606/7786 +f 4309/9606/7786 4313/9605/7785 7408/9608/7788 +f 4308/9601/7784 6754/9609/6048 4313/9605/7785 +f 4313/9605/7785 6753/9610/6045 7407/9607/7787 +f 4310/9591/7777 4306/9599/7782 4314/9612/7789 +f 7405/9596/7780 4310/9591/7777 7404/9613/7790 +f 4314/9612/7789 4309/9606/7786 7409/9614/7791 +f 4314/9612/7789 7409/9614/7791 7404/9613/7790 +f 4315/9624/7798 4319/9616/7793 4316/9618/7795 +f 4316/9618/7795 4320/9617/7794 6718/9620/5965 +f 4319/9616/7793 7411/9621/7796 4320/9617/7794 +f 4320/9617/7794 7412/9622/7797 6719/9619/5973 +f 4317/9626/7800 4315/9624/7798 4321/9625/7799 +f 7399/9633/7764 4317/9626/7800 7400/9627/7765 +f 4321/9625/7799 4316/9618/7795 6717/9628/5964 +f 7400/9627/7765 4321/9625/7799 7851/9629/5968 +f 4315/9624/7798 4317/9626/7800 4318/9631/7802 +f 7414/9638/7806 4318/9631/7802 7413/9632/7803 +f 4317/9626/7800 7399/9633/7764 4322/9630/7801 +f 7413/9632/7803 4322/9630/7801 7945/9635/7776 +f 4315/9624/7798 4318/9631/7802 4319/9616/7793 +f 4319/9616/7793 4323/9636/7804 7411/9621/7796 +f 4318/9631/7802 7414/9638/7806 4323/9636/7804 +f 4323/9636/7804 7415/9639/7807 7410/9637/7805 +f 4324/9646/7814 4328/9641/7809 4325/9643/7811 +f 4325/9643/7811 4329/9642/7810 7408/9608/7788 +f 4329/9642/7810 4328/9641/7809 7418/9645/7813 +f 7409/9614/7791 4329/9642/7810 7946/9615/7792 +f 4326/9648/7816 4324/9646/7814 4330/9647/7815 +f 6733/9654/6004 4326/9648/7816 6734/9649/6005 +f 4330/9647/7815 4325/9643/7811 7407/9607/7787 +f 6734/9649/6005 4330/9647/7815 7852/9611/6006 +f 4324/9646/7814 4326/9648/7816 4327/9651/7818 +f 4327/9651/7818 4331/9650/7817 7420/9653/7820 +f 4326/9648/7816 6733/9654/6004 4331/9650/7817 +f 4331/9650/7817 6732/9655/5997 7419/9652/7819 +f 4324/9646/7814 4327/9651/7818 4328/9641/7809 +f 4328/9641/7809 4332/9657/7821 7417/9644/7812 +f 4327/9651/7818 7420/9653/7820 4332/9657/7821 +f 4332/9657/7821 7421/9659/7823 7416/9658/7822 +f 4333/9669/7830 4337/9661/7825 4334/9663/7827 +f 4334/9663/7827 4338/9662/7826 7423/9665/7829 +f 4337/9661/7825 7414/9666/7806 4338/9662/7826 +f 4338/9662/7826 7413/9667/7803 7422/9664/7828 +f 4335/9671/7832 4333/9669/7830 4339/9670/7831 +f 7351/9679/7642 4335/9671/7832 7352/9672/7643 +f 4339/9670/7831 4334/9663/7827 7424/9673/7833 +f 7352/9672/7643 4339/9670/7831 7953/9674/7644 +f 4333/9669/7830 4335/9671/7832 4336/9676/7835 +f 4336/9676/7835 4340/9675/7834 7426/9678/7837 +f 4335/9671/7832 7351/9679/7642 4340/9675/7834 +f 4340/9675/7834 7350/9680/7637 7425/9677/7836 +f 4337/9661/7825 4333/9669/7830 4341/9682/7838 +f 7414/9666/7806 4337/9661/7825 7415/9683/7807 +f 4341/9682/7838 4336/9676/7835 7427/9684/7839 +f 7415/9683/7807 4341/9682/7838 7957/9685/7808 +f 4342/9694/7845 4346/9686/7840 4343/9688/7842 +f 4343/9688/7842 4347/9687/7841 7354/9690/7650 +f 4346/9686/7840 7429/9691/7843 4347/9687/7841 +f 4347/9687/7841 7430/9692/7844 7355/9689/7651 +f 4344/9696/7847 4342/9694/7845 4348/9695/7846 +f 7432/9702/7851 4344/9696/7847 7431/9697/7848 +f 4348/9695/7846 4343/9688/7842 7353/9698/7661 +f 7431/9697/7848 4348/9695/7846 7954/9699/7664 +f 4342/9694/7845 4344/9696/7847 4345/9701/7850 +f 4345/9701/7850 4349/9700/7849 7417/9644/7812 +f 4344/9696/7847 7432/9702/7851 4349/9700/7849 +f 4349/9700/7849 7433/9703/7852 7418/9645/7813 +f 4346/9686/7840 4342/9694/7845 4350/9704/7853 +f 7429/9691/7843 4346/9686/7840 7428/9705/7854 +f 4350/9704/7853 4345/9701/7850 7416/9658/7822 +f 7428/9705/7854 4350/9704/7853 7958/9660/7824 +f 4352/9709/7858 4351/9706/7855 4356/9708/7857 +f 7402/9715/7772 4352/9709/7858 7403/9710/7775 +f 4356/9708/7857 4355/9707/7856 7422/9664/7828 +f 7403/9710/7775 4356/9708/7857 7945/9668/7776 +f 4351/9706/7855 4352/9709/7858 4353/9712/7860 +f 4353/9712/7860 4357/9711/7859 7435/9714/7862 +f 4352/9709/7858 7402/9715/7772 4357/9711/7859 +f 7434/9713/7861 4357/9711/7859 7947/9717/7744 +f 4354/9719/7864 4351/9706/7855 4358/9718/7863 +f 7366/9724/7678 4354/9719/7864 7367/9720/7679 +f 4358/9718/7863 4353/9712/7860 7436/9721/7865 +f 7367/9720/7679 4358/9718/7863 7951/9722/7680 +f 4351/9706/7855 4354/9719/7864 4355/9707/7856 +f 4355/9707/7856 4359/9723/7866 7423/9665/7829 +f 4354/9719/7864 7366/9724/7678 4359/9723/7866 +f 4359/9723/7866 7365/9725/7675 7424/9673/7833 +f 4361/9729/7870 4360/9726/7867 4365/9728/7869 +f 7438/9738/7874 4361/9729/7870 7437/9730/7871 +f 4365/9728/7869 4364/9727/7868 7368/9732/7693 +f 7437/9730/7871 4365/9728/7869 7952/9733/7696 +f 4360/9726/7867 4361/9729/7870 4362/9735/7873 +f 4362/9735/7873 4366/9734/7872 7405/9737/7780 +f 4361/9729/7870 7438/9738/7874 4366/9734/7872 +f 7406/9736/7781 4366/9734/7872 7948/9740/7760 +f 4363/9742/7877 4360/9726/7867 4367/9741/7876 +f 7432/9748/7851 4363/9742/7877 7433/9743/7852 +f 4367/9741/7876 4362/9735/7873 7404/9744/7790 +f 7433/9743/7852 4367/9741/7876 7946/9745/7792 +f 4360/9726/7867 4363/9742/7877 4364/9727/7868 +f 4364/9727/7868 4368/9746/7878 7369/9731/7685 +f 4363/9742/7877 7432/9748/7851 4368/9746/7878 +f 4368/9746/7878 7431/9749/7848 7370/9747/7686 +f 4369/9756/7882 4372/9751/7879 4370/9753/7881 +f 4370/9753/7881 4373/9752/7880 7390/9538/7740 +f 4373/9752/7880 4372/9751/7879 7434/9755/7861 +f 4373/9752/7880 7434/9755/7861 7391/9544/7743 +f 4371/9758/7884 4369/9756/7882 4374/9757/7883 +f 7378/9762/7710 4371/9758/7884 7379/9759/7711 +f 4374/9757/7883 4370/9753/7881 7389/9537/7739 +f 7379/9759/7711 4374/9757/7883 7949/9541/7712 +f 4369/9756/7882 4371/9758/7884 4372/9751/7879 +f 4372/9751/7879 4375/9760/7885 7435/9754/7862 +f 4371/9758/7884 7378/9762/7710 4375/9760/7885 +f 4375/9760/7885 7377/9763/7707 7436/9761/7865 +f 4376/9770/7889 4379/9765/7886 4377/9767/7888 +f 4377/9767/7888 4380/9766/7887 7381/9769/7717 +f 4379/9765/7886 7438/9738/7874 4380/9766/7887 +f 4380/9766/7887 7437/9730/7871 7382/9768/7718 +f 4378/9772/7891 4376/9770/7889 4381/9771/7890 +f 7393/9777/7748 4378/9772/7891 7394/9773/7749 +f 4381/9771/7890 4377/9767/7888 7380/9774/7726 +f 7394/9773/7749 4381/9771/7890 7950/9775/7728 +f 4376/9770/7889 4378/9772/7891 4379/9765/7886 +f 7438/9738/7874 4379/9765/7886 7439/9739/7875 +f 4378/9772/7891 7393/9777/7748 4382/9776/7892 +f 4382/9776/7892 7392/9778/7758 7439/9739/7875 +f 4383/9786/7898 4387/9779/7893 4384/9781/7895 +f 6613/9790/5648 4384/9781/7895 6614/9782/5649 +f 4387/9779/7893 7441/9783/7896 4388/9780/7894 +f 4388/9780/7894 7442/9784/7897 6614/9782/5649 +f 4385/9793/7900 4383/9786/7898 4389/9787/7899 +f 4385/9793/7900 4389/9787/7899 6943/9789/6510 +f 4384/9781/7895 6613/9790/5648 4389/9787/7899 +f 4389/9787/7899 6612/9791/5656 6944/9788/6511 +f 4383/9786/7898 4385/9793/7900 4386/9795/7902 +f 4386/9795/7902 4390/9794/7901 7411/9621/7796 +f 4390/9794/7901 4385/9793/7900 6942/9796/6523 +f 7412/9622/7797 4390/9794/7901 7839/9623/5974 +f 4387/9779/7893 4383/9786/7898 4391/9797/7903 +f 7441/9783/7896 4387/9779/7893 7440/9798/7904 +f 4391/9797/7903 4386/9795/7902 7410/9637/7805 +f 7440/9798/7904 4391/9797/7903 7957/9640/7808 +f 4392/9803/7908 4396/9799/7905 4393/9801/7907 +f 6958/9807/6541 4393/9801/7907 6959/9802/6542 +f 4396/9799/7905 7420/9653/7820 4397/9800/7906 +f 6959/9802/6542 4397/9800/7906 7840/9656/5999 +f 4394/9810/7910 4392/9803/7908 4398/9804/7909 +f 4394/9810/7910 4398/9804/7909 6616/9806/5666 +f 4393/9801/7907 6958/9807/6541 4398/9804/7909 +f 4398/9804/7909 6957/9808/6538 6617/9805/5668 +f 4392/9803/7908 4394/9810/7910 4395/9812/7912 +f 4395/9812/7912 4399/9811/7911 7444/9814/7914 +f 4399/9811/7911 4394/9810/7910 6615/9815/5665 +f 4399/9811/7911 6615/9815/5665 7443/9813/7913 +f 4396/9799/7905 4392/9803/7908 4400/9817/7915 +f 7420/9653/7820 4396/9799/7905 7421/9659/7823 +f 4400/9817/7915 4395/9812/7912 7445/9818/7916 +f 7421/9659/7823 4400/9817/7915 7958/9660/7824 +f 4402/9822/7920 4401/9819/7917 4406/9821/7919 +f 7441/9828/7896 4402/9822/7920 7442/9823/7897 +f 4406/9821/7919 4405/9820/7918 6672/7142/5832 +f 7442/9823/7897 4406/9821/7919 7765/7146/5296 +f 4401/9819/7917 4402/9822/7920 4403/9825/7922 +f 4403/9825/7922 4407/9824/7921 7426/9827/7837 +f 4402/9822/7920 7441/9828/7896 4407/9824/7921 +f 4407/9824/7921 7440/9829/7904 7427/9826/7839 +f 4404/9836/7926 4401/9819/7917 4408/9831/7923 +f 4404/9836/7926 4408/9831/7923 7447/9833/7925 +f 4408/9831/7923 4403/9825/7922 7425/9834/7836 +f 7446/9832/7924 4408/9831/7923 7955/9835/7639 +f 4401/9819/7917 4404/9836/7926 4405/9820/7918 +f 4405/9820/7918 4409/9837/7927 6673/7143/5833 +f 4409/9837/7927 4404/9836/7926 7448/9838/7928 +f 6674/7151/5838 4409/9837/7927 7773/7152/5839 +f 4411/9842/7932 4410/9839/7929 4415/9841/7931 +f 7429/9851/7843 4411/9842/7932 7430/9843/7844 +f 4414/9840/7930 7450/9844/7933 4415/9841/7931 +f 7430/9843/7844 4415/9841/7931 7956/9846/7652 +f 4410/9839/7929 4411/9842/7932 4412/9848/7936 +f 4412/9848/7936 4416/9847/7935 7444/9850/7914 +f 4411/9842/7932 7429/9851/7843 4416/9847/7935 +f 4416/9847/7935 7428/9852/7854 7445/9849/7916 +f 4413/9855/7938 4410/9839/7929 4417/9854/7937 +f 6688/7180/5865 4413/9855/7938 6689/7181/5866 +f 4417/9854/7937 4412/9848/7936 7443/9856/7913 +f 6689/7181/5866 4417/9854/7937 7766/6472/5326 +f 4410/9839/7929 4413/9855/7938 4414/9840/7930 +f 7450/9844/7933 4414/9840/7930 7449/9858/7940 +f 4413/9855/7938 6688/7180/5865 4418/9857/7939 +f 7449/9858/7940 4418/9857/7939 7774/7177/5862 +f 4420/9862/7944 4419/9859/7941 4424/9861/7943 +f 6697/9871/5896 4420/9862/7944 6698/9863/5897 +f 4424/9861/7943 4423/9860/7942 6924/9865/6454 +f 6698/9863/5897 4424/9861/7943 7771/9866/5727 +f 4419/9859/7941 4420/9862/7944 4421/9868/7946 +f 4421/9868/7946 4425/9867/7945 7447/9870/7925 +f 4420/9862/7944 6697/9871/5896 4425/9867/7945 +f 4425/9867/7945 6696/9872/5907 7448/9869/7928 +f 4422/9879/7948 4419/9859/7941 4426/9874/7947 +f 4422/9879/7948 4426/9874/7947 7348/9876/7634 +f 4426/9874/7947 4421/9868/7946 7446/9877/7924 +f 4426/9874/7947 7446/9877/7924 7349/9875/7638 +f 4419/9859/7941 4422/9879/7948 4423/9860/7942 +f 4423/9860/7942 4427/9880/7949 6925/9864/6455 +f 4427/9880/7949 4422/9879/7948 7347/9882/7633 +f 6926/9881/6459 4427/9880/7949 7841/9883/6190 +f 4429/9887/7953 4428/9884/7950 4433/9886/7952 +f 7450/9896/7933 4429/9887/7953 7451/9888/7934 +f 4432/9885/7951 7357/9889/7655 4433/9886/7952 +f 4433/9886/7952 7356/9890/7649 7451/9888/7934 +f 4428/9884/7950 4429/9887/7953 4430/9893/7955 +f 4430/9893/7955 4434/9892/7954 6706/9895/5920 +f 4429/9887/7953 7450/9896/7933 4434/9892/7954 +f 4434/9892/7954 7449/9897/7940 6707/9894/5922 +f 4431/9900/7957 4428/9884/7950 4435/9899/7956 +f 6934/9906/6477 4431/9900/7957 6935/9901/6478 +f 4435/9899/7956 4430/9893/7955 6705/9902/5919 +f 6935/9901/6478 4435/9899/7956 7772/9903/5746 +f 4428/9884/7950 4431/9900/7957 4432/9885/7951 +f 7357/9889/7655 4432/9885/7951 7358/9905/7656 +f 4431/9900/7957 6934/9906/6477 4436/9904/7958 +f 7358/9905/7656 4436/9904/7958 7842/9908/6210 diff --git a/example/ios/.gitignore b/example/ios/.gitignore new file mode 100644 index 00000000..151026b9 --- /dev/null +++ b/example/ios/.gitignore @@ -0,0 +1,33 @@ +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 00000000..8d4492f9 --- /dev/null +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 9.0 + + diff --git a/example/ios/Flutter/Debug.xcconfig b/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 00000000..ec97fc6f --- /dev/null +++ b/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/example/ios/Flutter/Release.xcconfig b/example/ios/Flutter/Release.xcconfig new file mode 100644 index 00000000..c4855bfe --- /dev/null +++ b/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/example/ios/Podfile b/example/ios/Podfile new file mode 100644 index 00000000..9061ff8f --- /dev/null +++ b/example/ios/Podfile @@ -0,0 +1,41 @@ +# Uncomment this line to define a global platform for your project +platform :ios, '12.1' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock new file mode 100644 index 00000000..f2a9bb4d --- /dev/null +++ b/example/ios/Podfile.lock @@ -0,0 +1,53 @@ +PODS: + - Filament (1.12.4): + - Filament/camutils (= 1.12.4) + - Filament/filamat (= 1.12.4) + - Filament/filament (= 1.12.4) + - Filament/filameshio (= 1.12.4) + - Filament/gltfio_core (= 1.12.4) + - Filament/image (= 1.12.4) + - Filament/math (= 1.12.4) + - Filament/utils (= 1.12.4) + - Filament/camutils (1.12.4): + - Filament/math + - Filament/filamat (1.12.4): + - Filament/math + - Filament/utils + - Filament/filament (1.12.4): + - Filament/math + - Filament/utils + - Filament/filameshio (1.12.4): + - Filament/filament + - Filament/gltfio_core (1.12.4): + - Filament/filament + - Filament/image (1.12.4): + - Filament/filament + - Filament/math (1.12.4) + - Filament/utils (1.12.4) + - Flutter (1.0.0) + - mimetic_filament (0.0.1): + - Filament (~> 1.12.3) + - Flutter + +DEPENDENCIES: + - Flutter (from `Flutter`) + - mimetic_filament (from `.symlinks/plugins/mimetic_filament/ios`) + +SPEC REPOS: + trunk: + - Filament + +EXTERNAL SOURCES: + Flutter: + :path: Flutter + mimetic_filament: + :path: ".symlinks/plugins/mimetic_filament/ios" + +SPEC CHECKSUMS: + Filament: 701abb8200237da11b6f74c318f2a875ef309b91 + Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a + mimetic_filament: 089d6e6ef4a3061fa22b023a895e0a3e0e350254 + +PODFILE CHECKSUM: 7adbc9d59f05e1b01f554ea99b6c79e97f2214a2 + +COCOAPODS: 1.10.2 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 00000000..d3759bfd --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,536 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 8A41CA36A92CBF01E35DB723 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 729293464FDAFA80294C03ED /* Pods_Runner.framework */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 729293464FDAFA80294C03ED /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 797D1F14B06B2A55521439EC /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + C38BB8F063815E911BAE04B0 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + FCA201E227680DE4C83A0A35 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8A41CA36A92CBF01E35DB723 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + B35AFB2F8AF234BCF7B9F86F /* Pods */, + F66758AA6E5A6218A5D00433 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; + B35AFB2F8AF234BCF7B9F86F /* Pods */ = { + isa = PBXGroup; + children = ( + 797D1F14B06B2A55521439EC /* Pods-Runner.debug.xcconfig */, + FCA201E227680DE4C83A0A35 /* Pods-Runner.release.xcconfig */, + C38BB8F063815E911BAE04B0 /* Pods-Runner.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + F66758AA6E5A6218A5D00433 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 729293464FDAFA80294C03ED /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 3734E56921255BC0B0DCAC3C /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1020; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3734E56921255BC0B0DCAC3C /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 873X6R39HP; + ENABLE_BITCODE = NO; + GCC_INPUT_FILETYPE = sourcecode.cpp.objcpp; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.mimeticAvatarExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 873X6R39HP; + ENABLE_BITCODE = NO; + GCC_INPUT_FILETYPE = sourcecode.cpp.objcpp; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.mimeticAvatarExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = 873X6R39HP; + ENABLE_BITCODE = NO; + GCC_INPUT_FILETYPE = sourcecode.cpp.objcpp; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.mimeticAvatarExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..f9b0d7c5 --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 00000000..a28140cf --- /dev/null +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..21a3cc14 --- /dev/null +++ b/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..f9b0d7c5 --- /dev/null +++ b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/example/ios/Runner/AppDelegate.swift b/example/ios/Runner/AppDelegate.swift new file mode 100644 index 00000000..70693e4a --- /dev/null +++ b/example/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..d36b1fab --- /dev/null +++ b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 00000000..dc9ada47 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 00000000..28c6bf03 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 00000000..2ccbfd96 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 00000000..f091b6b0 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 00000000..4cde1211 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 00000000..d0ef06e7 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 00000000..dcdc2306 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 00000000..2ccbfd96 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 00000000..c8f9ed8f Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 00000000..a6d6b860 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 00000000..a6d6b860 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 00000000..75b2d164 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 00000000..c4df70d3 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 00000000..6a84f41e Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 00000000..d0e1f585 Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 00000000..0bedcf2f --- /dev/null +++ b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 00000000..9da19eac Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 00000000..9da19eac Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 00000000..9da19eac Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 00000000..89c2725b --- /dev/null +++ b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/example/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..f2e259c7 --- /dev/null +++ b/example/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/ios/Runner/Base.lproj/Main.storyboard b/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 00000000..f3c28516 --- /dev/null +++ b/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist new file mode 100644 index 00000000..b0cc6bd9 --- /dev/null +++ b/example/ios/Runner/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + mimetic_filament_example + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/example/ios/Runner/Runner-Bridging-Header.h b/example/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 00000000..308a2a56 --- /dev/null +++ b/example/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/example/lib/main.dart b/example/lib/main.dart new file mode 100644 index 00000000..06ebb516 --- /dev/null +++ b/example/lib/main.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:mimetic_filament/filament_controller.dart'; +import 'package:mimetic_filament/view/filament_widget.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + final FilamentController _filamentController = MimeticFilamentController(); + String _platformVersion = 'Unknown'; + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('Plugin example app'), + ), + body: Stack(children: [ + FilamentWidget(controller: _filamentController), + Column(children: [ + ElevatedButton( + child: Text("initialize"), + onPressed: () { + _filamentController.initialize(); + }), + ElevatedButton( + child: Text("load asset"), + onPressed: () { + _filamentController.loadSkybox( + "assets/default_env/default_env_skybox.ktx", + "assets/default_env/default_env_ibl.ktx"); + }), + ]), + ]), + ), + ); + } +} diff --git a/example/pubspec.lock b/example/pubspec.lock new file mode 100644 index 00000000..e02f334d --- /dev/null +++ b/example/pubspec.lock @@ -0,0 +1,182 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.8.2" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.15.0" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.4" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + lints: + dependency: transitive + description: + name: lints + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.11" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.7.0" + mimetic_filament: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "0.0.1" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.1" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.3" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" +sdks: + dart: ">=2.12.0 <3.0.0" + flutter: ">=1.20.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml new file mode 100644 index 00000000..354966cc --- /dev/null +++ b/example/pubspec.yaml @@ -0,0 +1,77 @@ +name: mimetic_filament_example +description: Demonstrates how to use the mimetic_filament plugin. + +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +environment: + sdk: ">=2.12.0 <3.0.0" + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + mimetic_filament: + path: ../ + + cupertino_icons: ^1.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^1.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware. + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart new file mode 100644 index 00000000..63b0bc32 --- /dev/null +++ b/example/test/widget_test.dart @@ -0,0 +1,27 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility that Flutter provides. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:mimetic_filament_example/main.dart'; + +void main() { + testWidgets('Verify Platform version', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that platform version is retrieved. + expect( + find.byWidgetPredicate( + (Widget widget) => + widget is Text && widget.data!.startsWith('Running on:'), + ), + findsOneWidget, + ); + }); +} diff --git a/ios/.gitignore b/ios/.gitignore new file mode 100644 index 00000000..0c885071 --- /dev/null +++ b/ios/.gitignore @@ -0,0 +1,38 @@ +.idea/ +.vagrant/ +.sconsign.dblite +.svn/ + +.DS_Store +*.swp +profile + +DerivedData/ +build/ +GeneratedPluginRegistrant.h +GeneratedPluginRegistrant.m + +.generated/ + +*.pbxuser +*.mode1v3 +*.mode2v3 +*.perspectivev3 + +!default.pbxuser +!default.mode1v3 +!default.mode2v3 +!default.perspectivev3 + +xcuserdata + +*.moved-aside + +*.pyc +*sync/ +Icon? +.tags* + +/Flutter/Generated.xcconfig +/Flutter/ephemeral/ +/Flutter/flutter_export_environment.sh \ No newline at end of file diff --git a/ios/Assets/.gitkeep b/ios/Assets/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/ios/Classes/MimeticFilamentPlugin.h b/ios/Classes/MimeticFilamentPlugin.h new file mode 100644 index 00000000..3df66ba2 --- /dev/null +++ b/ios/Classes/MimeticFilamentPlugin.h @@ -0,0 +1,4 @@ +#import + +@interface MimeticFilamentPlugin : NSObject +@end diff --git a/ios/Classes/MimeticFilamentPlugin.mm b/ios/Classes/MimeticFilamentPlugin.mm new file mode 100644 index 00000000..55a87f16 --- /dev/null +++ b/ios/Classes/MimeticFilamentPlugin.mm @@ -0,0 +1,12 @@ +#import "MimeticFilamentPlugin.h" +#import "filament/FilamentNativeViewFactory.h" + +FilamentNativeViewFactory* factory; + +@implementation MimeticFilamentPlugin ++ (void)registerWithRegistrar:(NSObject*)registrar { + factory = + [[FilamentNativeViewFactory alloc] initWithRegistrar:registrar]; + [registrar registerViewFactory:factory withId:@"mimetic.app/filament_view"]; +} +@end diff --git a/ios/Classes/filament/FilamentNativeViewFactory.h b/ios/Classes/filament/FilamentNativeViewFactory.h new file mode 100644 index 00000000..1835ec08 --- /dev/null +++ b/ios/Classes/filament/FilamentNativeViewFactory.h @@ -0,0 +1,29 @@ +#ifndef FilamentNativeViewFactory_h +#define FilamentNativeViewFactory_h +#endif /* FilamentNativeViewFactory_h */ + +#import +#import "FilamentViewer.hpp" + +@interface FilamentMethodCallHandler : FlutterMethodChannel +- (void)handleMethodCall:(FlutterMethodCall* _Nonnull)call result:( FlutterResult _Nonnull)result; +- (mimetic::FilamentViewer*) _viewer; +@end + +@interface FilamentNativeViewFactory : NSObject +- (instancetype)initWithRegistrar:(NSObject*)registrar; +- (mimetic::ResourceBuffer)loadResource:(const char* const)path; +- (void)freeResource:(void*)mem size:(size_t)size misc:(void*)misc; +@end + +@interface FilamentNativeView : NSObject + +- (instancetype)initWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args + registrar:(NSObject*)registrar; + +- (UIView*)view; + +@end + diff --git a/ios/Classes/filament/FilamentNativeViewFactory.mm b/ios/Classes/filament/FilamentNativeViewFactory.mm new file mode 100644 index 00000000..caff7174 --- /dev/null +++ b/ios/Classes/filament/FilamentNativeViewFactory.mm @@ -0,0 +1,125 @@ +#import "FilamentNativeViewFactory.h" +#import "FilamentViewController.h" + +static const id VIEW_TYPE = @"mimetic.app/filament_view"; + +static const FilamentNativeViewFactory* _factory; + +static mimetic::ResourceBuffer loadResource(const char* const name) { + return [_factory loadResource:name]; +} + +static void* freeResource(void* mem, size_t size, void* misc) { + [_factory freeResource:mem size:size misc:misc ]; + return nullptr; +} + +@implementation FilamentNativeViewFactory { + NSObject* _registrar; +} + +- (instancetype)initWithRegistrar:(NSObject*)registrar { + self = [super init]; + if (self) { + _registrar = registrar; + } + _factory = self; + return self; +} + +- (NSObject*)createWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args { + return [[FilamentNativeView alloc] initWithFrame:frame + viewIdentifier:viewId + arguments:args + registrar:_registrar]; +} + +@end + +@implementation FilamentMethodCallHandler { + FilamentViewController *_controller; + FlutterMethodChannel* _channel; + mimetic::FilamentViewer* _viewer; + void* _layer; +} +- (instancetype)initWithController:(FilamentViewController*)controller + registrar:(NSObject*)registrar + viewId:(int64_t)viewId + layer:(void*)layer + { + _layer = layer; + _controller = controller; + NSString* channelName = [NSString stringWithFormat:@"%@_%d",VIEW_TYPE,viewId]; + _channel = [FlutterMethodChannel + methodChannelWithName:channelName + binaryMessenger:[registrar messenger]]; + [_channel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult _Nonnull result) { + [self handleMethodCall:call result:result]; + }]; + return self; +} + +- (void)handleMethodCall:(FlutterMethodCall* _Nonnull)call result:(FlutterResult _Nonnull )result { + if([@"initialize" isEqualToString:call.method]) { + [self initialize]; + } else { + result(FlutterMethodNotImplemented); + } +} + +- (mimetic::ResourceBuffer)loadResource:(const char* const)path { + + NSString* documentPath = [NSSearchPathForDirectoriesInDomains( + NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; + NSString* pathComponent = [NSString stringWithUTF8String:path]; + NSString* nsPath = [documentPath stringByAppendingPathComponent:pathComponent]; + + if (![[NSFileManager defaultManager] fileExistsAtPath:nsPath]) { + NSLog(@"Error: no file exists at %@", nsPath); + exit(-1); + } + + NSData* buffer = [NSData dataWithContentsOfFile:nsPath]; + + mimetic::ResourceBuffer rbuf([buffer bytes], [buffer length]); + return rbuf; +} + +- (void)freeResource:(void*)mem size:(size_t)s misc:(void *)m { + // TODO +} + +-(void)initialize { + _viewer = new mimetic::FilamentViewer(_layer, loadResource, freeResource); +} +@end + +@implementation FilamentNativeView { + FilamentView *_view; + FilamentViewController *_controller; + FilamentMethodCallHandler *_handler; +} + +- (instancetype)initWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args + registrar:(NSObject*)registrar { + if (self = [super init]) { + _view = [[FilamentView alloc] init]; + _controller = [[FilamentViewController alloc] initWithRegistrar:registrar]; + _controller.modelView = _view; + [_controller viewDidLoad]; + [_controller startDisplayLink]; + _handler = [[FilamentMethodCallHandler alloc] initWithController:_controller registrar:registrar viewId:viewId layer:(__bridge void*)[_view layer]]; + } + return self; +} + +- (UIView*)view { + return _view; +} + + +@end diff --git a/ios/Classes/filament/FilamentView.h b/ios/Classes/filament/FilamentView.h new file mode 100644 index 00000000..306b8551 --- /dev/null +++ b/ios/Classes/filament/FilamentView.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * FILModelView is simply a UIView with an OpenGL layer. + * + * + */ +@interface FilamentView : UIView + + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/Classes/filament/FilamentView.mm b/ios/Classes/filament/FilamentView.mm new file mode 100644 index 00000000..20c2082c --- /dev/null +++ b/ios/Classes/filament/FilamentView.mm @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// These defines are set in the "Preprocessor Macros" build setting for each scheme. +#include "FilamentView.h" + +#import + +using namespace std; + +@interface FilamentView () + +- (void)initCommon; + +@end + +@implementation FilamentView { + +} + +- (instancetype)initWithFrame:(CGRect)frame { + if (self = [super initWithFrame:frame]) { + [self initCommon]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder*)coder { + if (self = [super initWithCoder:coder]) { + [self initCommon]; + } + return self; +} + +- (void)initCommon { + [self initializeGLLayer]; +} + +- (void)initializeGLLayer { + CAEAGLLayer* glLayer = (CAEAGLLayer*)self.layer; + glLayer.opaque = YES; +} + ++ (Class)layerClass { + return [CAEAGLLayer class]; +} + +@end diff --git a/ios/Classes/filament/FilamentViewController.h b/ios/Classes/filament/FilamentViewController.h new file mode 100644 index 00000000..36a33679 --- /dev/null +++ b/ios/Classes/filament/FilamentViewController.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +#import "FilamentView.h" +#import "Flutter/Flutter.h" + +@interface FilamentViewController : UIViewController + +@property(weak, nonatomic) IBOutlet FilamentView* modelView; + +- (void)startDisplayLink; +- (void)stopDisplayLink; + +-initWithRegistrar:(NSObject*)registrar; + +@end diff --git a/ios/Classes/filament/FilamentViewController.mm b/ios/Classes/filament/FilamentViewController.mm new file mode 100644 index 00000000..39d1e6d8 --- /dev/null +++ b/ios/Classes/filament/FilamentViewController.mm @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "FilamentViewController.h" +#import "FilamentView.h" + +#import + +@implementation FilamentViewController { + CADisplayLink* _displayLink; + NSObject* _registrar; +} + +- (instancetype)initWithRegistrar:(NSObject*)registrar { + if (self = [super init]) { + _registrar = registrar; + } + return self; +} +#pragma mark UIViewController methods + +- (void)viewDidLoad { + [super viewDidLoad]; +} + +- (void)viewWillAppear:(BOOL)animated { + [self startDisplayLink]; +} + +- (void)viewWillDisappear:(BOOL)animated { + [self stopDisplayLink]; +} + +- (void)startDisplayLink { + [self stopDisplayLink]; + + // Call our render method 60 times a second. + _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(render)]; + _displayLink.preferredFramesPerSecond = 60; + [_displayLink addToRunLoop:NSRunLoop.currentRunLoop forMode:NSDefaultRunLoopMode]; +} + +- (void)stopDisplayLink { + [_displayLink invalidate]; + _displayLink = nil; +} + +- (void)render { + +} + +- (void)dealloc { + +} + +@end diff --git a/ios/include/MaterialKey.h b/ios/include/MaterialKey.h new file mode 100644 index 00000000..10406c9d --- /dev/null +++ b/ios/include/MaterialKey.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#define JAVA_MATERIAL_KEY "com/google/android/filament/gltfio/MaterialProvider$MaterialKey" + +class MaterialKeyHelper { +public: + static MaterialKeyHelper& get(); + + void copy(JNIEnv* env, gltfio::MaterialKey& dst, jobject src); + void copy(JNIEnv* env, jobject dst, const gltfio::MaterialKey& src); + + void init(JNIEnv* env); // called only from the Java static class constructor + +private: + jfieldID doubleSided; + jfieldID unlit; + jfieldID hasVertexColors; + jfieldID hasBaseColorTexture; + jfieldID hasNormalTexture; + jfieldID hasOcclusionTexture; + jfieldID hasEmissiveTexture; + jfieldID useSpecularGlossiness; + jfieldID alphaMode; + jfieldID enableDiagnostics; + jfieldID hasMetallicRoughnessTexture; + jfieldID metallicRoughnessUV; + jfieldID baseColorUV; + jfieldID hasClearCoatTexture; + jfieldID clearCoatUV; + jfieldID hasClearCoatRoughnessTexture; + jfieldID clearCoatRoughnessUV; + jfieldID hasClearCoatNormalTexture; + jfieldID clearCoatNormalUV; + jfieldID hasClearCoat; + jfieldID hasTransmission; + jfieldID hasTextureTransforms; + jfieldID emissiveUV; + jfieldID aoUV; + jfieldID normalUV; + jfieldID hasTransmissionTexture; + jfieldID transmissionUV; + jfieldID hasSheenColorTexture; + jfieldID sheenColorUV; + jfieldID hasSheenRoughnessTexture; + jfieldID sheenRoughnessUV; + jfieldID hasVolumeThicknessTexture; + jfieldID volumeThicknessUV; + jfieldID hasSheen; + jfieldID hasIOR; +}; diff --git a/ios/include/backend/BufferDescriptor.h b/ios/include/backend/BufferDescriptor.h new file mode 100644 index 00000000..f6c7c69b --- /dev/null +++ b/ios/include/backend/BufferDescriptor.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! \file + +#ifndef TNT_FILAMENT_DRIVER_BUFFERDESCRIPTOR_H +#define TNT_FILAMENT_DRIVER_BUFFERDESCRIPTOR_H + +#include + +#include +#include + +namespace filament { +namespace backend { + +/** + * A CPU memory-buffer descriptor, typically used to transfer data from the CPU to the GPU. + * + * A BufferDescriptor owns the memory buffer it references, therefore BufferDescriptor cannot + * be copied, but can be moved. + * + * BufferDescriptor releases ownership of the memory-buffer when it's destroyed. + */ +class UTILS_PUBLIC BufferDescriptor { +public: + /** + * Callback used to destroy the buffer data. + * Guarantees: + * Called on the main filament thread. + * + * Limitations: + * Must be lightweight. + * Must not call filament APIs. + */ + using Callback = void(*)(void* buffer, size_t size, void* user); + + //! creates an empty descriptor + BufferDescriptor() noexcept = default; + + //! calls the callback to advertise BufferDescriptor no-longer owns the buffer + ~BufferDescriptor() noexcept { + if (callback) { + callback(buffer, size, user); + } + } + + BufferDescriptor(const BufferDescriptor& rhs) = delete; + BufferDescriptor& operator=(const BufferDescriptor& rhs) = delete; + + BufferDescriptor(BufferDescriptor&& rhs) noexcept + : buffer(rhs.buffer), size(rhs.size), callback(rhs.callback), user(rhs.user) { + rhs.buffer = nullptr; + rhs.callback = nullptr; + } + + BufferDescriptor& operator=(BufferDescriptor&& rhs) noexcept { + if (this != &rhs) { + buffer = rhs.buffer; + size = rhs.size; + callback = rhs.callback; + user = rhs.user; + rhs.buffer = nullptr; + rhs.callback = nullptr; + } + return *this; + } + + /** + * Creates a BufferDescriptor that references a CPU memory-buffer + * @param buffer Memory address of the CPU buffer to reference + * @param size Size of the CPU buffer in bytes + * @param callback A callback used to release the CPU buffer from this BufferDescriptor + * @param user An opaque user pointer passed to the callback function when it's called + */ + BufferDescriptor(void const* buffer, size_t size, + Callback callback = nullptr, void* user = nullptr) noexcept + : buffer(const_cast(buffer)), size(size), callback(callback), user(user) { + } + + /** + * Set or replace the release callback function + * @param callback The new callback function + * @param user An opaque user pointer passed to the callbeck function when it's called + */ + void setCallback(Callback callback, void* user = nullptr) noexcept { + this->callback = callback; + this->user = user; + } + + //! Returns whether a release callback is set + bool hasCallback() const noexcept { return callback != nullptr; } + + //! Returns the currently set release callback function + Callback getCallback() const noexcept { + return callback; + } + + //! Returns the user opaque pointer associated to this BufferDescriptor + void* getUser() const noexcept { + return user; + } + + //! CPU mempry-buffer virtual address + void* buffer = nullptr; + + //! CPU memory-buffer size in bytes + size_t size = 0; + +private: + // callback when the buffer is consumed. + Callback callback = nullptr; + void* user = nullptr; +}; + +} // namespace backend +} // namespace filament + +#endif // TNT_FILAMENT_DRIVER_BUFFERDESCRIPTOR_H diff --git a/ios/include/backend/DriverEnums.h b/ios/include/backend/DriverEnums.h new file mode 100644 index 00000000..0be3b219 --- /dev/null +++ b/ios/include/backend/DriverEnums.h @@ -0,0 +1,929 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! \file + +#ifndef TNT_FILAMENT_DRIVER_DRIVERENUMS_H +#define TNT_FILAMENT_DRIVER_DRIVERENUMS_H + +#include +#include // Because we define ERROR in the FenceStatus enum. + +#include + +#include + +#include // FIXME: STL headers are not allowed in public headers + +#include +#include + +namespace filament { +/** + * Types and enums used by filament's driver. + * + * Effectively these types are public but should not be used directly. Instead use public classes + * internal redeclaration of these types. + * For e.g. Use Texture::Sampler instead of filament::SamplerType. + */ +namespace backend { + +static constexpr uint64_t SWAP_CHAIN_CONFIG_TRANSPARENT = 0x1; +static constexpr uint64_t SWAP_CHAIN_CONFIG_READABLE = 0x2; +static constexpr uint64_t SWAP_CHAIN_CONFIG_ENABLE_XCB = 0x4; +static constexpr uint64_t SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER = 0x8; + +static constexpr size_t MAX_VERTEX_ATTRIBUTE_COUNT = 16; // This is guaranteed by OpenGL ES. +static constexpr size_t MAX_SAMPLER_COUNT = 16; // Matches the Adreno Vulkan driver. +static constexpr size_t MAX_VERTEX_BUFFER_COUNT = 16; // Max number of bound buffer objects. + +static_assert(MAX_VERTEX_BUFFER_COUNT <= MAX_VERTEX_ATTRIBUTE_COUNT, + "The number of buffer objects that can be attached to a VertexBuffer must be " + "less than or equal to the maximum number of vertex attributes."); + +static constexpr size_t CONFIG_BINDING_COUNT = 8; + +/** + * Selects which driver a particular Engine should use. + */ +enum class Backend : uint8_t { + DEFAULT = 0, //!< Automatically selects an appropriate driver for the platform. + OPENGL = 1, //!< Selects the OpenGL/ES driver (default on Android) + VULKAN = 2, //!< Selects the Vulkan driver if the platform supports it (default on Linux/Windows) + METAL = 3, //!< Selects the Metal driver if the platform supports it (default on MacOS/iOS). + NOOP = 4, //!< Selects the no-op driver for testing purposes. +}; + +static constexpr const char* backendToString(Backend backend) { + switch (backend) { + case Backend::NOOP: + return "Noop"; + case Backend::OPENGL: + return "OpenGL"; + case Backend::VULKAN: + return "Vulkan"; + case Backend::METAL: + return "Metal"; + default: + return "Unknown"; + } +} + +/** + * Bitmask for selecting render buffers + */ +enum class TargetBufferFlags : uint32_t { + NONE = 0x0u, //!< No buffer selected. + COLOR0 = 0x00000001u, //!< Color buffer selected. + COLOR1 = 0x00000002u, //!< Color buffer selected. + COLOR2 = 0x00000004u, //!< Color buffer selected. + COLOR3 = 0x00000008u, //!< Color buffer selected. + COLOR4 = 0x00000010u, //!< Color buffer selected. + COLOR5 = 0x00000020u, //!< Color buffer selected. + COLOR6 = 0x00000040u, //!< Color buffer selected. + COLOR7 = 0x00000080u, //!< Color buffer selected. + + COLOR = COLOR0, //!< \deprecated + COLOR_ALL = COLOR0 | COLOR1 | COLOR2 | COLOR3 | COLOR4 | COLOR5 | COLOR6 | COLOR7, + DEPTH = 0x10000000u, //!< Depth buffer selected. + STENCIL = 0x20000000u, //!< Stencil buffer selected. + DEPTH_AND_STENCIL = DEPTH | STENCIL, //!< depth and stencil buffer selected. + ALL = COLOR_ALL | DEPTH | STENCIL //!< Color, depth and stencil buffer selected. +}; + +inline constexpr TargetBufferFlags getTargetBufferFlagsAt(size_t index) noexcept { + if (index == 0u) return TargetBufferFlags::COLOR0; + if (index == 1u) return TargetBufferFlags::COLOR1; + if (index == 2u) return TargetBufferFlags::COLOR2; + if (index == 3u) return TargetBufferFlags::COLOR3; + if (index == 4u) return TargetBufferFlags::COLOR4; + if (index == 5u) return TargetBufferFlags::COLOR5; + if (index == 6u) return TargetBufferFlags::COLOR6; + if (index == 7u) return TargetBufferFlags::COLOR7; + if (index == 8u) return TargetBufferFlags::DEPTH; + if (index == 9u) return TargetBufferFlags::STENCIL; + return TargetBufferFlags::NONE; +} + +/** + * Frequency at which a buffer is expected to be modified and used. This is used as an hint + * for the driver to make better decisions about managing memory internally. + */ +enum class BufferUsage : uint8_t { + STATIC, //!< content modified once, used many times + DYNAMIC, //!< content modified frequently, used many times + STREAM, //!< content invalidated and modified frequently, used many times +}; + +/** + * Defines a viewport, which is the origin and extent of the clip-space. + * All drawing is clipped to the viewport. + */ +struct Viewport { + int32_t left; //!< left coordinate in window space. + int32_t bottom; //!< bottom coordinate in window space. + uint32_t width; //!< width in pixels + uint32_t height; //!< height in pixels + //! get the right coordinate in window space of the viewport + int32_t right() const noexcept { return left + width; } + //! get the top coordinate in window space of the viewport + int32_t top() const noexcept { return bottom + height; } +}; + +/** + * Specifies the mapping of the near and far clipping plane to window coordinates. + */ +struct DepthRange { + float near = 0.0f; //!< mapping of the near plane to window coordinates. + float far = 1.0f; //!< mapping of the far plane to window coordinates. +}; + +/** + * Error codes for Fence::wait() + * @see Fence, Fence::wait() + */ +enum class FenceStatus : int8_t { + ERROR = -1, //!< An error occured. The Fence condition is not satisfied. + CONDITION_SATISFIED = 0, //!< The Fence condition is satisfied. + TIMEOUT_EXPIRED = 1, //!< wait()'s timeout expired. The Fence condition is not satisfied. +}; + +/** + * Status codes for sync objects + */ +enum class SyncStatus : int8_t { + ERROR = -1, //!< An error occured. The Sync is not signaled. + SIGNALED = 0, //!< The Sync is signaled. + NOT_SIGNALED = 1, //!< The Sync is not signaled yet +}; + +static constexpr uint64_t FENCE_WAIT_FOR_EVER = uint64_t(-1); + +/** + * Shader model. + * + * These enumerants are used across all backends and refer to a level of functionality, rather + * than to an OpenGL specific shader model. + */ +enum class ShaderModel : uint8_t { + //! For testing + UNKNOWN = 0, + GL_ES_30 = 1, //!< Mobile level functionality + GL_CORE_41 = 2, //!< Desktop level functionality +}; +static constexpr size_t SHADER_MODEL_COUNT = 3; + +/** + * Primitive types + */ +enum class PrimitiveType : uint8_t { + // don't change the enums values (made to match GL) + POINTS = 0, //!< points + LINES = 1, //!< lines + TRIANGLES = 4, //!< triangles + NONE = 0xFF +}; + +/** + * Supported uniform types + */ +enum class UniformType : uint8_t { + BOOL, + BOOL2, + BOOL3, + BOOL4, + FLOAT, + FLOAT2, + FLOAT3, + FLOAT4, + INT, + INT2, + INT3, + INT4, + UINT, + UINT2, + UINT3, + UINT4, + MAT3, //!< a 3x3 float matrix + MAT4 //!< a 4x4 float matrix +}; + +enum class Precision : uint8_t { + LOW, + MEDIUM, + HIGH, + DEFAULT +}; + +//! Texture sampler type +enum class SamplerType : uint8_t { + SAMPLER_2D, //!< 2D texture + SAMPLER_2D_ARRAY, //!< 2D array texture + SAMPLER_CUBEMAP, //!< Cube map texture + SAMPLER_EXTERNAL, //!< External texture + SAMPLER_3D, //!< 3D texture +}; + +//! Subpass type +enum class SubpassType : uint8_t { + SUBPASS_INPUT +}; + +//! Texture sampler format +enum class SamplerFormat : uint8_t { + INT = 0, //!< signed integer sampler + UINT = 1, //!< unsigned integer sampler + FLOAT = 2, //!< float sampler + SHADOW = 3 //!< shadow sampler (PCF) +}; + +/** + * Supported element types + */ +enum class ElementType : uint8_t { + BYTE, + BYTE2, + BYTE3, + BYTE4, + UBYTE, + UBYTE2, + UBYTE3, + UBYTE4, + SHORT, + SHORT2, + SHORT3, + SHORT4, + USHORT, + USHORT2, + USHORT3, + USHORT4, + INT, + UINT, + FLOAT, + FLOAT2, + FLOAT3, + FLOAT4, + HALF, + HALF2, + HALF3, + HALF4, +}; + +//! Buffer object binding type +enum class BufferObjectBinding : uint8_t { + VERTEX, + UNIFORM +}; + +//! Face culling Mode +enum class CullingMode : uint8_t { + NONE, //!< No culling, front and back faces are visible + FRONT, //!< Front face culling, only back faces are visible + BACK, //!< Back face culling, only front faces are visible + FRONT_AND_BACK //!< Front and Back, geometry is not visible +}; + +//! Pixel Data Format +enum class PixelDataFormat : uint8_t { + R, //!< One Red channel, float + R_INTEGER, //!< One Red channel, integer + RG, //!< Two Red and Green channels, float + RG_INTEGER, //!< Two Red and Green channels, integer + RGB, //!< Three Red, Green and Blue channels, float + RGB_INTEGER, //!< Three Red, Green and Blue channels, integer + RGBA, //!< Four Red, Green, Blue and Alpha channels, float + RGBA_INTEGER, //!< Four Red, Green, Blue and Alpha channels, integer + UNUSED, // used to be rgbm + DEPTH_COMPONENT, //!< Depth, 16-bit or 24-bits usually + DEPTH_STENCIL, //!< Two Depth (24-bits) + Stencil (8-bits) channels + ALPHA //! One Alpha channel, float +}; + +//! Pixel Data Type +enum class PixelDataType : uint8_t { + UBYTE, //!< unsigned byte + BYTE, //!< signed byte + USHORT, //!< unsigned short (16-bit) + SHORT, //!< signed short (16-bit) + UINT, //!< unsigned int (16-bit) + INT, //!< signed int (32-bit) + HALF, //!< half-float (16-bit float) + FLOAT, //!< float (32-bits float) + COMPRESSED, //!< compressed pixels, @see CompressedPixelDataType + UINT_10F_11F_11F_REV, //!< three low precision floating-point numbers + USHORT_565, //!< unsigned int (16-bit), encodes 3 RGB channels + UINT_2_10_10_10_REV, //!< unsigned normalized 10 bits RGB, 2 bits alpha +}; + +//! Compressed pixel data types +enum class CompressedPixelDataType : uint16_t { + // Mandatory in GLES 3.0 and GL 4.3 + EAC_R11, EAC_R11_SIGNED, EAC_RG11, EAC_RG11_SIGNED, + ETC2_RGB8, ETC2_SRGB8, + ETC2_RGB8_A1, ETC2_SRGB8_A1, + ETC2_EAC_RGBA8, ETC2_EAC_SRGBA8, + + // Available everywhere except Android/iOS + DXT1_RGB, DXT1_RGBA, DXT3_RGBA, DXT5_RGBA, + DXT1_SRGB, DXT1_SRGBA, DXT3_SRGBA, DXT5_SRGBA, + + // ASTC formats are available with a GLES extension + RGBA_ASTC_4x4, + RGBA_ASTC_5x4, + RGBA_ASTC_5x5, + RGBA_ASTC_6x5, + RGBA_ASTC_6x6, + RGBA_ASTC_8x5, + RGBA_ASTC_8x6, + RGBA_ASTC_8x8, + RGBA_ASTC_10x5, + RGBA_ASTC_10x6, + RGBA_ASTC_10x8, + RGBA_ASTC_10x10, + RGBA_ASTC_12x10, + RGBA_ASTC_12x12, + SRGB8_ALPHA8_ASTC_4x4, + SRGB8_ALPHA8_ASTC_5x4, + SRGB8_ALPHA8_ASTC_5x5, + SRGB8_ALPHA8_ASTC_6x5, + SRGB8_ALPHA8_ASTC_6x6, + SRGB8_ALPHA8_ASTC_8x5, + SRGB8_ALPHA8_ASTC_8x6, + SRGB8_ALPHA8_ASTC_8x8, + SRGB8_ALPHA8_ASTC_10x5, + SRGB8_ALPHA8_ASTC_10x6, + SRGB8_ALPHA8_ASTC_10x8, + SRGB8_ALPHA8_ASTC_10x10, + SRGB8_ALPHA8_ASTC_12x10, + SRGB8_ALPHA8_ASTC_12x12, +}; + +/** Supported texel formats + * These formats are typically used to specify a texture's internal storage format. + * + * Enumerants syntax format + * ======================== + * + * `[components][size][type]` + * + * `components` : List of stored components by this format.\n + * `size` : Size in bit of each component.\n + * `type` : Type this format is stored as.\n + * + * + * Name | Component + * :--------|:------------------------------- + * R | Linear Red + * RG | Linear Red, Green + * RGB | Linear Red, Green, Blue + * RGBA | Linear Red, Green Blue, Alpha + * SRGB | sRGB encoded Red, Green, Blue + * DEPTH | Depth + * STENCIL | Stencil + * + * \n + * Name | Type + * :--------|:--------------------------------------------------- + * (none) | Unsigned Normalized Integer [0, 1] + * _SNORM | Signed Normalized Integer [-1, 1] + * UI | Unsigned Integer @f$ [0, 2^{size}] @f$ + * I | Signed Integer @f$ [-2^{size-1}, 2^{size-1}-1] @f$ + * F | Floating-point + * + * + * Special color formats + * --------------------- + * + * There are a few special color formats that don't follow the convention above: + * + * Name | Format + * :----------------|:-------------------------------------------------------------------------- + * RGB565 | 5-bits for R and B, 6-bits for G. + * RGB5_A1 | 5-bits for R, G and B, 1-bit for A. + * RGB10_A2 | 10-bits for R, G and B, 2-bits for A. + * RGB9_E5 | **Unsigned** floating point. 9-bits mantissa for RGB, 5-bits shared exponent + * R11F_G11F_B10F | **Unsigned** floating point. 6-bits mantissa, for R and G, 5-bits for B. 5-bits exponent. + * SRGB8_A8 | sRGB 8-bits with linear 8-bits alpha. + * DEPTH24_STENCIL8 | 24-bits unsigned normalized integer depth, 8-bits stencil. + * DEPTH32F_STENCIL8| 32-bits floating-point depth, 8-bits stencil. + * + * + * Compressed texture formats + * -------------------------- + * + * Many compressed texture formats are supported as well, which include (but are not limited to) + * the following list: + * + * Name | Format + * :----------------|:-------------------------------------------------------------------------- + * EAC_R11 | Compresses R11UI + * EAC_R11_SIGNED | Compresses R11I + * EAC_RG11 | Compresses RG11UI + * EAC_RG11_SIGNED | Compresses RG11I + * ETC2_RGB8 | Compresses RGB8 + * ETC2_SRGB8 | compresses SRGB8 + * ETC2_EAC_RGBA8 | Compresses RGBA8 + * ETC2_EAC_SRGBA8 | Compresses SRGB8_A8 + * ETC2_RGB8_A1 | Compresses RGB8 with 1-bit alpha + * ETC2_SRGB8_A1 | Compresses sRGB8 with 1-bit alpha + * + * + * @see Texture + */ +enum class TextureFormat : uint16_t { + // 8-bits per element + R8, R8_SNORM, R8UI, R8I, STENCIL8, + + // 16-bits per element + R16F, R16UI, R16I, + RG8, RG8_SNORM, RG8UI, RG8I, + RGB565, + RGB9_E5, // 9995 is actually 32 bpp but it's here for historical reasons. + RGB5_A1, + RGBA4, + DEPTH16, + + // 24-bits per element + RGB8, SRGB8, RGB8_SNORM, RGB8UI, RGB8I, + DEPTH24, + + // 32-bits per element + R32F, R32UI, R32I, + RG16F, RG16UI, RG16I, + R11F_G11F_B10F, + RGBA8, SRGB8_A8,RGBA8_SNORM, + UNUSED, // used to be rgbm + RGB10_A2, RGBA8UI, RGBA8I, + DEPTH32F, DEPTH24_STENCIL8, DEPTH32F_STENCIL8, + + // 48-bits per element + RGB16F, RGB16UI, RGB16I, + + // 64-bits per element + RG32F, RG32UI, RG32I, + RGBA16F, RGBA16UI, RGBA16I, + + // 96-bits per element + RGB32F, RGB32UI, RGB32I, + + // 128-bits per element + RGBA32F, RGBA32UI, RGBA32I, + + // compressed formats + + // Mandatory in GLES 3.0 and GL 4.3 + EAC_R11, EAC_R11_SIGNED, EAC_RG11, EAC_RG11_SIGNED, + ETC2_RGB8, ETC2_SRGB8, + ETC2_RGB8_A1, ETC2_SRGB8_A1, + ETC2_EAC_RGBA8, ETC2_EAC_SRGBA8, + + // Available everywhere except Android/iOS + DXT1_RGB, DXT1_RGBA, DXT3_RGBA, DXT5_RGBA, + DXT1_SRGB, DXT1_SRGBA, DXT3_SRGBA, DXT5_SRGBA, + + // ASTC formats are available with a GLES extension + RGBA_ASTC_4x4, + RGBA_ASTC_5x4, + RGBA_ASTC_5x5, + RGBA_ASTC_6x5, + RGBA_ASTC_6x6, + RGBA_ASTC_8x5, + RGBA_ASTC_8x6, + RGBA_ASTC_8x8, + RGBA_ASTC_10x5, + RGBA_ASTC_10x6, + RGBA_ASTC_10x8, + RGBA_ASTC_10x10, + RGBA_ASTC_12x10, + RGBA_ASTC_12x12, + SRGB8_ALPHA8_ASTC_4x4, + SRGB8_ALPHA8_ASTC_5x4, + SRGB8_ALPHA8_ASTC_5x5, + SRGB8_ALPHA8_ASTC_6x5, + SRGB8_ALPHA8_ASTC_6x6, + SRGB8_ALPHA8_ASTC_8x5, + SRGB8_ALPHA8_ASTC_8x6, + SRGB8_ALPHA8_ASTC_8x8, + SRGB8_ALPHA8_ASTC_10x5, + SRGB8_ALPHA8_ASTC_10x6, + SRGB8_ALPHA8_ASTC_10x8, + SRGB8_ALPHA8_ASTC_10x10, + SRGB8_ALPHA8_ASTC_12x10, + SRGB8_ALPHA8_ASTC_12x12, +}; + +//! Bitmask describing the intended Texture Usage +enum class TextureUsage : uint8_t { + NONE = 0x0, + COLOR_ATTACHMENT = 0x1, //!< Texture can be used as a color attachment + DEPTH_ATTACHMENT = 0x2, //!< Texture can be used as a depth attachment + STENCIL_ATTACHMENT = 0x4, //!< Texture can be used as a stencil attachment + UPLOADABLE = 0x8, //!< Data can be uploaded into this texture (default) + SAMPLEABLE = 0x10, //!< Texture can be sampled (default) + SUBPASS_INPUT = 0x20, //!< Texture can be used as a subpass input + DEFAULT = UPLOADABLE | SAMPLEABLE //!< Default texture usage +}; + +//! Texture swizzle +enum class TextureSwizzle : uint8_t { + SUBSTITUTE_ZERO, + SUBSTITUTE_ONE, + CHANNEL_0, + CHANNEL_1, + CHANNEL_2, + CHANNEL_3 +}; + +//! returns whether this format a depth format +static constexpr bool isDepthFormat(TextureFormat format) noexcept { + switch (format) { + case TextureFormat::DEPTH32F: + case TextureFormat::DEPTH24: + case TextureFormat::DEPTH16: + case TextureFormat::DEPTH32F_STENCIL8: + case TextureFormat::DEPTH24_STENCIL8: + return true; + default: + return false; + } +} + +//! returns whether this format a compressed format +static constexpr bool isCompressedFormat(TextureFormat format) noexcept { + return format >= TextureFormat::EAC_R11; +} + +//! returns whether this format is an ETC2 compressed format +static constexpr bool isETC2Compression(TextureFormat format) noexcept { + return format >= TextureFormat::EAC_R11 && format <= TextureFormat::ETC2_EAC_SRGBA8; +} + +//! returns whether this format is an ETC3 compressed format +static constexpr bool isS3TCCompression(TextureFormat format) noexcept { + return format >= TextureFormat::DXT1_RGB && format <= TextureFormat::DXT5_SRGBA; +} + +static constexpr bool isS3TCSRGBCompression(TextureFormat format) noexcept { + return format >= TextureFormat::DXT1_SRGB && format <= TextureFormat::DXT5_SRGBA; +} + +//! Texture Cubemap Face +enum class TextureCubemapFace : uint8_t { + // don't change the enums values + POSITIVE_X = 0, //!< +x face + NEGATIVE_X = 1, //!< -x face + POSITIVE_Y = 2, //!< +y face + NEGATIVE_Y = 3, //!< -y face + POSITIVE_Z = 4, //!< +z face + NEGATIVE_Z = 5, //!< -z face +}; + +//! Face offsets for all faces of a cubemap +struct FaceOffsets { + using size_type = size_t; + union { + struct { + size_type px; //!< +x face offset in bytes + size_type nx; //!< -x face offset in bytes + size_type py; //!< +y face offset in bytes + size_type ny; //!< -y face offset in bytes + size_type pz; //!< +z face offset in bytes + size_type nz; //!< -z face offset in bytes + }; + size_type offsets[6]; + }; + size_type operator[](size_t n) const noexcept { return offsets[n]; } + size_type& operator[](size_t n) { return offsets[n]; } + FaceOffsets() noexcept = default; + explicit FaceOffsets(size_type faceSize) noexcept { + px = faceSize * 0; + nx = faceSize * 1; + py = faceSize * 2; + ny = faceSize * 3; + pz = faceSize * 4; + nz = faceSize * 5; + } + FaceOffsets(const FaceOffsets& rhs) noexcept { + px = rhs.px; + nx = rhs.nx; + py = rhs.py; + ny = rhs.ny; + pz = rhs.pz; + nz = rhs.nz; + } + FaceOffsets& operator=(const FaceOffsets& rhs) noexcept { + px = rhs.px; + nx = rhs.nx; + py = rhs.py; + ny = rhs.ny; + pz = rhs.pz; + nz = rhs.nz; + return *this; + } +}; + +//! Sampler Wrap mode +enum class SamplerWrapMode : uint8_t { + CLAMP_TO_EDGE, //!< clamp-to-edge. The edge of the texture extends to infinity. + REPEAT, //!< repeat. The texture infinitely repeats in the wrap direction. + MIRRORED_REPEAT, //!< mirrored-repeat. The texture infinitely repeats and mirrors in the wrap direction. +}; + +//! Sampler minification filter +enum class SamplerMinFilter : uint8_t { + // don't change the enums values + NEAREST = 0, //!< No filtering. Nearest neighbor is used. + LINEAR = 1, //!< Box filtering. Weighted average of 4 neighbors is used. + NEAREST_MIPMAP_NEAREST = 2, //!< Mip-mapping is activated. But no filtering occurs. + LINEAR_MIPMAP_NEAREST = 3, //!< Box filtering within a mip-map level. + NEAREST_MIPMAP_LINEAR = 4, //!< Mip-map levels are interpolated, but no other filtering occurs. + LINEAR_MIPMAP_LINEAR = 5 //!< Both interpolated Mip-mapping and linear filtering are used. +}; + +//! Sampler magnification filter +enum class SamplerMagFilter : uint8_t { + // don't change the enums values + NEAREST = 0, //!< No filtering. Nearest neighbor is used. + LINEAR = 1, //!< Box filtering. Weighted average of 4 neighbors is used. +}; + +//! Sampler compare mode +enum class SamplerCompareMode : uint8_t { + // don't change the enums values + NONE = 0, + COMPARE_TO_TEXTURE = 1 +}; + +//! comparison function for the depth sampler +enum class SamplerCompareFunc : uint8_t { + // don't change the enums values + LE = 0, //!< Less or equal + GE, //!< Greater or equal + L, //!< Strictly less than + G, //!< Strictly greater than + E, //!< Equal + NE, //!< Not equal + A, //!< Always. Depth testing is deactivated. + N //!< Never. The depth test always fails. +}; + +//! Sampler paramters +struct SamplerParams { // NOLINT + union { + struct { + SamplerMagFilter filterMag : 1; //!< magnification filter (NEAREST) + SamplerMinFilter filterMin : 3; //!< minification filter (NEAREST) + SamplerWrapMode wrapS : 2; //!< s-coordinate wrap mode (CLAMP_TO_EDGE) + SamplerWrapMode wrapT : 2; //!< t-coordinate wrap mode (CLAMP_TO_EDGE) + + SamplerWrapMode wrapR : 2; //!< r-coordinate wrap mode (CLAMP_TO_EDGE) + uint8_t anisotropyLog2 : 3; //!< anisotropy level (0) + SamplerCompareMode compareMode : 1; //!< sampler compare mode (NONE) + uint8_t padding0 : 2; //!< reserved. must be 0. + + SamplerCompareFunc compareFunc : 3; //!< sampler comparison function (LE) + uint8_t padding1 : 5; //!< reserved. must be 0. + + uint8_t padding2 : 8; //!< reserved. must be 0. + }; + uint32_t u; + }; +private: + friend inline bool operator < (SamplerParams lhs, SamplerParams rhs) { + return lhs.u < rhs.u; + } +}; + +static_assert(sizeof(SamplerParams) == sizeof(uint32_t), "SamplerParams must be 32 bits"); + +//! blending equation function +enum class BlendEquation : uint8_t { + ADD, //!< the fragment is added to the color buffer + SUBTRACT, //!< the fragment is subtracted from the color buffer + REVERSE_SUBTRACT, //!< the color buffer is subtracted from the fragment + MIN, //!< the min between the fragment and color buffer + MAX //!< the max between the fragment and color buffer +}; + +//! blending function +enum class BlendFunction : uint8_t { + ZERO, //!< f(src, dst) = 0 + ONE, //!< f(src, dst) = 1 + SRC_COLOR, //!< f(src, dst) = src + ONE_MINUS_SRC_COLOR, //!< f(src, dst) = 1-src + DST_COLOR, //!< f(src, dst) = dst + ONE_MINUS_DST_COLOR, //!< f(src, dst) = 1-dst + SRC_ALPHA, //!< f(src, dst) = src.a + ONE_MINUS_SRC_ALPHA, //!< f(src, dst) = 1-src.a + DST_ALPHA, //!< f(src, dst) = dst.a + ONE_MINUS_DST_ALPHA, //!< f(src, dst) = 1-dst.a + SRC_ALPHA_SATURATE //!< f(src, dst) = (1,1,1) * min(src.a, 1 - dst.a), 1 +}; + +//! Stream for external textures +enum class StreamType { + NATIVE, //!< Not synchronized but copy-free. Good for video. + TEXTURE_ID, //!< Synchronized, but GL-only and incurs copies. Good for AR on devices before API 26. + ACQUIRED, //!< Synchronized, copy-free, and take a release callback. Good for AR but requires API 26+. +}; + +//! Releases an ACQUIRED external texture, guaranteed to be called on the application thread. +using StreamCallback = void(*)(void* image, void* user); + +//! Vertex attribute descriptor +struct Attribute { + //! attribute is normalized (remapped between 0 and 1) + static constexpr uint8_t FLAG_NORMALIZED = 0x1; + //! attribute is an integer + static constexpr uint8_t FLAG_INTEGER_TARGET = 0x2; + static constexpr uint8_t BUFFER_UNUSED = 0xFF; + uint32_t offset = 0; //!< attribute offset in bytes + uint8_t stride = 0; //!< attribute stride in bytes + uint8_t buffer = BUFFER_UNUSED; //!< attribute buffer index + ElementType type = ElementType::BYTE; //!< attribute element type + uint8_t flags = 0x0; //!< attribute flags +}; + +using AttributeArray = std::array; + +//! Raster state descriptor +struct RasterState { + + using CullingMode = CullingMode; + using DepthFunc = SamplerCompareFunc; + using BlendEquation = BlendEquation; + using BlendFunction = BlendFunction; + + RasterState() noexcept { // NOLINT + static_assert(sizeof(RasterState) == sizeof(uint32_t), + "RasterState size not what was intended"); + culling = CullingMode::BACK; + blendEquationRGB = BlendEquation::ADD; + blendEquationAlpha = BlendEquation::ADD; + blendFunctionSrcRGB = BlendFunction::ONE; + blendFunctionSrcAlpha = BlendFunction::ONE; + blendFunctionDstRGB = BlendFunction::ZERO; + blendFunctionDstAlpha = BlendFunction::ZERO; + } + + bool operator == (RasterState rhs) const noexcept { return u == rhs.u; } + bool operator != (RasterState rhs) const noexcept { return u != rhs.u; } + + void disableBlending() noexcept { + blendEquationRGB = BlendEquation::ADD; + blendEquationAlpha = BlendEquation::ADD; + blendFunctionSrcRGB = BlendFunction::ONE; + blendFunctionSrcAlpha = BlendFunction::ONE; + blendFunctionDstRGB = BlendFunction::ZERO; + blendFunctionDstAlpha = BlendFunction::ZERO; + } + + // note: clang reduces this entire function to a simple load/mask/compare + bool hasBlending() const noexcept { + // This is used to decide if blending needs to be enabled in the h/w + return !(blendEquationRGB == BlendEquation::ADD && + blendEquationAlpha == BlendEquation::ADD && + blendFunctionSrcRGB == BlendFunction::ONE && + blendFunctionSrcAlpha == BlendFunction::ONE && + blendFunctionDstRGB == BlendFunction::ZERO && + blendFunctionDstAlpha == BlendFunction::ZERO); + } + + union { + struct { + //! culling mode + CullingMode culling : 2; // 2 + + //! blend equation for the red, green and blue components + BlendEquation blendEquationRGB : 3; // 5 + //! blend equation for the alpha component + BlendEquation blendEquationAlpha : 3; // 8 + + //! blending function for the source color + BlendFunction blendFunctionSrcRGB : 4; // 12 + //! blending function for the source alpha + BlendFunction blendFunctionSrcAlpha : 4; // 16 + //! blending function for the destination color + BlendFunction blendFunctionDstRGB : 4; // 20 + //! blending function for the destination alpha + BlendFunction blendFunctionDstAlpha : 4; // 24 + + //! Whether depth-buffer writes are enabled + bool depthWrite : 1; // 25 + //! Depth test function + DepthFunc depthFunc : 3; // 28 + + //! Whether color-buffer writes are enabled + bool colorWrite : 1; // 29 + + //! use alpha-channel as coverage mask for anti-aliasing + bool alphaToCoverage : 1; // 30 + + //! whether front face winding direction must be inverted + bool inverseFrontFaces : 1; // 31 + + //! padding, must be 0 + uint8_t padding : 1; // 32 + }; + uint32_t u = 0; + }; +}; + +/** + ********************************************************************************************** + * \privatesection + */ + +enum ShaderType : uint8_t { + VERTEX = 0, + FRAGMENT = 1 +}; +static constexpr size_t PIPELINE_STAGE_COUNT = 2; + +/** + * Selects which buffers to clear at the beginning of the render pass, as well as which buffers + * can be discarded at the beginning and end of the render pass. + * + */ +struct RenderPassFlags { + /** + * bitmask indicating which buffers to clear at the beginning of a render pass. + * This implies discard. + */ + TargetBufferFlags clear; + + /** + * bitmask indicating which buffers to discard at the beginning of a render pass. + * Discarded buffers have uninitialized content, they must be entirely drawn over or cleared. + */ + TargetBufferFlags discardStart; + + /** + * bitmask indicating which buffers to discard at the end of a render pass. + * Discarded buffers' content becomes invalid, they must not be read from again. + */ + TargetBufferFlags discardEnd; +}; + +/** + * Parameters of a render pass. + */ +struct RenderPassParams { + RenderPassFlags flags{}; //!< operations performed on the buffers for this pass + + Viewport viewport{}; //!< viewport for this pass + DepthRange depthRange{}; //!< depth range for this pass + + //! Color to use to clear the COLOR buffer. RenderPassFlags::clear must be set. + math::float4 clearColor = {}; + + //! Depth value to clear the depth buffer with + double clearDepth = 0.0; + + //! Stencil value to clear the stencil buffer with + uint32_t clearStencil = 0; + + /** + * The subpass mask specifies which color attachments are designated for read-back in the second + * subpass. If this is zero, the render pass has only one subpass. The least significant bit + * specifies that the first color attachment in the render target is a subpass input. + * + * For now only 2 subpasses are supported, so only the lower 4 bits are used, one for each color + * attachment (see MRT::TARGET_COUNT). + */ + uint32_t subpassMask = 0; +}; + +struct PolygonOffset { + float slope = 0; // factor in GL-speak + float constant = 0; // units in GL-speak +}; + + +using FrameScheduledCallback = void(*)(PresentCallable callable, void* user); + +using FrameCompletedCallback = void(*)(void* user); + + +} // namespace backend +} // namespace filament + +template<> struct utils::EnableBitMaskOperators + : public std::true_type {}; +template<> struct utils::EnableBitMaskOperators + : public std::true_type {}; + +#endif // TNT_FILAMENT_DRIVER_DRIVERENUMS_H diff --git a/ios/include/backend/Handle.h b/ios/include/backend/Handle.h new file mode 100644 index 00000000..e7737ed5 --- /dev/null +++ b/ios/include/backend/Handle.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_DRIVER_HANDLE_H +#define TNT_FILAMENT_DRIVER_HANDLE_H + +#include +#include +#include + +namespace filament { +namespace backend { + +struct HwBufferObject; +struct HwFence; +struct HwIndexBuffer; +struct HwProgram; +struct HwRenderPrimitive; +struct HwRenderTarget; +struct HwSamplerGroup; +struct HwStream; +struct HwSwapChain; +struct HwSync; +struct HwTexture; +struct HwTimerQuery; +struct HwVertexBuffer; + +/* + * A type handle to a h/w resource + */ + +//! \privatesection + +class HandleBase { +public: + using HandleId = uint32_t; + static constexpr const HandleId nullid = HandleId{ std::numeric_limits::max() }; + + constexpr HandleBase() noexcept: object(nullid) {} + + explicit HandleBase(HandleId id) noexcept : object(id) { + assert_invariant(object != nullid); // usually means an uninitialized handle is used + } + + HandleBase(HandleBase const& rhs) noexcept = default; + HandleBase(HandleBase&& rhs) noexcept : object(rhs.object) { + rhs.object = nullid; + } + + HandleBase& operator=(HandleBase const& rhs) noexcept = default; + HandleBase& operator=(HandleBase&& rhs) noexcept { + std::swap(object, rhs.object); + return *this; + } + + explicit operator bool() const noexcept { return object != nullid; } + + void clear() noexcept { object = nullid; } + + bool operator==(const HandleBase& rhs) const noexcept { return object == rhs.object; } + bool operator!=(const HandleBase& rhs) const noexcept { return object != rhs.object; } + + // get this handle's handleId + HandleId getId() const noexcept { return object; } + +protected: + HandleId object; +}; + +template +struct Handle : public HandleBase { + using HandleBase::HandleBase; + + template::value> > + Handle(Handle const& base) noexcept : HandleBase(base) { } // NOLINT(hicpp-explicit-conversions) + +private: +#if !defined(NDEBUG) + template + friend utils::io::ostream& operator<<(utils::io::ostream& out, const Handle& h) noexcept; +#endif +}; + +// Types used by the command stream +// (we use this renaming because the macro-system doesn't deal well with "<" and ">") +using BufferObjectHandle = Handle; +using FenceHandle = Handle; +using IndexBufferHandle = Handle; +using ProgramHandle = Handle; +using RenderPrimitiveHandle = Handle; +using RenderTargetHandle = Handle; +using SamplerGroupHandle = Handle; +using StreamHandle = Handle; +using SwapChainHandle = Handle; +using SyncHandle = Handle; +using TextureHandle = Handle; +using TimerQueryHandle = Handle; +using VertexBufferHandle = Handle; + +} // namespace backend +} // namespace filament + +#endif // TNT_FILAMENT_DRIVER_HANDLE_H diff --git a/ios/include/backend/PipelineState.h b/ios/include/backend/PipelineState.h new file mode 100644 index 00000000..a3bac43a --- /dev/null +++ b/ios/include/backend/PipelineState.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_DRIVER_PIPELINESTATE_H +#define TNT_FILAMENT_DRIVER_PIPELINESTATE_H + +#include +#include + +#include + +#include + +namespace filament { +namespace backend { + +//! \privatesection + +struct PipelineState { + Handle program; + RasterState rasterState; + PolygonOffset polygonOffset; + Viewport scissor{ 0, 0, + (uint32_t)std::numeric_limits::max(), + (uint32_t)std::numeric_limits::max() + }; +}; + + +} // namespace backend +} // namespace filament + +#endif //TNT_FILAMENT_DRIVER_PIPELINESTATE_H diff --git a/ios/include/backend/PixelBufferDescriptor.h b/ios/include/backend/PixelBufferDescriptor.h new file mode 100644 index 00000000..327f49a4 --- /dev/null +++ b/ios/include/backend/PixelBufferDescriptor.h @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! \file + +#ifndef TNT_FILAMENT_DRIVER_PIXEL_BUFFERDESCRIPTOR_H +#define TNT_FILAMENT_DRIVER_PIXEL_BUFFERDESCRIPTOR_H + +#include +#include + +#include +#include + +#include +#include + +namespace filament { +namespace backend { + +/** + * A descriptor to an image in main memory, typically used to transfer image data from the CPU + * to the GPU. + * + * A PixelBufferDescriptor owns the memory buffer it references, therefore PixelBufferDescriptor + * cannot be copied, but can be moved. + * + * PixelBufferDescriptor releases ownership of the memory-buffer when it's destroyed. + */ +class UTILS_PUBLIC PixelBufferDescriptor : public BufferDescriptor { +public: + using PixelDataFormat = backend::PixelDataFormat; + using PixelDataType = backend::PixelDataType; + + PixelBufferDescriptor() = default; + + /** + * Creates a new PixelBufferDescriptor referencing an image in main memory + * + * @param buffer Virtual address of the buffer containing the image + * @param size Size in bytes of the buffer containing the image + * @param format Format of the image pixels + * @param type Type of the image pixels + * @param alignment Alignment in bytes of pixel rows + * @param left Left coordinate in pixels + * @param top Top coordinate in pixels + * @param stride Stride of a row in pixels + * @param callback A callback used to release the CPU buffer + * @param user An opaque user pointer passed to the callback function when it's called + */ + PixelBufferDescriptor(void const* buffer, size_t size, + PixelDataFormat format, PixelDataType type, uint8_t alignment = 1, + uint32_t left = 0, uint32_t top = 0, uint32_t stride = 0, + Callback callback = nullptr, void* user = nullptr) noexcept + : BufferDescriptor(buffer, size, callback, user), + left(left), top(top), stride(stride), + format(format), type(type), alignment(alignment) { + } + + /** + * Creates a new PixelBufferDescriptor referencing an image in main memory + * + * @param buffer Virtual address of the buffer containing the image + * @param size Size in bytes of the buffer containing the image + * @param format Format of the image pixels + * @param type Type of the image pixels + * @param callback A callback used to release the CPU buffer + * @param user An opaque user pointer passed to the callback function when it's called + */ + PixelBufferDescriptor(void const* buffer, size_t size, + PixelDataFormat format, PixelDataType type, + Callback callback, void* user = nullptr) noexcept + : BufferDescriptor(buffer, size, callback, user), + stride(0), format(format), type(type), alignment(1) { + } + + /** + * Creates a new PixelBufferDescriptor referencing a compressed image in main memory + * + * @param buffer Virtual address of the buffer containing the image + * @param size Size in bytes of the buffer containing the image + * @param format Compressed format of the image + * @param imageSize Compressed size of the image + * @param callback A callback used to release the CPU buffer + * @param user An opaque user pointer passed to the callback function when it's called + */ + PixelBufferDescriptor(void const* buffer, size_t size, + backend::CompressedPixelDataType format, uint32_t imageSize, + Callback callback, void* user = nullptr) noexcept + : BufferDescriptor(buffer, size, callback, user), + imageSize(imageSize), compressedFormat(format), type(PixelDataType::COMPRESSED), + alignment(1) { + } + + /** + * Computes the size in bytes needed to fit an image of given dimensions and format + * + * @param format Format of the image pixels + * @param type Type of the image pixels + * @param stride Stride of a row in pixels + * @param height Height of the image in rows + * @param alignment Alignment in bytes of pixel rows + * @return The buffer size needed to fit this image in bytes + */ + static constexpr size_t computeDataSize(PixelDataFormat format, PixelDataType type, + size_t stride, size_t height, size_t alignment) noexcept { + assert_invariant(alignment); + + if (type == PixelDataType::COMPRESSED) { + return 0; + } + + size_t n = 0; + switch (format) { + case PixelDataFormat::R: + case PixelDataFormat::R_INTEGER: + case PixelDataFormat::DEPTH_COMPONENT: + case PixelDataFormat::ALPHA: + n = 1; + break; + case PixelDataFormat::RG: + case PixelDataFormat::RG_INTEGER: + case PixelDataFormat::DEPTH_STENCIL: + n = 2; + break; + case PixelDataFormat::RGB: + case PixelDataFormat::RGB_INTEGER: + n = 3; + break; + case PixelDataFormat::UNUSED: // shouldn't happen (used to be rgbm) + case PixelDataFormat::RGBA: + case PixelDataFormat::RGBA_INTEGER: + n = 4; + break; + } + + size_t bpp = n; + switch (type) { + case PixelDataType::COMPRESSED: // Impossible -- to squash the IDE warnings + case PixelDataType::UBYTE: + case PixelDataType::BYTE: + // nothing to do + break; + case PixelDataType::USHORT: + case PixelDataType::SHORT: + case PixelDataType::HALF: + bpp *= 2; + break; + case PixelDataType::UINT: + case PixelDataType::INT: + case PixelDataType::FLOAT: + bpp *= 4; + break; + case PixelDataType::UINT_10F_11F_11F_REV: + // Special case, format must be RGB and uses 4 bytes + assert_invariant(format == PixelDataFormat::RGB); + bpp = 4; + break; + case PixelDataType::UINT_2_10_10_10_REV: + // Special case, format must be RGBA and uses 4 bytes + assert_invariant(format == PixelDataFormat::RGBA); + bpp = 4; + break; + case PixelDataType::USHORT_565: + // Special case, format must be RGB and uses 2 bytes + assert_invariant(format == PixelDataFormat::RGB); + bpp = 2; + break; + } + + size_t bpr = bpp * stride; + size_t bprAligned = (bpr + (alignment - 1)) & -alignment; + return bprAligned * height; + } + + //! left coordinate in pixels + uint32_t left = 0; + //! top coordinate in pixels + uint32_t top = 0; + union { + struct { + //! stride in pixels + uint32_t stride; + //! Pixel data format + PixelDataFormat format; + }; + struct { + //! compressed image size + uint32_t imageSize; + //! compressed image format + backend::CompressedPixelDataType compressedFormat; + }; + }; + //! pixel data type + PixelDataType type : 4; + //! row alignment in bytes + uint8_t alignment : 4; +}; + +} // namespace backend +} // namespace filament + +#endif // TNT_FILAMENT_DRIVER_PIXEL_BUFFERDESCRIPTOR_H diff --git a/ios/include/backend/Platform.h b/ios/include/backend/Platform.h new file mode 100644 index 00000000..b667dbb0 --- /dev/null +++ b/ios/include/backend/Platform.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! \file + +#ifndef TNT_FILAMENT_DRIVER_PLATFORM_H +#define TNT_FILAMENT_DRIVER_PLATFORM_H + +#include + +#include + +namespace filament { +namespace backend { + +class Driver; + +class UTILS_PUBLIC Platform { +public: + struct SwapChain {}; + struct Fence {}; + struct Stream {}; + struct ExternalTexture { + uintptr_t image = 0; + }; + + virtual ~Platform() noexcept; + + /** + * Queries the underlying OS version. + * @return The OS version. + */ + virtual int getOSVersion() const noexcept = 0; + + /** + * Creates and initializes the low-level API (e.g. an OpenGL context or Vulkan instance), + * then creates the concrete Driver. + * The caller takes ownership of the returned Driver* and must destroy it with delete. + * + * @param sharedContext an optional shared context. This is not meaningful with all graphic + * APIs and platforms. + * For EGL platforms, this is an EGLContext. + * + * @return nullptr on failure, or a pointer to the newly created driver. + */ + virtual backend::Driver* createDriver(void* sharedContext) noexcept = 0; + + /** + * Processes the platform's event queue when called from its primary event-handling thread. + * + * Internally, Filament might need to call this when waiting on a fence. It is only implemented + * on platforms that need it, such as macOS + OpenGL. Returns false if this is not the main + * thread, or if the platform does not need to perform any special processing. + */ + virtual bool pumpEvents() noexcept { return false; } +}; + + +class UTILS_PUBLIC DefaultPlatform : public Platform { +public: + ~DefaultPlatform() noexcept override; + + /** + * Creates a Platform configured for the requested backend if available + * + * @param backendHint Preferred backend, if not available the backend most suitable for the + * underlying platform is returned and \p backendHint is updated + * accordingly. Can't be nullptr. + * + * @return A pointer to the Plaform object. + * + * @see destroy + */ + static DefaultPlatform* create(backend::Backend* backendHint) noexcept; + + /** + * Destroys a Platform object returned by create() + * + * @param platform a reference (as a pointer) to the DefaultPlatform pointer to destroy. + * \p platform is cleared upon return. + * + * @see create + */ + static void destroy(DefaultPlatform** platform) noexcept; +}; + +} // namespace backend +} // namespace filament + +#endif // TNT_FILAMENT_DRIVER_PLATFORM_H diff --git a/ios/include/backend/PresentCallable.h b/ios/include/backend/PresentCallable.h new file mode 100644 index 00000000..5a12e686 --- /dev/null +++ b/ios/include/backend/PresentCallable.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! \file + +#ifndef TNT_FILAMENT_BACKEND_PRESENT_CALLABLE +#define TNT_FILAMENT_BACKEND_PRESENT_CALLABLE + +#include + +namespace filament { +namespace backend { + +/** + * A PresentCallable is a callable object that, when called, schedules a frame for presentation on + * a SwapChain. + * + * Typically, Filament's backend is responsible scheduling a frame's presentation. However, there + * are certain cases where the application might want to control when a frame is scheduled for + * presentation. + * + * For example, on iOS, UIKit elements can be synchronized to 3D content by scheduling a present + * within a CATransation: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * void myFrameScheduledCallback(PresentCallable presentCallable, void* user) { + * [CATransaction begin]; + * // Update other UI elements... + * presentCallable(); + * [CATransaction commit]; + * } + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * To obtain a PresentCallable, set a SwapChain::FrameScheduledCallback on a SwapChain with the + * SwapChain::setFrameScheduledCallback method. The callback is called with a PresentCallable object + * and optional user data: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * swapChain->setFrameScheduledCallback(myFrameScheduledCallback, nullptr); + * if (renderer->beginFrame(swapChain)) { + * renderer->render(view); + * renderer->endFrame(); + * } + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * @remark Only Filament's Metal backend supports PresentCallables and frame callbacks. Other + * backends ignore the callback (which will never be called) and proceed normally. + * + * @remark The SwapChain::FrameScheduledCallback is called on an arbitrary thread. + * + * Applications *must* call each PresentCallable they receive. Each PresentCallable represents a + * frame that is waiting to be presented. If an application fails to call a PresentCallable, a + * memory leak could occur. To "cancel" the presentation of a frame, pass false to the + * PresentCallable, which will cancel the presentation of the frame and release associated memory. + * + * @see Renderer, SwapChain::setFrameScheduledCallback + */ +class UTILS_PUBLIC PresentCallable { +public: + + using PresentFn = void(*)(bool presentFrame, void* user); + + PresentCallable(PresentFn fn, void* user) noexcept; + ~PresentCallable() noexcept = default; + PresentCallable(const PresentCallable& rhs) = default; + PresentCallable& operator=(const PresentCallable& rhs) = default; + + /** + * Call this PresentCallable, scheduling the associated frame for presentation. Pass false for + * presentFrame to effectively "cancel" the presentation of the frame. + * + * @param presentFrame if false, will not present the frame but releases associated memory + */ + void operator()(bool presentFrame = true) noexcept; + +private: + + PresentFn mPresentFn; + void* mUser = nullptr; + +}; + +/** + * @deprecated, FrameFinishedCallback has been renamed to SwapChain::FrameScheduledCallback. + */ +using FrameFinishedCallback UTILS_DEPRECATED = void(*)(PresentCallable callable, void* user); + +} // namespace backend +} // namespace filament + +#endif // TNT_FILAMENT_BACKEND_PRESENT_FRAME_CALLABLE diff --git a/ios/include/backend/TargetBufferInfo.h b/ios/include/backend/TargetBufferInfo.h new file mode 100644 index 00000000..3c505d06 --- /dev/null +++ b/ios/include/backend/TargetBufferInfo.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_DRIVER_TARGETBUFFERINFO_H +#define TNT_FILAMENT_DRIVER_TARGETBUFFERINFO_H + +#include +#include + +#include + +namespace filament { +namespace backend { + +//! \privatesection + +class TargetBufferInfo { +public: + // ctor for 2D textures + TargetBufferInfo(Handle h, uint8_t level = 0) noexcept // NOLINT(google-explicit-constructor) + : handle(h), level(level) { } + // ctor for cubemaps + TargetBufferInfo(Handle h, uint8_t level, TextureCubemapFace face) noexcept + : handle(h), level(level), face(face) { } + // ctor for 3D textures + TargetBufferInfo(Handle h, uint8_t level, uint16_t layer) noexcept + : handle(h), level(level), layer(layer) { } + + explicit TargetBufferInfo(TextureCubemapFace face) noexcept : face(face) {} + + explicit TargetBufferInfo(uint16_t layer) noexcept : layer(layer) {} + + // texture to be used as render target + Handle handle; + // level to be used + uint8_t level = 0; + union { + // face if texture is a cubemap + TextureCubemapFace face; + // for 3D textures + uint16_t layer = 0; + }; + TargetBufferInfo() noexcept { } +}; + +class MRT { +public: + static constexpr uint8_t MIN_SUPPORTED_RENDER_TARGET_COUNT = 4u; + + // When updating this, make sure to also take care of RenderTarget.java + static constexpr uint8_t MAX_SUPPORTED_RENDER_TARGET_COUNT = 8u; + +private: + TargetBufferInfo mInfos[MAX_SUPPORTED_RENDER_TARGET_COUNT]; + +public: + TargetBufferInfo const& operator[](size_t i) const noexcept { + return mInfos[i]; + } + + TargetBufferInfo& operator[](size_t i) noexcept { + return mInfos[i]; + } + + MRT() noexcept = default; + + MRT(TargetBufferInfo const& color) noexcept // NOLINT(hicpp-explicit-conversions) + : mInfos{ color } { + } + + MRT(TargetBufferInfo const& color0, TargetBufferInfo const& color1) noexcept + : mInfos{ color0, color1 } { + } + + MRT(TargetBufferInfo const& color0, TargetBufferInfo const& color1, + TargetBufferInfo const& color2) noexcept + : mInfos{ color0, color1, color2 } { + } + + MRT(TargetBufferInfo const& color0, TargetBufferInfo const& color1, + TargetBufferInfo const& color2, TargetBufferInfo const& color3) noexcept + : mInfos{ color0, color1, color2, color3 } { + } + + // this is here for backward compatibility + MRT(Handle h, uint8_t level, uint16_t layer) noexcept + : mInfos{{ h, level, layer }} { + } +}; + +} // namespace backend +} // namespace filament + +#endif //TNT_FILAMENT_DRIVER_TARGETBUFFERINFO_H diff --git a/ios/include/camutils/Bookmark.h b/ios/include/camutils/Bookmark.h new file mode 100644 index 00000000..44ec1d62 --- /dev/null +++ b/ios/include/camutils/Bookmark.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CAMUTILS_BOOKMARK_H +#define CAMUTILS_BOOKMARK_H + +#include + +#include +#include + +namespace filament { +namespace camutils { + +template class FreeFlightManipulator; +template class OrbitManipulator; +template class MapManipulator; +template class Manipulator; + +enum class Mode { ORBIT, MAP, FREE_FLIGHT }; + +/** + * Opaque memento to a viewing position and orientation (e.g. the "home" camera position). + * + * This little struct is meant to be passed around by value and can be used to track camera + * animation between waypoints. In map mode this implements Van Wijk interpolation. + * + * @see Manipulator::getCurrentBookmark, Manipulator::jumpToBookmark + */ +template +struct CAMUTILS_PUBLIC Bookmark { + /** + * Interpolates between two bookmarks. The t argument must be between 0 and 1 (inclusive), and + * the two endpoints must have the same mode (ORBIT or MAP). + */ + static Bookmark interpolate(Bookmark a, Bookmark b, double t); + + /** + * Recommends a duration for animation between two MAP endpoints. The return value is a unitless + * multiplier. + */ + static double duration(Bookmark a, Bookmark b); + +private: + struct MapParams { + FLOAT extent; + filament::math::vec2 center; + }; + struct OrbitParams { + FLOAT phi; + FLOAT theta; + FLOAT distance; + filament::math::vec3 pivot; + }; + struct FlightParams { + FLOAT pitch; + FLOAT yaw; + filament::math::vec3 position; + }; + Mode mode; + MapParams map; + OrbitParams orbit; + FlightParams flight; + friend class FreeFlightManipulator; + friend class OrbitManipulator; + friend class MapManipulator; +}; + +} // namespace camutils +} // namespace filament + +#endif // CAMUTILS_BOOKMARK_H diff --git a/ios/include/camutils/Manipulator.h b/ios/include/camutils/Manipulator.h new file mode 100644 index 00000000..2cc8a4da --- /dev/null +++ b/ios/include/camutils/Manipulator.h @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CAMUTILS_MANIPULATOR_H +#define CAMUTILS_MANIPULATOR_H + +#include +#include + +#include +#include +#include + +#include + +namespace filament { +namespace camutils { + +enum class Fov { VERTICAL, HORIZONTAL }; + +/** + * Helper that enables camera interaction similar to sketchfab or Google Maps. + * + * Clients notify the camera manipulator of various mouse or touch events, then periodically call + * its getLookAt() method so that they can adjust their camera(s). Three modes are supported: ORBIT, + * MAP, and FREE_FLIGHT. To construct a manipulator instance, the desired mode is passed into the + * create method. + * + * Usage example: + * + * using CameraManipulator = camutils::Manipulator; + * CameraManipulator* manip; + * + * void init() { + * manip = CameraManipulator::Builder() + * .viewport(1024, 768) + * .build(camutils::Mode::ORBIT); + * } + * + * void onMouseDown(int x, int y) { + * manip->grabBegin(x, y, false); + * } + * + * void onMouseMove(int x, int y) { + * manip->grabUpdate(x, y); + * } + * + * void onMouseUp(int x, int y) { + * manip->grabEnd(); + * } + * + * void gameLoop() { + * while (true) { + * filament::math::float3 eye, center, up; + * manip->getLookAt(&eye, ¢er, &up); + * camera->lookAt(eye, center, up); + * render(); + * } + * } + * + * @see Bookmark + */ +template +class CAMUTILS_PUBLIC Manipulator { +public: + using vec2 = filament::math::vec2; + using vec3 = filament::math::vec3; + using vec4 = filament::math::vec4; + + /** Opaque handle to a viewing position and orientation to facilitate camera animation. */ + using Bookmark = filament::camutils::Bookmark; + + /** Optional raycasting function to enable perspective-correct panning. */ + typedef bool (*RayCallback)(const vec3& origin, const vec3& dir, FLOAT* t, void* userdata); + + /** Builder state, direct access is allowed but Builder methods are preferred. **/ + struct Config { + int viewport[2]; + vec3 targetPosition; + vec3 upVector; + FLOAT zoomSpeed; + vec3 orbitHomePosition; + vec2 orbitSpeed; + Fov fovDirection; + FLOAT fovDegrees; + FLOAT farPlane; + vec2 mapExtent; + FLOAT mapMinDistance; + vec3 flightStartPosition; + FLOAT flightStartPitch; + FLOAT flightStartYaw; + FLOAT flightMaxSpeed; + FLOAT flightSpeedSteps; + vec2 flightPanSpeed; + FLOAT flightMoveDamping; + vec4 groundPlane; + RayCallback raycastCallback; + void* raycastUserdata; + }; + + struct Builder { + // Common properties + Builder& viewport(int width, int height); //! Width and height of the viewing area + Builder& targetPosition(FLOAT x, FLOAT y, FLOAT z); //! World-space position of interest, defaults to (0,0,0) + Builder& upVector(FLOAT x, FLOAT y, FLOAT z); //! Orientation for the home position, defaults to (0,1,0) + Builder& zoomSpeed(FLOAT val); //! Multiplied with scroll delta, defaults to 0.01 + + // Orbit mode properties + Builder& orbitHomePosition(FLOAT x, FLOAT y, FLOAT z); //! Initial eye position in world space, defaults to (0,0,1) + Builder& orbitSpeed(FLOAT x, FLOAT y); //! Multiplied with viewport delta, defaults to 0.01 + + // Map mode properties + Builder& fovDirection(Fov fov); //! The axis that's held constant when viewport changes + Builder& fovDegrees(FLOAT degrees); //! The full FOV (not the half-angle) + Builder& farPlane(FLOAT distance); //! The distance to the far plane + Builder& mapExtent(FLOAT worldWidth, FLOAT worldHeight); //! The ground size for computing home position + Builder& mapMinDistance(FLOAT mindist); //! Constrains the zoom-in level + + // Free flight properties + Builder& flightStartPosition(FLOAT x, FLOAT y, FLOAT z); //! Initial eye position in world space, defaults to (0,0,0) + Builder& flightStartOrientation(FLOAT pitch, FLOAT yaw); //! Initial orientation in pitch and yaw, defaults to (0,0) + Builder& flightMaxMoveSpeed(FLOAT maxSpeed); //! The maximum camera speed in world units per second, defaults to 10 + Builder& flightSpeedSteps(int steps); //! The number of speed steps adjustable with scroll wheel, defaults to 80 + Builder& flightPanSpeed(FLOAT x, FLOAT y); //! Multiplied with viewport delta, defaults to 0.01,0.01 + Builder& flightMoveDamping(FLOAT damping); //! Applies a deceleration to camera movement, defaults to 0 (no damping) + //! Lower values give slower damping times, a good default is 15 + //! Too high a value may lead to instability + + // Raycast properties + Builder& groundPlane(FLOAT a, FLOAT b, FLOAT c, FLOAT d); //! Plane equation used as a raycast fallback + Builder& raycastCallback(RayCallback cb, void* userdata); //! Raycast function for accurate grab-and-pan + + /** + * Creates a new camera manipulator, either ORBIT, MAP, or FREE_FLIGHT. + * + * Clients can simply use "delete" to destroy the manipulator. + */ + Manipulator* build(Mode mode); + + Config details = {}; + }; + + virtual ~Manipulator() = default; + + /** + * Gets the immutable mode of the manipulator. + */ + Mode getMode() const { return mMode; } + + /** + * Sets the viewport dimensions. The manipulator uses this to process grab events and raycasts. + */ + void setViewport(int width, int height); + + /** + * Gets the current orthonormal basis; this is usually called once per frame. + */ + void getLookAt(vec3* eyePosition, vec3* targetPosition, vec3* upward) const; + + /** + * Given a viewport coordinate, picks a point in the ground plane, or in the actual scene if the + * raycast callback was provided. + */ + bool raycast(int x, int y, vec3* result) const; + + /** + * Given a viewport coordinate, computes a picking ray (origin + direction). + */ + void getRay(int x, int y, vec3* origin, vec3* dir) const; + + /** + * Starts a grabbing session (i.e. the user is dragging around in the viewport). + * + * In MAP mode, this starts a panning session. + * In ORBIT mode, this starts either rotating or strafing. + * In FREE_FLIGHT mode, this starts a nodal panning session. + * + * @param x X-coordinate for point of interest in viewport space + * @param y Y-coordinate for point of interest in viewport space + * @param strafe ORBIT mode only; if true, starts a translation rather than a rotation + */ + virtual void grabBegin(int x, int y, bool strafe) = 0; + + /** + * Updates a grabbing session. + * + * This must be called at least once between grabBegin / grabEnd to dirty the camera. + */ + virtual void grabUpdate(int x, int y) = 0; + + /** + * Ends a grabbing session. + */ + virtual void grabEnd() = 0; + + /** + * Keys used to translate the camera in FREE_FLIGHT mode. + * FORWARD and BACKWARD dolly the camera forwards and backwards. + * LEFT and RIGHT strafe the camera left and right. + * UP and DOWN boom the camera upwards and downwards. + */ + enum class Key { + FORWARD, + LEFT, + BACKWARD, + RIGHT, + UP, + DOWN, + + COUNT + }; + + /** + * Signals that a key is now in the down state. + * + * In FREE_FLIGHT mode, the camera is translated forward and backward and strafed left and right + * depending on the depressed keys. This allows WASD-style movement. + */ + virtual void keyDown(Key key); + + /** + * Signals that a key is now in the up state. + * + * @see keyDown + */ + virtual void keyUp(Key key); + + /** + * In MAP and ORBIT modes, dollys the camera along the viewing direction. + * In FREE_FLIGHT mode, adjusts the move speed of the camera. + * + * @param x X-coordinate for point of interest in viewport space, ignored in FREE_FLIGHT mode + * @param y Y-coordinate for point of interest in viewport space, ignored in FREE_FLIGHT mode + * @param scrolldelta In MAP and ORBIT modes, negative means "zoom in", positive means "zoom out" + * In FREE_FLIGHT mode, negative means "slower", positive means "faster" + */ + virtual void scroll(int x, int y, FLOAT scrolldelta) = 0; + + /** + * Processes input and updates internal state. + * + * This must be called once every frame before getLookAt is valid. + * + * @param deltaTime The amount of time, in seconds, passed since the previous call to update. + */ + virtual void update(FLOAT deltaTime); + + /** + * Gets a handle that can be used to reset the manipulator back to its current position. + * + * @see jumpToBookmark + */ + virtual Bookmark getCurrentBookmark() const = 0; + + /** + * Gets a handle that can be used to reset the manipulator back to its home position. + * + * @see jumpToBookmark + */ + virtual Bookmark getHomeBookmark() const = 0; + + /** + * Sets the manipulator position and orientation back to a stashed state. + * + * @see getCurrentBookmark, getHomeBookmark + */ + virtual void jumpToBookmark(const Bookmark& bookmark) = 0; + +protected: + Manipulator(Mode mode, const Config& props); + + virtual void setProperties(const Config& props); + + vec3 raycastFarPlane(int x, int y) const; + + const Mode mMode; + Config mProps; + vec3 mEye; + vec3 mTarget; +}; + +} // namespace camutils +} // namespace filament + +#endif /* CAMUTILS_MANIPULATOR_H */ diff --git a/ios/include/camutils/compiler.h b/ios/include/camutils/compiler.h new file mode 100644 index 00000000..5e5edf94 --- /dev/null +++ b/ios/include/camutils/compiler.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CAMUTILS_COMPILER_H +#define CAMUTILS_COMPILER_H + +#if __has_attribute(visibility) +# define CAMUTILS_PUBLIC __attribute__((visibility("default"))) +#else +# define CAMUTILS_PUBLIC +#endif + +#endif // CAMUTILS_COMPILER_H diff --git a/ios/include/cgltf.h b/ios/include/cgltf.h new file mode 100644 index 00000000..4cc2d7ec --- /dev/null +++ b/ios/include/cgltf.h @@ -0,0 +1,6327 @@ +/** + * cgltf - a single-file glTF 2.0 parser written in C99. + * + * Version: 1.10 + * + * Website: https://github.com/jkuhlmann/cgltf + * + * Distributed under the MIT License, see notice at the end of this file. + * + * Building: + * Include this file where you need the struct and function + * declarations. Have exactly one source file where you define + * `CGLTF_IMPLEMENTATION` before including this file to get the + * function definitions. + * + * Reference: + * `cgltf_result cgltf_parse(const cgltf_options*, const void*, + * cgltf_size, cgltf_data**)` parses both glTF and GLB data. If + * this function returns `cgltf_result_success`, you have to call + * `cgltf_free()` on the created `cgltf_data*` variable. + * Note that contents of external files for buffers and images are not + * automatically loaded. You'll need to read these files yourself using + * URIs in the `cgltf_data` structure. + * + * `cgltf_options` is the struct passed to `cgltf_parse()` to control + * parts of the parsing process. You can use it to force the file type + * and provide memory allocation as well as file operation callbacks. + * Should be zero-initialized to trigger default behavior. + * + * `cgltf_data` is the struct allocated and filled by `cgltf_parse()`. + * It generally mirrors the glTF format as described by the spec (see + * https://github.com/KhronosGroup/glTF/tree/master/specification/2.0). + * + * `void cgltf_free(cgltf_data*)` frees the allocated `cgltf_data` + * variable. + * + * `cgltf_result cgltf_load_buffers(const cgltf_options*, cgltf_data*, + * const char* gltf_path)` can be optionally called to open and read buffer + * files using the `FILE*` APIs. The `gltf_path` argument is the path to + * the original glTF file, which allows the parser to resolve the path to + * buffer files. + * + * `cgltf_result cgltf_load_buffer_base64(const cgltf_options* options, + * cgltf_size size, const char* base64, void** out_data)` decodes + * base64-encoded data content. Used internally by `cgltf_load_buffers()`. + * This is useful when decoding data URIs in images. + * + * `cgltf_result cgltf_parse_file(const cgltf_options* options, const + * char* path, cgltf_data** out_data)` can be used to open the given + * file using `FILE*` APIs and parse the data using `cgltf_parse()`. + * + * `cgltf_result cgltf_validate(cgltf_data*)` can be used to do additional + * checks to make sure the parsed glTF data is valid. + * + * `cgltf_node_transform_local` converts the translation / rotation / scale properties of a node + * into a mat4. + * + * `cgltf_node_transform_world` calls `cgltf_node_transform_local` on every ancestor in order + * to compute the root-to-node transformation. + * + * `cgltf_accessor_unpack_floats` reads in the data from an accessor, applies sparse data (if any), + * and converts them to floating point. Assumes that `cgltf_load_buffers` has already been called. + * By passing null for the output pointer, users can find out how many floats are required in the + * output buffer. + * + * `cgltf_accessor_num_components` is a tiny utility that tells you the dimensionality of + * a certain accessor type. This can be used before `cgltf_accessor_unpack_floats` to help allocate + * the necessary amount of memory. + * + * `cgltf_accessor_read_float` reads a certain element from a non-sparse accessor and converts it to + * floating point, assuming that `cgltf_load_buffers` has already been called. The passed-in element + * size is the number of floats in the output buffer, which should be in the range [1, 16]. Returns + * false if the passed-in element_size is too small, or if the accessor is sparse. + * + * `cgltf_accessor_read_uint` is similar to its floating-point counterpart, but limited to reading + * vector types and does not support matrix types. The passed-in element size is the number of uints + * in the output buffer, which should be in the range [1, 4]. Returns false if the passed-in + * element_size is too small, or if the accessor is sparse. + * + * `cgltf_accessor_read_index` is similar to its floating-point counterpart, but it returns size_t + * and only works with single-component data types. + * + * `cgltf_result cgltf_copy_extras_json(const cgltf_data*, const cgltf_extras*, + * char* dest, cgltf_size* dest_size)` allows users to retrieve the "extras" data that + * can be attached to many glTF objects (which can be arbitrary JSON data). The + * `cgltf_extras` struct stores the offsets of the start and end of the extras JSON data + * as it appears in the complete glTF JSON data. This function copies the extras data + * into the provided buffer. If `dest` is NULL, the length of the data is written into + * `dest_size`. You can then parse this data using your own JSON parser + * or, if you've included the cgltf implementation using the integrated JSMN JSON parser. + */ +#ifndef CGLTF_H_INCLUDED__ +#define CGLTF_H_INCLUDED__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef size_t cgltf_size; +typedef float cgltf_float; +typedef int cgltf_int; +typedef unsigned int cgltf_uint; +typedef int cgltf_bool; + +typedef enum cgltf_file_type +{ + cgltf_file_type_invalid, + cgltf_file_type_gltf, + cgltf_file_type_glb, +} cgltf_file_type; + +typedef enum cgltf_result +{ + cgltf_result_success, + cgltf_result_data_too_short, + cgltf_result_unknown_format, + cgltf_result_invalid_json, + cgltf_result_invalid_gltf, + cgltf_result_invalid_options, + cgltf_result_file_not_found, + cgltf_result_io_error, + cgltf_result_out_of_memory, + cgltf_result_legacy_gltf, +} cgltf_result; + +typedef struct cgltf_memory_options +{ + void* (*alloc)(void* user, cgltf_size size); + void (*free) (void* user, void* ptr); + void* user_data; +} cgltf_memory_options; + +typedef struct cgltf_file_options +{ + cgltf_result(*read)(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, const char* path, cgltf_size* size, void** data); + void (*release)(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, void* data); + void* user_data; +} cgltf_file_options; + +typedef struct cgltf_options +{ + cgltf_file_type type; /* invalid == auto detect */ + cgltf_size json_token_count; /* 0 == auto */ + cgltf_memory_options memory; + cgltf_file_options file; +} cgltf_options; + +typedef enum cgltf_buffer_view_type +{ + cgltf_buffer_view_type_invalid, + cgltf_buffer_view_type_indices, + cgltf_buffer_view_type_vertices, +} cgltf_buffer_view_type; + +typedef enum cgltf_attribute_type +{ + cgltf_attribute_type_invalid, + cgltf_attribute_type_position, + cgltf_attribute_type_normal, + cgltf_attribute_type_tangent, + cgltf_attribute_type_texcoord, + cgltf_attribute_type_color, + cgltf_attribute_type_joints, + cgltf_attribute_type_weights, +} cgltf_attribute_type; + +typedef enum cgltf_component_type +{ + cgltf_component_type_invalid, + cgltf_component_type_r_8, /* BYTE */ + cgltf_component_type_r_8u, /* UNSIGNED_BYTE */ + cgltf_component_type_r_16, /* SHORT */ + cgltf_component_type_r_16u, /* UNSIGNED_SHORT */ + cgltf_component_type_r_32u, /* UNSIGNED_INT */ + cgltf_component_type_r_32f, /* FLOAT */ +} cgltf_component_type; + +typedef enum cgltf_type +{ + cgltf_type_invalid, + cgltf_type_scalar, + cgltf_type_vec2, + cgltf_type_vec3, + cgltf_type_vec4, + cgltf_type_mat2, + cgltf_type_mat3, + cgltf_type_mat4, +} cgltf_type; + +typedef enum cgltf_primitive_type +{ + cgltf_primitive_type_points, + cgltf_primitive_type_lines, + cgltf_primitive_type_line_loop, + cgltf_primitive_type_line_strip, + cgltf_primitive_type_triangles, + cgltf_primitive_type_triangle_strip, + cgltf_primitive_type_triangle_fan, +} cgltf_primitive_type; + +typedef enum cgltf_alpha_mode +{ + cgltf_alpha_mode_opaque, + cgltf_alpha_mode_mask, + cgltf_alpha_mode_blend, +} cgltf_alpha_mode; + +typedef enum cgltf_animation_path_type { + cgltf_animation_path_type_invalid, + cgltf_animation_path_type_translation, + cgltf_animation_path_type_rotation, + cgltf_animation_path_type_scale, + cgltf_animation_path_type_weights, +} cgltf_animation_path_type; + +typedef enum cgltf_interpolation_type { + cgltf_interpolation_type_linear, + cgltf_interpolation_type_step, + cgltf_interpolation_type_cubic_spline, +} cgltf_interpolation_type; + +typedef enum cgltf_camera_type { + cgltf_camera_type_invalid, + cgltf_camera_type_perspective, + cgltf_camera_type_orthographic, +} cgltf_camera_type; + +typedef enum cgltf_light_type { + cgltf_light_type_invalid, + cgltf_light_type_directional, + cgltf_light_type_point, + cgltf_light_type_spot, +} cgltf_light_type; + +typedef struct cgltf_extras { + cgltf_size start_offset; + cgltf_size end_offset; +} cgltf_extras; + +typedef struct cgltf_extension { + char* name; + char* data; +} cgltf_extension; + +typedef struct cgltf_buffer +{ + char* name; + cgltf_size size; + char* uri; + void* data; /* loaded by cgltf_load_buffers */ + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_buffer; + +typedef enum cgltf_meshopt_compression_mode { + cgltf_meshopt_compression_mode_invalid, + cgltf_meshopt_compression_mode_attributes, + cgltf_meshopt_compression_mode_triangles, + cgltf_meshopt_compression_mode_indices, +} cgltf_meshopt_compression_mode; + +typedef enum cgltf_meshopt_compression_filter { + cgltf_meshopt_compression_filter_none, + cgltf_meshopt_compression_filter_octahedral, + cgltf_meshopt_compression_filter_quaternion, + cgltf_meshopt_compression_filter_exponential, +} cgltf_meshopt_compression_filter; + +typedef struct cgltf_meshopt_compression +{ + cgltf_buffer* buffer; + cgltf_size offset; + cgltf_size size; + cgltf_size stride; + cgltf_size count; + cgltf_meshopt_compression_mode mode; + cgltf_meshopt_compression_filter filter; +} cgltf_meshopt_compression; + +typedef struct cgltf_buffer_view +{ + char *name; + cgltf_buffer* buffer; + cgltf_size offset; + cgltf_size size; + cgltf_size stride; /* 0 == automatically determined by accessor */ + cgltf_buffer_view_type type; + void* data; /* overrides buffer->data if present, filled by extensions */ + cgltf_bool has_meshopt_compression; + cgltf_meshopt_compression meshopt_compression; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_buffer_view; + +typedef struct cgltf_accessor_sparse +{ + cgltf_size count; + cgltf_buffer_view* indices_buffer_view; + cgltf_size indices_byte_offset; + cgltf_component_type indices_component_type; + cgltf_buffer_view* values_buffer_view; + cgltf_size values_byte_offset; + cgltf_extras extras; + cgltf_extras indices_extras; + cgltf_extras values_extras; + cgltf_size extensions_count; + cgltf_extension* extensions; + cgltf_size indices_extensions_count; + cgltf_extension* indices_extensions; + cgltf_size values_extensions_count; + cgltf_extension* values_extensions; +} cgltf_accessor_sparse; + +typedef struct cgltf_accessor +{ + char* name; + cgltf_component_type component_type; + cgltf_bool normalized; + cgltf_type type; + cgltf_size offset; + cgltf_size count; + cgltf_size stride; + cgltf_buffer_view* buffer_view; + cgltf_bool has_min; + cgltf_float min[16]; + cgltf_bool has_max; + cgltf_float max[16]; + cgltf_bool is_sparse; + cgltf_accessor_sparse sparse; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_accessor; + +typedef struct cgltf_attribute +{ + char* name; + cgltf_attribute_type type; + cgltf_int index; + cgltf_accessor* data; +} cgltf_attribute; + +typedef struct cgltf_image +{ + char* name; + char* uri; + cgltf_buffer_view* buffer_view; + char* mime_type; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_image; + +typedef struct cgltf_sampler +{ + char* name; + cgltf_int mag_filter; + cgltf_int min_filter; + cgltf_int wrap_s; + cgltf_int wrap_t; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_sampler; + +typedef struct cgltf_texture +{ + char* name; + cgltf_image* image; + cgltf_sampler* sampler; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_texture; + +typedef struct cgltf_texture_transform +{ + cgltf_float offset[2]; + cgltf_float rotation; + cgltf_float scale[2]; + cgltf_int texcoord; +} cgltf_texture_transform; + +typedef struct cgltf_texture_view +{ + cgltf_texture* texture; + cgltf_int texcoord; + cgltf_float scale; /* equivalent to strength for occlusion_texture */ + cgltf_bool has_transform; + cgltf_texture_transform transform; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_texture_view; + +typedef struct cgltf_pbr_metallic_roughness +{ + cgltf_texture_view base_color_texture; + cgltf_texture_view metallic_roughness_texture; + + cgltf_float base_color_factor[4]; + cgltf_float metallic_factor; + cgltf_float roughness_factor; + + cgltf_extras extras; +} cgltf_pbr_metallic_roughness; + +typedef struct cgltf_pbr_specular_glossiness +{ + cgltf_texture_view diffuse_texture; + cgltf_texture_view specular_glossiness_texture; + + cgltf_float diffuse_factor[4]; + cgltf_float specular_factor[3]; + cgltf_float glossiness_factor; +} cgltf_pbr_specular_glossiness; + +typedef struct cgltf_clearcoat +{ + cgltf_texture_view clearcoat_texture; + cgltf_texture_view clearcoat_roughness_texture; + cgltf_texture_view clearcoat_normal_texture; + + cgltf_float clearcoat_factor; + cgltf_float clearcoat_roughness_factor; +} cgltf_clearcoat; + +typedef struct cgltf_transmission +{ + cgltf_texture_view transmission_texture; + cgltf_float transmission_factor; +} cgltf_transmission; + +typedef struct cgltf_ior +{ + cgltf_float ior; +} cgltf_ior; + +typedef struct cgltf_specular +{ + cgltf_texture_view specular_texture; + cgltf_texture_view specular_color_texture; + cgltf_float specular_color_factor[3]; + cgltf_float specular_factor; +} cgltf_specular; + +typedef struct cgltf_volume +{ + cgltf_texture_view thickness_texture; + cgltf_float thickness_factor; + cgltf_float attenuation_color[3]; + cgltf_float attenuation_distance; +} cgltf_volume; + +typedef struct cgltf_sheen +{ + cgltf_texture_view sheen_color_texture; + cgltf_float sheen_color_factor[3]; + cgltf_texture_view sheen_roughness_texture; + cgltf_float sheen_roughness_factor; +} cgltf_sheen; + +typedef struct cgltf_material +{ + char* name; + cgltf_bool has_pbr_metallic_roughness; + cgltf_bool has_pbr_specular_glossiness; + cgltf_bool has_clearcoat; + cgltf_bool has_transmission; + cgltf_bool has_volume; + cgltf_bool has_ior; + cgltf_bool has_specular; + cgltf_bool has_sheen; + cgltf_pbr_metallic_roughness pbr_metallic_roughness; + cgltf_pbr_specular_glossiness pbr_specular_glossiness; + cgltf_clearcoat clearcoat; + cgltf_ior ior; + cgltf_specular specular; + cgltf_sheen sheen; + cgltf_transmission transmission; + cgltf_volume volume; + cgltf_texture_view normal_texture; + cgltf_texture_view occlusion_texture; + cgltf_texture_view emissive_texture; + cgltf_float emissive_factor[3]; + cgltf_alpha_mode alpha_mode; + cgltf_float alpha_cutoff; + cgltf_bool double_sided; + cgltf_bool unlit; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_material; + +typedef struct cgltf_material_mapping +{ + cgltf_size variant; + cgltf_material* material; + cgltf_extras extras; +} cgltf_material_mapping; + +typedef struct cgltf_morph_target { + cgltf_attribute* attributes; + cgltf_size attributes_count; +} cgltf_morph_target; + +typedef struct cgltf_draco_mesh_compression { + cgltf_buffer_view* buffer_view; + cgltf_attribute* attributes; + cgltf_size attributes_count; +} cgltf_draco_mesh_compression; + +typedef struct cgltf_primitive { + cgltf_primitive_type type; + cgltf_accessor* indices; + cgltf_material* material; + cgltf_attribute* attributes; + cgltf_size attributes_count; + cgltf_morph_target* targets; + cgltf_size targets_count; + cgltf_extras extras; + cgltf_bool has_draco_mesh_compression; + cgltf_draco_mesh_compression draco_mesh_compression; + cgltf_material_mapping* mappings; + cgltf_size mappings_count; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_primitive; + +typedef struct cgltf_mesh { + char* name; + cgltf_primitive* primitives; + cgltf_size primitives_count; + cgltf_float* weights; + cgltf_size weights_count; + char** target_names; + cgltf_size target_names_count; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_mesh; + +typedef struct cgltf_node cgltf_node; + +typedef struct cgltf_skin { + char* name; + cgltf_node** joints; + cgltf_size joints_count; + cgltf_node* skeleton; + cgltf_accessor* inverse_bind_matrices; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_skin; + +typedef struct cgltf_camera_perspective { + cgltf_bool has_aspect_ratio; + cgltf_float aspect_ratio; + cgltf_float yfov; + cgltf_bool has_zfar; + cgltf_float zfar; + cgltf_float znear; + cgltf_extras extras; +} cgltf_camera_perspective; + +typedef struct cgltf_camera_orthographic { + cgltf_float xmag; + cgltf_float ymag; + cgltf_float zfar; + cgltf_float znear; + cgltf_extras extras; +} cgltf_camera_orthographic; + +typedef struct cgltf_camera { + char* name; + cgltf_camera_type type; + union { + cgltf_camera_perspective perspective; + cgltf_camera_orthographic orthographic; + } data; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_camera; + +typedef struct cgltf_light { + char* name; + cgltf_float color[3]; + cgltf_float intensity; + cgltf_light_type type; + cgltf_float range; + cgltf_float spot_inner_cone_angle; + cgltf_float spot_outer_cone_angle; +} cgltf_light; + +struct cgltf_node { + char* name; + cgltf_node* parent; + cgltf_node** children; + cgltf_size children_count; + cgltf_skin* skin; + cgltf_mesh* mesh; + cgltf_camera* camera; + cgltf_light* light; + cgltf_float* weights; + cgltf_size weights_count; + cgltf_bool has_translation; + cgltf_bool has_rotation; + cgltf_bool has_scale; + cgltf_bool has_matrix; + cgltf_float translation[3]; + cgltf_float rotation[4]; + cgltf_float scale[3]; + cgltf_float matrix[16]; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +}; + +typedef struct cgltf_scene { + char* name; + cgltf_node** nodes; + cgltf_size nodes_count; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_scene; + +typedef struct cgltf_animation_sampler { + cgltf_accessor* input; + cgltf_accessor* output; + cgltf_interpolation_type interpolation; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_animation_sampler; + +typedef struct cgltf_animation_channel { + cgltf_animation_sampler* sampler; + cgltf_node* target_node; + cgltf_animation_path_type target_path; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_animation_channel; + +typedef struct cgltf_animation { + char* name; + cgltf_animation_sampler* samplers; + cgltf_size samplers_count; + cgltf_animation_channel* channels; + cgltf_size channels_count; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_animation; + +typedef struct cgltf_material_variant +{ + char* name; + cgltf_extras extras; +} cgltf_material_variant; + +typedef struct cgltf_asset { + char* copyright; + char* generator; + char* version; + char* min_version; + cgltf_extras extras; + cgltf_size extensions_count; + cgltf_extension* extensions; +} cgltf_asset; + +typedef struct cgltf_data +{ + cgltf_file_type file_type; + void* file_data; + + cgltf_asset asset; + + cgltf_mesh* meshes; + cgltf_size meshes_count; + + cgltf_material* materials; + cgltf_size materials_count; + + cgltf_accessor* accessors; + cgltf_size accessors_count; + + cgltf_buffer_view* buffer_views; + cgltf_size buffer_views_count; + + cgltf_buffer* buffers; + cgltf_size buffers_count; + + cgltf_image* images; + cgltf_size images_count; + + cgltf_texture* textures; + cgltf_size textures_count; + + cgltf_sampler* samplers; + cgltf_size samplers_count; + + cgltf_skin* skins; + cgltf_size skins_count; + + cgltf_camera* cameras; + cgltf_size cameras_count; + + cgltf_light* lights; + cgltf_size lights_count; + + cgltf_node* nodes; + cgltf_size nodes_count; + + cgltf_scene* scenes; + cgltf_size scenes_count; + + cgltf_scene* scene; + + cgltf_animation* animations; + cgltf_size animations_count; + + cgltf_material_variant* variants; + cgltf_size variants_count; + + cgltf_extras extras; + + cgltf_size data_extensions_count; + cgltf_extension* data_extensions; + + char** extensions_used; + cgltf_size extensions_used_count; + + char** extensions_required; + cgltf_size extensions_required_count; + + const char* json; + cgltf_size json_size; + + const void* bin; + cgltf_size bin_size; + + cgltf_memory_options memory; + cgltf_file_options file; +} cgltf_data; + +cgltf_result cgltf_parse( + const cgltf_options* options, + const void* data, + cgltf_size size, + cgltf_data** out_data); + +cgltf_result cgltf_parse_file( + const cgltf_options* options, + const char* path, + cgltf_data** out_data); + +cgltf_result cgltf_load_buffers( + const cgltf_options* options, + cgltf_data* data, + const char* gltf_path); + +cgltf_result cgltf_load_buffer_base64(const cgltf_options* options, cgltf_size size, const char* base64, void** out_data); + +void cgltf_decode_uri(char* uri); + +cgltf_result cgltf_validate(cgltf_data* data); + +void cgltf_free(cgltf_data* data); + +void cgltf_node_transform_local(const cgltf_node* node, cgltf_float* out_matrix); +void cgltf_node_transform_world(const cgltf_node* node, cgltf_float* out_matrix); + +cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size); +cgltf_bool cgltf_accessor_read_uint(const cgltf_accessor* accessor, cgltf_size index, cgltf_uint* out, cgltf_size element_size); +cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size index); + +cgltf_size cgltf_num_components(cgltf_type type); + +cgltf_size cgltf_accessor_unpack_floats(const cgltf_accessor* accessor, cgltf_float* out, cgltf_size float_count); + +cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras* extras, char* dest, cgltf_size* dest_size); + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef CGLTF_H_INCLUDED__ */ + +/* + * + * Stop now, if you are only interested in the API. + * Below, you find the implementation. + * + */ + +#if defined(__INTELLISENSE__) || defined(__JETBRAINS_IDE__) +/* This makes MSVC/CLion intellisense work. */ +#define CGLTF_IMPLEMENTATION +#endif + +#ifdef CGLTF_IMPLEMENTATION + +#include /* For uint8_t, uint32_t */ +#include /* For strncpy */ +#include /* For fopen */ +#include /* For UINT_MAX etc */ +#include /* For FLT_MAX */ + +#if !defined(CGLTF_MALLOC) || !defined(CGLTF_FREE) || !defined(CGLTF_ATOI) || !defined(CGLTF_ATOF) +#include /* For malloc, free, atoi, atof */ +#endif + +/* JSMN_PARENT_LINKS is necessary to make parsing large structures linear in input size */ +#define JSMN_PARENT_LINKS + +/* JSMN_STRICT is necessary to reject invalid JSON documents */ +#define JSMN_STRICT + +/* + * -- jsmn.h start -- + * Source: https://github.com/zserge/jsmn + * License: MIT + */ +typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1, + JSMN_ARRAY = 2, + JSMN_STRING = 3, + JSMN_PRIMITIVE = 4 +} jsmntype_t; +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; +typedef struct { + jsmntype_t type; + int start; + int end; + int size; +#ifdef JSMN_PARENT_LINKS + int parent; +#endif +} jsmntok_t; +typedef struct { + unsigned int pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g parent object or array */ +} jsmn_parser; +static void jsmn_init(jsmn_parser *parser); +static int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, size_t num_tokens); +/* + * -- jsmn.h end -- + */ + + +static const cgltf_size GlbHeaderSize = 12; +static const cgltf_size GlbChunkHeaderSize = 8; +static const uint32_t GlbVersion = 2; +static const uint32_t GlbMagic = 0x46546C67; +static const uint32_t GlbMagicJsonChunk = 0x4E4F534A; +static const uint32_t GlbMagicBinChunk = 0x004E4942; + +#ifndef CGLTF_MALLOC +#define CGLTF_MALLOC(size) malloc(size) +#endif +#ifndef CGLTF_FREE +#define CGLTF_FREE(ptr) free(ptr) +#endif +#ifndef CGLTF_ATOI +#define CGLTF_ATOI(str) atoi(str) +#endif +#ifndef CGLTF_ATOF +#define CGLTF_ATOF(str) atof(str) +#endif +#ifndef CGLTF_VALIDATE_ENABLE_ASSERTS +#define CGLTF_VALIDATE_ENABLE_ASSERTS 0 +#endif + +static void* cgltf_default_alloc(void* user, cgltf_size size) +{ + (void)user; + return CGLTF_MALLOC(size); +} + +static void cgltf_default_free(void* user, void* ptr) +{ + (void)user; + CGLTF_FREE(ptr); +} + +static void* cgltf_calloc(cgltf_options* options, size_t element_size, cgltf_size count) +{ + if (SIZE_MAX / element_size < count) + { + return NULL; + } + void* result = options->memory.alloc(options->memory.user_data, element_size * count); + if (!result) + { + return NULL; + } + memset(result, 0, element_size * count); + return result; +} + +static cgltf_result cgltf_default_file_read(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, const char* path, cgltf_size* size, void** data) +{ + (void)file_options; + void* (*memory_alloc)(void*, cgltf_size) = memory_options->alloc ? memory_options->alloc : &cgltf_default_alloc; + void (*memory_free)(void*, void*) = memory_options->free ? memory_options->free : &cgltf_default_free; + + FILE* file = fopen(path, "rb"); + if (!file) + { + return cgltf_result_file_not_found; + } + + cgltf_size file_size = size ? *size : 0; + + if (file_size == 0) + { + fseek(file, 0, SEEK_END); + + long length = ftell(file); + if (length < 0) + { + fclose(file); + return cgltf_result_io_error; + } + + fseek(file, 0, SEEK_SET); + file_size = (cgltf_size)length; + } + + char* file_data = (char*)memory_alloc(memory_options->user_data, file_size); + if (!file_data) + { + fclose(file); + return cgltf_result_out_of_memory; + } + + cgltf_size read_size = fread(file_data, 1, file_size, file); + + fclose(file); + + if (read_size != file_size) + { + memory_free(memory_options->user_data, file_data); + return cgltf_result_io_error; + } + + if (size) + { + *size = file_size; + } + if (data) + { + *data = file_data; + } + + return cgltf_result_success; +} + +static void cgltf_default_file_release(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, void* data) +{ + (void)file_options; + void (*memfree)(void*, void*) = memory_options->free ? memory_options->free : &cgltf_default_free; + memfree(memory_options->user_data, data); +} + +static cgltf_result cgltf_parse_json(cgltf_options* options, const uint8_t* json_chunk, cgltf_size size, cgltf_data** out_data); + +cgltf_result cgltf_parse(const cgltf_options* options, const void* data, cgltf_size size, cgltf_data** out_data) +{ + if (size < GlbHeaderSize) + { + return cgltf_result_data_too_short; + } + + if (options == NULL) + { + return cgltf_result_invalid_options; + } + + cgltf_options fixed_options = *options; + if (fixed_options.memory.alloc == NULL) + { + fixed_options.memory.alloc = &cgltf_default_alloc; + } + if (fixed_options.memory.free == NULL) + { + fixed_options.memory.free = &cgltf_default_free; + } + + uint32_t tmp; + // Magic + memcpy(&tmp, data, 4); + if (tmp != GlbMagic) + { + if (fixed_options.type == cgltf_file_type_invalid) + { + fixed_options.type = cgltf_file_type_gltf; + } + else if (fixed_options.type == cgltf_file_type_glb) + { + return cgltf_result_unknown_format; + } + } + + if (fixed_options.type == cgltf_file_type_gltf) + { + cgltf_result json_result = cgltf_parse_json(&fixed_options, (const uint8_t*)data, size, out_data); + if (json_result != cgltf_result_success) + { + return json_result; + } + + (*out_data)->file_type = cgltf_file_type_gltf; + + return cgltf_result_success; + } + + const uint8_t* ptr = (const uint8_t*)data; + // Version + memcpy(&tmp, ptr + 4, 4); + uint32_t version = tmp; + if (version != GlbVersion) + { + return version < GlbVersion ? cgltf_result_legacy_gltf : cgltf_result_unknown_format; + } + + // Total length + memcpy(&tmp, ptr + 8, 4); + if (tmp > size) + { + return cgltf_result_data_too_short; + } + + const uint8_t* json_chunk = ptr + GlbHeaderSize; + + if (GlbHeaderSize + GlbChunkHeaderSize > size) + { + return cgltf_result_data_too_short; + } + + // JSON chunk: length + uint32_t json_length; + memcpy(&json_length, json_chunk, 4); + if (GlbHeaderSize + GlbChunkHeaderSize + json_length > size) + { + return cgltf_result_data_too_short; + } + + // JSON chunk: magic + memcpy(&tmp, json_chunk + 4, 4); + if (tmp != GlbMagicJsonChunk) + { + return cgltf_result_unknown_format; + } + + json_chunk += GlbChunkHeaderSize; + + const void* bin = 0; + cgltf_size bin_size = 0; + + if (GlbHeaderSize + GlbChunkHeaderSize + json_length + GlbChunkHeaderSize <= size) + { + // We can read another chunk + const uint8_t* bin_chunk = json_chunk + json_length; + + // Bin chunk: length + uint32_t bin_length; + memcpy(&bin_length, bin_chunk, 4); + if (GlbHeaderSize + GlbChunkHeaderSize + json_length + GlbChunkHeaderSize + bin_length > size) + { + return cgltf_result_data_too_short; + } + + // Bin chunk: magic + memcpy(&tmp, bin_chunk + 4, 4); + if (tmp != GlbMagicBinChunk) + { + return cgltf_result_unknown_format; + } + + bin_chunk += GlbChunkHeaderSize; + + bin = bin_chunk; + bin_size = bin_length; + } + + cgltf_result json_result = cgltf_parse_json(&fixed_options, json_chunk, json_length, out_data); + if (json_result != cgltf_result_success) + { + return json_result; + } + + (*out_data)->file_type = cgltf_file_type_glb; + (*out_data)->bin = bin; + (*out_data)->bin_size = bin_size; + + return cgltf_result_success; +} + +cgltf_result cgltf_parse_file(const cgltf_options* options, const char* path, cgltf_data** out_data) +{ + if (options == NULL) + { + return cgltf_result_invalid_options; + } + + void (*memory_free)(void*, void*) = options->memory.free ? options->memory.free : &cgltf_default_free; + cgltf_result (*file_read)(const struct cgltf_memory_options*, const struct cgltf_file_options*, const char*, cgltf_size*, void**) = options->file.read ? options->file.read : &cgltf_default_file_read; + + void* file_data = NULL; + cgltf_size file_size = 0; + cgltf_result result = file_read(&options->memory, &options->file, path, &file_size, &file_data); + if (result != cgltf_result_success) + { + return result; + } + + result = cgltf_parse(options, file_data, file_size, out_data); + + if (result != cgltf_result_success) + { + memory_free(options->memory.user_data, file_data); + return result; + } + + (*out_data)->file_data = file_data; + + return cgltf_result_success; +} + +static void cgltf_combine_paths(char* path, const char* base, const char* uri) +{ + const char* s0 = strrchr(base, '/'); + const char* s1 = strrchr(base, '\\'); + const char* slash = s0 ? (s1 && s1 > s0 ? s1 : s0) : s1; + + if (slash) + { + size_t prefix = slash - base + 1; + + strncpy(path, base, prefix); + strcpy(path + prefix, uri); + } + else + { + strcpy(path, uri); + } +} + +static cgltf_result cgltf_load_buffer_file(const cgltf_options* options, cgltf_size size, const char* uri, const char* gltf_path, void** out_data) +{ + void* (*memory_alloc)(void*, cgltf_size) = options->memory.alloc ? options->memory.alloc : &cgltf_default_alloc; + void (*memory_free)(void*, void*) = options->memory.free ? options->memory.free : &cgltf_default_free; + cgltf_result (*file_read)(const struct cgltf_memory_options*, const struct cgltf_file_options*, const char*, cgltf_size*, void**) = options->file.read ? options->file.read : &cgltf_default_file_read; + + char* path = (char*)memory_alloc(options->memory.user_data, strlen(uri) + strlen(gltf_path) + 1); + if (!path) + { + return cgltf_result_out_of_memory; + } + + cgltf_combine_paths(path, gltf_path, uri); + + // after combining, the tail of the resulting path is a uri; decode_uri converts it into path + cgltf_decode_uri(path + strlen(path) - strlen(uri)); + + void* file_data = NULL; + cgltf_result result = file_read(&options->memory, &options->file, path, &size, &file_data); + + memory_free(options->memory.user_data, path); + + *out_data = (result == cgltf_result_success) ? file_data : NULL; + + return result; +} + +cgltf_result cgltf_load_buffer_base64(const cgltf_options* options, cgltf_size size, const char* base64, void** out_data) +{ + void* (*memory_alloc)(void*, cgltf_size) = options->memory.alloc ? options->memory.alloc : &cgltf_default_alloc; + void (*memory_free)(void*, void*) = options->memory.free ? options->memory.free : &cgltf_default_free; + + unsigned char* data = (unsigned char*)memory_alloc(options->memory.user_data, size); + if (!data) + { + return cgltf_result_out_of_memory; + } + + unsigned int buffer = 0; + unsigned int buffer_bits = 0; + + for (cgltf_size i = 0; i < size; ++i) + { + while (buffer_bits < 8) + { + char ch = *base64++; + + int index = + (unsigned)(ch - 'A') < 26 ? (ch - 'A') : + (unsigned)(ch - 'a') < 26 ? (ch - 'a') + 26 : + (unsigned)(ch - '0') < 10 ? (ch - '0') + 52 : + ch == '+' ? 62 : + ch == '/' ? 63 : + -1; + + if (index < 0) + { + memory_free(options->memory.user_data, data); + return cgltf_result_io_error; + } + + buffer = (buffer << 6) | index; + buffer_bits += 6; + } + + data[i] = (unsigned char)(buffer >> (buffer_bits - 8)); + buffer_bits -= 8; + } + + *out_data = data; + + return cgltf_result_success; +} + +static int cgltf_unhex(char ch) +{ + return + (unsigned)(ch - '0') < 10 ? (ch - '0') : + (unsigned)(ch - 'A') < 6 ? (ch - 'A') + 10 : + (unsigned)(ch - 'a') < 6 ? (ch - 'a') + 10 : + -1; +} + +void cgltf_decode_uri(char* uri) +{ + char* write = uri; + char* i = uri; + + while (*i) + { + if (*i == '%') + { + int ch1 = cgltf_unhex(i[1]); + + if (ch1 >= 0) + { + int ch2 = cgltf_unhex(i[2]); + + if (ch2 >= 0) + { + *write++ = (char)(ch1 * 16 + ch2); + i += 3; + continue; + } + } + } + + *write++ = *i++; + } + + *write = 0; +} + +cgltf_result cgltf_load_buffers(const cgltf_options* options, cgltf_data* data, const char* gltf_path) +{ + if (options == NULL) + { + return cgltf_result_invalid_options; + } + + if (data->buffers_count && data->buffers[0].data == NULL && data->buffers[0].uri == NULL && data->bin) + { + if (data->bin_size < data->buffers[0].size) + { + return cgltf_result_data_too_short; + } + + data->buffers[0].data = (void*)data->bin; + } + + for (cgltf_size i = 0; i < data->buffers_count; ++i) + { + if (data->buffers[i].data) + { + continue; + } + + const char* uri = data->buffers[i].uri; + + if (uri == NULL) + { + continue; + } + + if (strncmp(uri, "data:", 5) == 0) + { + const char* comma = strchr(uri, ','); + + if (comma && comma - uri >= 7 && strncmp(comma - 7, ";base64", 7) == 0) + { + cgltf_result res = cgltf_load_buffer_base64(options, data->buffers[i].size, comma + 1, &data->buffers[i].data); + + if (res != cgltf_result_success) + { + return res; + } + } + else + { + return cgltf_result_unknown_format; + } + } + else if (strstr(uri, "://") == NULL && gltf_path) + { + cgltf_result res = cgltf_load_buffer_file(options, data->buffers[i].size, uri, gltf_path, &data->buffers[i].data); + + if (res != cgltf_result_success) + { + return res; + } + } + else + { + return cgltf_result_unknown_format; + } + } + + return cgltf_result_success; +} + +static cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type); + +static cgltf_size cgltf_calc_index_bound(cgltf_buffer_view* buffer_view, cgltf_size offset, cgltf_component_type component_type, cgltf_size count) +{ + char* data = (char*)buffer_view->buffer->data + offset + buffer_view->offset; + cgltf_size bound = 0; + + switch (component_type) + { + case cgltf_component_type_r_8u: + for (size_t i = 0; i < count; ++i) + { + cgltf_size v = ((unsigned char*)data)[i]; + bound = bound > v ? bound : v; + } + break; + + case cgltf_component_type_r_16u: + for (size_t i = 0; i < count; ++i) + { + cgltf_size v = ((unsigned short*)data)[i]; + bound = bound > v ? bound : v; + } + break; + + case cgltf_component_type_r_32u: + for (size_t i = 0; i < count; ++i) + { + cgltf_size v = ((unsigned int*)data)[i]; + bound = bound > v ? bound : v; + } + break; + + default: + ; + } + + return bound; +} + +#if CGLTF_VALIDATE_ENABLE_ASSERTS +#define CGLTF_ASSERT_IF(cond, result) assert(!(cond)); if (cond) return result; +#else +#define CGLTF_ASSERT_IF(cond, result) if (cond) return result; +#endif + +cgltf_result cgltf_validate(cgltf_data* data) +{ + for (cgltf_size i = 0; i < data->accessors_count; ++i) + { + cgltf_accessor* accessor = &data->accessors[i]; + + cgltf_size element_size = cgltf_calc_size(accessor->type, accessor->component_type); + + if (accessor->buffer_view) + { + cgltf_size req_size = accessor->offset + accessor->stride * (accessor->count - 1) + element_size; + + CGLTF_ASSERT_IF(accessor->buffer_view->size < req_size, cgltf_result_data_too_short); + } + + if (accessor->is_sparse) + { + cgltf_accessor_sparse* sparse = &accessor->sparse; + + cgltf_size indices_component_size = cgltf_calc_size(cgltf_type_scalar, sparse->indices_component_type); + cgltf_size indices_req_size = sparse->indices_byte_offset + indices_component_size * sparse->count; + cgltf_size values_req_size = sparse->values_byte_offset + element_size * sparse->count; + + CGLTF_ASSERT_IF(sparse->indices_buffer_view->size < indices_req_size || + sparse->values_buffer_view->size < values_req_size, cgltf_result_data_too_short); + + CGLTF_ASSERT_IF(sparse->indices_component_type != cgltf_component_type_r_8u && + sparse->indices_component_type != cgltf_component_type_r_16u && + sparse->indices_component_type != cgltf_component_type_r_32u, cgltf_result_invalid_gltf); + + if (sparse->indices_buffer_view->buffer->data) + { + cgltf_size index_bound = cgltf_calc_index_bound(sparse->indices_buffer_view, sparse->indices_byte_offset, sparse->indices_component_type, sparse->count); + + CGLTF_ASSERT_IF(index_bound >= accessor->count, cgltf_result_data_too_short); + } + } + } + + for (cgltf_size i = 0; i < data->buffer_views_count; ++i) + { + cgltf_size req_size = data->buffer_views[i].offset + data->buffer_views[i].size; + + CGLTF_ASSERT_IF(data->buffer_views[i].buffer && data->buffer_views[i].buffer->size < req_size, cgltf_result_data_too_short); + + if (data->buffer_views[i].has_meshopt_compression) + { + cgltf_meshopt_compression* mc = &data->buffer_views[i].meshopt_compression; + + CGLTF_ASSERT_IF(mc->buffer == NULL || mc->buffer->size < mc->offset + mc->size, cgltf_result_data_too_short); + + CGLTF_ASSERT_IF(data->buffer_views[i].stride && mc->stride != data->buffer_views[i].stride, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF(data->buffer_views[i].size != mc->stride * mc->count, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF(mc->mode == cgltf_meshopt_compression_mode_invalid, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF(mc->mode == cgltf_meshopt_compression_mode_attributes && !(mc->stride % 4 == 0 && mc->stride <= 256), cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF(mc->mode == cgltf_meshopt_compression_mode_triangles && mc->count % 3 != 0, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF((mc->mode == cgltf_meshopt_compression_mode_triangles || mc->mode == cgltf_meshopt_compression_mode_indices) && mc->stride != 2 && mc->stride != 4, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF((mc->mode == cgltf_meshopt_compression_mode_triangles || mc->mode == cgltf_meshopt_compression_mode_indices) && mc->filter != cgltf_meshopt_compression_filter_none, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF(mc->filter == cgltf_meshopt_compression_filter_octahedral && mc->stride != 4 && mc->stride != 8, cgltf_result_invalid_gltf); + + CGLTF_ASSERT_IF(mc->filter == cgltf_meshopt_compression_filter_quaternion && mc->stride != 8, cgltf_result_invalid_gltf); + } + } + + for (cgltf_size i = 0; i < data->meshes_count; ++i) + { + if (data->meshes[i].weights) + { + CGLTF_ASSERT_IF(data->meshes[i].primitives_count && data->meshes[i].primitives[0].targets_count != data->meshes[i].weights_count, cgltf_result_invalid_gltf); + } + + if (data->meshes[i].target_names) + { + CGLTF_ASSERT_IF(data->meshes[i].primitives_count && data->meshes[i].primitives[0].targets_count != data->meshes[i].target_names_count, cgltf_result_invalid_gltf); + } + + for (cgltf_size j = 0; j < data->meshes[i].primitives_count; ++j) + { + CGLTF_ASSERT_IF(data->meshes[i].primitives[j].targets_count != data->meshes[i].primitives[0].targets_count, cgltf_result_invalid_gltf); + + if (data->meshes[i].primitives[j].attributes_count) + { + cgltf_accessor* first = data->meshes[i].primitives[j].attributes[0].data; + + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].attributes_count; ++k) + { + CGLTF_ASSERT_IF(data->meshes[i].primitives[j].attributes[k].data->count != first->count, cgltf_result_invalid_gltf); + } + + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].targets_count; ++k) + { + for (cgltf_size m = 0; m < data->meshes[i].primitives[j].targets[k].attributes_count; ++m) + { + CGLTF_ASSERT_IF(data->meshes[i].primitives[j].targets[k].attributes[m].data->count != first->count, cgltf_result_invalid_gltf); + } + } + + cgltf_accessor* indices = data->meshes[i].primitives[j].indices; + + CGLTF_ASSERT_IF(indices && + indices->component_type != cgltf_component_type_r_8u && + indices->component_type != cgltf_component_type_r_16u && + indices->component_type != cgltf_component_type_r_32u, cgltf_result_invalid_gltf); + + if (indices && indices->buffer_view && indices->buffer_view->buffer->data) + { + cgltf_size index_bound = cgltf_calc_index_bound(indices->buffer_view, indices->offset, indices->component_type, indices->count); + + CGLTF_ASSERT_IF(index_bound >= first->count, cgltf_result_data_too_short); + } + + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].mappings_count; ++k) + { + CGLTF_ASSERT_IF(data->meshes[i].primitives[j].mappings[k].variant >= data->variants_count, cgltf_result_invalid_gltf); + } + } + } + } + + for (cgltf_size i = 0; i < data->nodes_count; ++i) + { + if (data->nodes[i].weights && data->nodes[i].mesh) + { + CGLTF_ASSERT_IF (data->nodes[i].mesh->primitives_count && data->nodes[i].mesh->primitives[0].targets_count != data->nodes[i].weights_count, cgltf_result_invalid_gltf); + } + } + + for (cgltf_size i = 0; i < data->nodes_count; ++i) + { + cgltf_node* p1 = data->nodes[i].parent; + cgltf_node* p2 = p1 ? p1->parent : NULL; + + while (p1 && p2) + { + CGLTF_ASSERT_IF(p1 == p2, cgltf_result_invalid_gltf); + + p1 = p1->parent; + p2 = p2->parent ? p2->parent->parent : NULL; + } + } + + for (cgltf_size i = 0; i < data->scenes_count; ++i) + { + for (cgltf_size j = 0; j < data->scenes[i].nodes_count; ++j) + { + CGLTF_ASSERT_IF(data->scenes[i].nodes[j]->parent, cgltf_result_invalid_gltf); + } + } + + for (cgltf_size i = 0; i < data->animations_count; ++i) + { + for (cgltf_size j = 0; j < data->animations[i].channels_count; ++j) + { + cgltf_animation_channel* channel = &data->animations[i].channels[j]; + + if (!channel->target_node) + { + continue; + } + + cgltf_size components = 1; + + if (channel->target_path == cgltf_animation_path_type_weights) + { + CGLTF_ASSERT_IF(!channel->target_node->mesh || !channel->target_node->mesh->primitives_count, cgltf_result_invalid_gltf); + + components = channel->target_node->mesh->primitives[0].targets_count; + } + + cgltf_size values = channel->sampler->interpolation == cgltf_interpolation_type_cubic_spline ? 3 : 1; + + CGLTF_ASSERT_IF(channel->sampler->input->count * components * values != channel->sampler->output->count, cgltf_result_data_too_short); + } + } + + return cgltf_result_success; +} + +cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras* extras, char* dest, cgltf_size* dest_size) +{ + cgltf_size json_size = extras->end_offset - extras->start_offset; + + if (!dest) + { + if (dest_size) + { + *dest_size = json_size + 1; + return cgltf_result_success; + } + return cgltf_result_invalid_options; + } + + if (*dest_size + 1 < json_size) + { + strncpy(dest, data->json + extras->start_offset, *dest_size - 1); + dest[*dest_size - 1] = 0; + } + else + { + strncpy(dest, data->json + extras->start_offset, json_size); + dest[json_size] = 0; + } + + return cgltf_result_success; +} + +void cgltf_free_extensions(cgltf_data* data, cgltf_extension* extensions, cgltf_size extensions_count) +{ + for (cgltf_size i = 0; i < extensions_count; ++i) + { + data->memory.free(data->memory.user_data, extensions[i].name); + data->memory.free(data->memory.user_data, extensions[i].data); + } + data->memory.free(data->memory.user_data, extensions); +} + +void cgltf_free(cgltf_data* data) +{ + if (!data) + { + return; + } + + void (*file_release)(const struct cgltf_memory_options*, const struct cgltf_file_options*, void* data) = data->file.release ? data->file.release : cgltf_default_file_release; + + data->memory.free(data->memory.user_data, data->asset.copyright); + data->memory.free(data->memory.user_data, data->asset.generator); + data->memory.free(data->memory.user_data, data->asset.version); + data->memory.free(data->memory.user_data, data->asset.min_version); + + cgltf_free_extensions(data, data->asset.extensions, data->asset.extensions_count); + + for (cgltf_size i = 0; i < data->accessors_count; ++i) + { + data->memory.free(data->memory.user_data, data->accessors[i].name); + + if(data->accessors[i].is_sparse) + { + cgltf_free_extensions(data, data->accessors[i].sparse.extensions, data->accessors[i].sparse.extensions_count); + cgltf_free_extensions(data, data->accessors[i].sparse.indices_extensions, data->accessors[i].sparse.indices_extensions_count); + cgltf_free_extensions(data, data->accessors[i].sparse.values_extensions, data->accessors[i].sparse.values_extensions_count); + } + cgltf_free_extensions(data, data->accessors[i].extensions, data->accessors[i].extensions_count); + } + data->memory.free(data->memory.user_data, data->accessors); + + for (cgltf_size i = 0; i < data->buffer_views_count; ++i) + { + data->memory.free(data->memory.user_data, data->buffer_views[i].name); + data->memory.free(data->memory.user_data, data->buffer_views[i].data); + + cgltf_free_extensions(data, data->buffer_views[i].extensions, data->buffer_views[i].extensions_count); + } + data->memory.free(data->memory.user_data, data->buffer_views); + + for (cgltf_size i = 0; i < data->buffers_count; ++i) + { + data->memory.free(data->memory.user_data, data->buffers[i].name); + + if (data->buffers[i].data != data->bin) + { + file_release(&data->memory, &data->file, data->buffers[i].data); + } + data->memory.free(data->memory.user_data, data->buffers[i].uri); + + cgltf_free_extensions(data, data->buffers[i].extensions, data->buffers[i].extensions_count); + } + + data->memory.free(data->memory.user_data, data->buffers); + + for (cgltf_size i = 0; i < data->meshes_count; ++i) + { + data->memory.free(data->memory.user_data, data->meshes[i].name); + + for (cgltf_size j = 0; j < data->meshes[i].primitives_count; ++j) + { + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].attributes_count; ++k) + { + data->memory.free(data->memory.user_data, data->meshes[i].primitives[j].attributes[k].name); + } + + data->memory.free(data->memory.user_data, data->meshes[i].primitives[j].attributes); + + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].targets_count; ++k) + { + for (cgltf_size m = 0; m < data->meshes[i].primitives[j].targets[k].attributes_count; ++m) + { + data->memory.free(data->memory.user_data, data->meshes[i].primitives[j].targets[k].attributes[m].name); + } + + data->memory.free(data->memory.user_data, data->meshes[i].primitives[j].targets[k].attributes); + } + + data->memory.free(data->memory.user_data, data->meshes[i].primitives[j].targets); + + if (data->meshes[i].primitives[j].has_draco_mesh_compression) + { + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].draco_mesh_compression.attributes_count; ++k) + { + data->memory.free(data->memory.user_data, data->meshes[i].primitives[j].draco_mesh_compression.attributes[k].name); + } + + data->memory.free(data->memory.user_data, data->meshes[i].primitives[j].draco_mesh_compression.attributes); + } + + data->memory.free(data->memory.user_data, data->meshes[i].primitives[j].mappings); + + cgltf_free_extensions(data, data->meshes[i].primitives[j].extensions, data->meshes[i].primitives[j].extensions_count); + } + + data->memory.free(data->memory.user_data, data->meshes[i].primitives); + data->memory.free(data->memory.user_data, data->meshes[i].weights); + + for (cgltf_size j = 0; j < data->meshes[i].target_names_count; ++j) + { + data->memory.free(data->memory.user_data, data->meshes[i].target_names[j]); + } + + cgltf_free_extensions(data, data->meshes[i].extensions, data->meshes[i].extensions_count); + + data->memory.free(data->memory.user_data, data->meshes[i].target_names); + } + + data->memory.free(data->memory.user_data, data->meshes); + + for (cgltf_size i = 0; i < data->materials_count; ++i) + { + data->memory.free(data->memory.user_data, data->materials[i].name); + + if(data->materials[i].has_pbr_metallic_roughness) + { + cgltf_free_extensions(data, data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.extensions, data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.extensions_count); + cgltf_free_extensions(data, data->materials[i].pbr_metallic_roughness.base_color_texture.extensions, data->materials[i].pbr_metallic_roughness.base_color_texture.extensions_count); + } + if(data->materials[i].has_pbr_specular_glossiness) + { + cgltf_free_extensions(data, data->materials[i].pbr_specular_glossiness.diffuse_texture.extensions, data->materials[i].pbr_specular_glossiness.diffuse_texture.extensions_count); + cgltf_free_extensions(data, data->materials[i].pbr_specular_glossiness.specular_glossiness_texture.extensions, data->materials[i].pbr_specular_glossiness.specular_glossiness_texture.extensions_count); + } + if(data->materials[i].has_clearcoat) + { + cgltf_free_extensions(data, data->materials[i].clearcoat.clearcoat_texture.extensions, data->materials[i].clearcoat.clearcoat_texture.extensions_count); + cgltf_free_extensions(data, data->materials[i].clearcoat.clearcoat_roughness_texture.extensions, data->materials[i].clearcoat.clearcoat_roughness_texture.extensions_count); + cgltf_free_extensions(data, data->materials[i].clearcoat.clearcoat_normal_texture.extensions, data->materials[i].clearcoat.clearcoat_normal_texture.extensions_count); + } + if(data->materials[i].has_specular) + { + cgltf_free_extensions(data, data->materials[i].specular.specular_texture.extensions, data->materials[i].specular.specular_texture.extensions_count); + cgltf_free_extensions(data, data->materials[i].specular.specular_color_texture.extensions, data->materials[i].specular.specular_color_texture.extensions_count); + } + if(data->materials[i].has_transmission) + { + cgltf_free_extensions(data, data->materials[i].transmission.transmission_texture.extensions, data->materials[i].transmission.transmission_texture.extensions_count); + } + if (data->materials[i].has_volume) + { + cgltf_free_extensions(data, data->materials[i].volume.thickness_texture.extensions, data->materials[i].volume.thickness_texture.extensions_count); + } + if(data->materials[i].has_sheen) + { + cgltf_free_extensions(data, data->materials[i].sheen.sheen_color_texture.extensions, data->materials[i].sheen.sheen_color_texture.extensions_count); + cgltf_free_extensions(data, data->materials[i].sheen.sheen_roughness_texture.extensions, data->materials[i].sheen.sheen_roughness_texture.extensions_count); + } + + cgltf_free_extensions(data, data->materials[i].normal_texture.extensions, data->materials[i].normal_texture.extensions_count); + cgltf_free_extensions(data, data->materials[i].occlusion_texture.extensions, data->materials[i].occlusion_texture.extensions_count); + cgltf_free_extensions(data, data->materials[i].emissive_texture.extensions, data->materials[i].emissive_texture.extensions_count); + + cgltf_free_extensions(data, data->materials[i].extensions, data->materials[i].extensions_count); + } + + data->memory.free(data->memory.user_data, data->materials); + + for (cgltf_size i = 0; i < data->images_count; ++i) + { + data->memory.free(data->memory.user_data, data->images[i].name); + data->memory.free(data->memory.user_data, data->images[i].uri); + data->memory.free(data->memory.user_data, data->images[i].mime_type); + + cgltf_free_extensions(data, data->images[i].extensions, data->images[i].extensions_count); + } + + data->memory.free(data->memory.user_data, data->images); + + for (cgltf_size i = 0; i < data->textures_count; ++i) + { + data->memory.free(data->memory.user_data, data->textures[i].name); + cgltf_free_extensions(data, data->textures[i].extensions, data->textures[i].extensions_count); + } + + data->memory.free(data->memory.user_data, data->textures); + + for (cgltf_size i = 0; i < data->samplers_count; ++i) + { + data->memory.free(data->memory.user_data, data->samplers[i].name); + cgltf_free_extensions(data, data->samplers[i].extensions, data->samplers[i].extensions_count); + } + + data->memory.free(data->memory.user_data, data->samplers); + + for (cgltf_size i = 0; i < data->skins_count; ++i) + { + data->memory.free(data->memory.user_data, data->skins[i].name); + data->memory.free(data->memory.user_data, data->skins[i].joints); + + cgltf_free_extensions(data, data->skins[i].extensions, data->skins[i].extensions_count); + } + + data->memory.free(data->memory.user_data, data->skins); + + for (cgltf_size i = 0; i < data->cameras_count; ++i) + { + data->memory.free(data->memory.user_data, data->cameras[i].name); + cgltf_free_extensions(data, data->cameras[i].extensions, data->cameras[i].extensions_count); + } + + data->memory.free(data->memory.user_data, data->cameras); + + for (cgltf_size i = 0; i < data->lights_count; ++i) + { + data->memory.free(data->memory.user_data, data->lights[i].name); + } + + data->memory.free(data->memory.user_data, data->lights); + + for (cgltf_size i = 0; i < data->nodes_count; ++i) + { + data->memory.free(data->memory.user_data, data->nodes[i].name); + data->memory.free(data->memory.user_data, data->nodes[i].children); + data->memory.free(data->memory.user_data, data->nodes[i].weights); + cgltf_free_extensions(data, data->nodes[i].extensions, data->nodes[i].extensions_count); + } + + data->memory.free(data->memory.user_data, data->nodes); + + for (cgltf_size i = 0; i < data->scenes_count; ++i) + { + data->memory.free(data->memory.user_data, data->scenes[i].name); + data->memory.free(data->memory.user_data, data->scenes[i].nodes); + + cgltf_free_extensions(data, data->scenes[i].extensions, data->scenes[i].extensions_count); + } + + data->memory.free(data->memory.user_data, data->scenes); + + for (cgltf_size i = 0; i < data->animations_count; ++i) + { + data->memory.free(data->memory.user_data, data->animations[i].name); + for (cgltf_size j = 0; j < data->animations[i].samplers_count; ++j) + { + cgltf_free_extensions(data, data->animations[i].samplers[j].extensions, data->animations[i].samplers[j].extensions_count); + } + data->memory.free(data->memory.user_data, data->animations[i].samplers); + + for (cgltf_size j = 0; j < data->animations[i].channels_count; ++j) + { + cgltf_free_extensions(data, data->animations[i].channels[j].extensions, data->animations[i].channels[j].extensions_count); + } + data->memory.free(data->memory.user_data, data->animations[i].channels); + + cgltf_free_extensions(data, data->animations[i].extensions, data->animations[i].extensions_count); + } + + data->memory.free(data->memory.user_data, data->animations); + + for (cgltf_size i = 0; i < data->variants_count; ++i) + { + data->memory.free(data->memory.user_data, data->variants[i].name); + } + + data->memory.free(data->memory.user_data, data->variants); + + cgltf_free_extensions(data, data->data_extensions, data->data_extensions_count); + + for (cgltf_size i = 0; i < data->extensions_used_count; ++i) + { + data->memory.free(data->memory.user_data, data->extensions_used[i]); + } + + data->memory.free(data->memory.user_data, data->extensions_used); + + for (cgltf_size i = 0; i < data->extensions_required_count; ++i) + { + data->memory.free(data->memory.user_data, data->extensions_required[i]); + } + + data->memory.free(data->memory.user_data, data->extensions_required); + + file_release(&data->memory, &data->file, data->file_data); + + data->memory.free(data->memory.user_data, data); +} + +void cgltf_node_transform_local(const cgltf_node* node, cgltf_float* out_matrix) +{ + cgltf_float* lm = out_matrix; + + if (node->has_matrix) + { + memcpy(lm, node->matrix, sizeof(float) * 16); + } + else + { + float tx = node->translation[0]; + float ty = node->translation[1]; + float tz = node->translation[2]; + + float qx = node->rotation[0]; + float qy = node->rotation[1]; + float qz = node->rotation[2]; + float qw = node->rotation[3]; + + float sx = node->scale[0]; + float sy = node->scale[1]; + float sz = node->scale[2]; + + lm[0] = (1 - 2 * qy*qy - 2 * qz*qz) * sx; + lm[1] = (2 * qx*qy + 2 * qz*qw) * sx; + lm[2] = (2 * qx*qz - 2 * qy*qw) * sx; + lm[3] = 0.f; + + lm[4] = (2 * qx*qy - 2 * qz*qw) * sy; + lm[5] = (1 - 2 * qx*qx - 2 * qz*qz) * sy; + lm[6] = (2 * qy*qz + 2 * qx*qw) * sy; + lm[7] = 0.f; + + lm[8] = (2 * qx*qz + 2 * qy*qw) * sz; + lm[9] = (2 * qy*qz - 2 * qx*qw) * sz; + lm[10] = (1 - 2 * qx*qx - 2 * qy*qy) * sz; + lm[11] = 0.f; + + lm[12] = tx; + lm[13] = ty; + lm[14] = tz; + lm[15] = 1.f; + } +} + +void cgltf_node_transform_world(const cgltf_node* node, cgltf_float* out_matrix) +{ + cgltf_float* lm = out_matrix; + cgltf_node_transform_local(node, lm); + + const cgltf_node* parent = node->parent; + + while (parent) + { + float pm[16]; + cgltf_node_transform_local(parent, pm); + + for (int i = 0; i < 4; ++i) + { + float l0 = lm[i * 4 + 0]; + float l1 = lm[i * 4 + 1]; + float l2 = lm[i * 4 + 2]; + + float r0 = l0 * pm[0] + l1 * pm[4] + l2 * pm[8]; + float r1 = l0 * pm[1] + l1 * pm[5] + l2 * pm[9]; + float r2 = l0 * pm[2] + l1 * pm[6] + l2 * pm[10]; + + lm[i * 4 + 0] = r0; + lm[i * 4 + 1] = r1; + lm[i * 4 + 2] = r2; + } + + lm[12] += pm[12]; + lm[13] += pm[13]; + lm[14] += pm[14]; + + parent = parent->parent; + } +} + +static cgltf_size cgltf_component_read_index(const void* in, cgltf_component_type component_type) +{ + switch (component_type) + { + case cgltf_component_type_r_16: + return *((const int16_t*) in); + case cgltf_component_type_r_16u: + return *((const uint16_t*) in); + case cgltf_component_type_r_32u: + return *((const uint32_t*) in); + case cgltf_component_type_r_32f: + return (cgltf_size)*((const float*) in); + case cgltf_component_type_r_8: + return *((const int8_t*) in); + case cgltf_component_type_r_8u: + return *((const uint8_t*) in); + default: + return 0; + } +} + +static cgltf_float cgltf_component_read_float(const void* in, cgltf_component_type component_type, cgltf_bool normalized) +{ + if (component_type == cgltf_component_type_r_32f) + { + return *((const float*) in); + } + + if (normalized) + { + switch (component_type) + { + // note: glTF spec doesn't currently define normalized conversions for 32-bit integers + case cgltf_component_type_r_16: + return *((const int16_t*) in) / (cgltf_float)32767; + case cgltf_component_type_r_16u: + return *((const uint16_t*) in) / (cgltf_float)65535; + case cgltf_component_type_r_8: + return *((const int8_t*) in) / (cgltf_float)127; + case cgltf_component_type_r_8u: + return *((const uint8_t*) in) / (cgltf_float)255; + default: + return 0; + } + } + + return (cgltf_float)cgltf_component_read_index(in, component_type); +} + +static cgltf_size cgltf_component_size(cgltf_component_type component_type); + +static cgltf_bool cgltf_element_read_float(const uint8_t* element, cgltf_type type, cgltf_component_type component_type, cgltf_bool normalized, cgltf_float* out, cgltf_size element_size) +{ + cgltf_size num_components = cgltf_num_components(type); + + if (element_size < num_components) { + return 0; + } + + // There are three special cases for component extraction, see #data-alignment in the 2.0 spec. + + cgltf_size component_size = cgltf_component_size(component_type); + + if (type == cgltf_type_mat2 && component_size == 1) + { + out[0] = cgltf_component_read_float(element, component_type, normalized); + out[1] = cgltf_component_read_float(element + 1, component_type, normalized); + out[2] = cgltf_component_read_float(element + 4, component_type, normalized); + out[3] = cgltf_component_read_float(element + 5, component_type, normalized); + return 1; + } + + if (type == cgltf_type_mat3 && component_size == 1) + { + out[0] = cgltf_component_read_float(element, component_type, normalized); + out[1] = cgltf_component_read_float(element + 1, component_type, normalized); + out[2] = cgltf_component_read_float(element + 2, component_type, normalized); + out[3] = cgltf_component_read_float(element + 4, component_type, normalized); + out[4] = cgltf_component_read_float(element + 5, component_type, normalized); + out[5] = cgltf_component_read_float(element + 6, component_type, normalized); + out[6] = cgltf_component_read_float(element + 8, component_type, normalized); + out[7] = cgltf_component_read_float(element + 9, component_type, normalized); + out[8] = cgltf_component_read_float(element + 10, component_type, normalized); + return 1; + } + + if (type == cgltf_type_mat3 && component_size == 2) + { + out[0] = cgltf_component_read_float(element, component_type, normalized); + out[1] = cgltf_component_read_float(element + 2, component_type, normalized); + out[2] = cgltf_component_read_float(element + 4, component_type, normalized); + out[3] = cgltf_component_read_float(element + 8, component_type, normalized); + out[4] = cgltf_component_read_float(element + 10, component_type, normalized); + out[5] = cgltf_component_read_float(element + 12, component_type, normalized); + out[6] = cgltf_component_read_float(element + 16, component_type, normalized); + out[7] = cgltf_component_read_float(element + 18, component_type, normalized); + out[8] = cgltf_component_read_float(element + 20, component_type, normalized); + return 1; + } + + for (cgltf_size i = 0; i < num_components; ++i) + { + out[i] = cgltf_component_read_float(element + component_size * i, component_type, normalized); + } + return 1; +} + +const uint8_t* cgltf_buffer_view_data(const cgltf_buffer_view* view) +{ + if (view->data) + return (const uint8_t*)view->data; + + if (!view->buffer->data) + return NULL; + + const uint8_t* result = (const uint8_t*)view->buffer->data; + result += view->offset; + return result; +} + +cgltf_bool cgltf_accessor_read_float(const cgltf_accessor* accessor, cgltf_size index, cgltf_float* out, cgltf_size element_size) +{ + if (accessor->is_sparse) + { + return 0; + } + if (accessor->buffer_view == NULL) + { + memset(out, 0, element_size * sizeof(cgltf_float)); + return 1; + } + const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view); + if (element == NULL) + { + return 0; + } + element += accessor->offset + accessor->stride * index; + return cgltf_element_read_float(element, accessor->type, accessor->component_type, accessor->normalized, out, element_size); +} + +cgltf_size cgltf_accessor_unpack_floats(const cgltf_accessor* accessor, cgltf_float* out, cgltf_size float_count) +{ + cgltf_size floats_per_element = cgltf_num_components(accessor->type); + cgltf_size available_floats = accessor->count * floats_per_element; + if (out == NULL) + { + return available_floats; + } + + float_count = available_floats < float_count ? available_floats : float_count; + cgltf_size element_count = float_count / floats_per_element; + + // First pass: convert each element in the base accessor. + cgltf_float* dest = out; + cgltf_accessor dense = *accessor; + dense.is_sparse = 0; + for (cgltf_size index = 0; index < element_count; index++, dest += floats_per_element) + { + if (!cgltf_accessor_read_float(&dense, index, dest, floats_per_element)) + { + return 0; + } + } + + // Second pass: write out each element in the sparse accessor. + if (accessor->is_sparse) + { + const cgltf_accessor_sparse* sparse = &dense.sparse; + + const uint8_t* index_data = cgltf_buffer_view_data(sparse->indices_buffer_view); + const uint8_t* reader_head = cgltf_buffer_view_data(sparse->values_buffer_view); + + if (index_data == NULL || reader_head == NULL) + { + return 0; + } + + index_data += sparse->indices_byte_offset; + reader_head += sparse->values_byte_offset; + + cgltf_size index_stride = cgltf_component_size(sparse->indices_component_type); + for (cgltf_size reader_index = 0; reader_index < sparse->count; reader_index++, index_data += index_stride) + { + size_t writer_index = cgltf_component_read_index(index_data, sparse->indices_component_type); + float* writer_head = out + writer_index * floats_per_element; + + if (!cgltf_element_read_float(reader_head, dense.type, dense.component_type, dense.normalized, writer_head, floats_per_element)) + { + return 0; + } + + reader_head += dense.stride; + } + } + + return element_count * floats_per_element; +} + +static cgltf_uint cgltf_component_read_uint(const void* in, cgltf_component_type component_type) +{ + switch (component_type) + { + case cgltf_component_type_r_8: + return *((const int8_t*) in); + + case cgltf_component_type_r_8u: + return *((const uint8_t*) in); + + case cgltf_component_type_r_16: + return *((const int16_t*) in); + + case cgltf_component_type_r_16u: + return *((const uint16_t*) in); + + case cgltf_component_type_r_32u: + return *((const uint32_t*) in); + + default: + return 0; + } +} + +static cgltf_bool cgltf_element_read_uint(const uint8_t* element, cgltf_type type, cgltf_component_type component_type, cgltf_uint* out, cgltf_size element_size) +{ + cgltf_size num_components = cgltf_num_components(type); + + if (element_size < num_components) + { + return 0; + } + + // Reading integer matrices is not a valid use case + if (type == cgltf_type_mat2 || type == cgltf_type_mat3 || type == cgltf_type_mat4) + { + return 0; + } + + cgltf_size component_size = cgltf_component_size(component_type); + + for (cgltf_size i = 0; i < num_components; ++i) + { + out[i] = cgltf_component_read_uint(element + component_size * i, component_type); + } + return 1; +} + +cgltf_bool cgltf_accessor_read_uint(const cgltf_accessor* accessor, cgltf_size index, cgltf_uint* out, cgltf_size element_size) +{ + if (accessor->is_sparse) + { + return 0; + } + if (accessor->buffer_view == NULL) + { + memset(out, 0, element_size * sizeof( cgltf_uint )); + return 1; + } + const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view); + if (element == NULL) + { + return 0; + } + element += accessor->offset + accessor->stride * index; + return cgltf_element_read_uint(element, accessor->type, accessor->component_type, out, element_size); +} + +cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size index) +{ + if (accessor->is_sparse) + { + return 0; // This is an error case, but we can't communicate the error with existing interface. + } + if (accessor->buffer_view == NULL) + { + return 0; + } + const uint8_t* element = cgltf_buffer_view_data(accessor->buffer_view); + if (element == NULL) + { + return 0; // This is an error case, but we can't communicate the error with existing interface. + } + element += accessor->offset + accessor->stride * index; + return cgltf_component_read_index(element, accessor->component_type); +} + +#define CGLTF_ERROR_JSON -1 +#define CGLTF_ERROR_NOMEM -2 +#define CGLTF_ERROR_LEGACY -3 + +#define CGLTF_CHECK_TOKTYPE(tok_, type_) if ((tok_).type != (type_)) { return CGLTF_ERROR_JSON; } +#define CGLTF_CHECK_KEY(tok_) if ((tok_).type != JSMN_STRING || (tok_).size == 0) { return CGLTF_ERROR_JSON; } /* checking size for 0 verifies that a value follows the key */ + +#define CGLTF_PTRINDEX(type, idx) (type*)((cgltf_size)idx + 1) +#define CGLTF_PTRFIXUP(var, data, size) if (var) { if ((cgltf_size)var > size) { return CGLTF_ERROR_JSON; } var = &data[(cgltf_size)var-1]; } +#define CGLTF_PTRFIXUP_REQ(var, data, size) if (!var || (cgltf_size)var > size) { return CGLTF_ERROR_JSON; } var = &data[(cgltf_size)var-1]; + +static int cgltf_json_strcmp(jsmntok_t const* tok, const uint8_t* json_chunk, const char* str) +{ + CGLTF_CHECK_TOKTYPE(*tok, JSMN_STRING); + size_t const str_len = strlen(str); + size_t const name_length = tok->end - tok->start; + return (str_len == name_length) ? strncmp((const char*)json_chunk + tok->start, str, str_len) : 128; +} + +static int cgltf_json_to_int(jsmntok_t const* tok, const uint8_t* json_chunk) +{ + CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE); + char tmp[128]; + int size = (cgltf_size)(tok->end - tok->start) < sizeof(tmp) ? tok->end - tok->start : (int)(sizeof(tmp) - 1); + strncpy(tmp, (const char*)json_chunk + tok->start, size); + tmp[size] = 0; + return CGLTF_ATOI(tmp); +} + +static cgltf_float cgltf_json_to_float(jsmntok_t const* tok, const uint8_t* json_chunk) +{ + CGLTF_CHECK_TOKTYPE(*tok, JSMN_PRIMITIVE); + char tmp[128]; + int size = (cgltf_size)(tok->end - tok->start) < sizeof(tmp) ? tok->end - tok->start : (int)(sizeof(tmp) - 1); + strncpy(tmp, (const char*)json_chunk + tok->start, size); + tmp[size] = 0; + return (cgltf_float)CGLTF_ATOF(tmp); +} + +static cgltf_bool cgltf_json_to_bool(jsmntok_t const* tok, const uint8_t* json_chunk) +{ + int size = tok->end - tok->start; + return size == 4 && memcmp(json_chunk + tok->start, "true", 4) == 0; +} + +static int cgltf_skip_json(jsmntok_t const* tokens, int i) +{ + int end = i + 1; + + while (i < end) + { + switch (tokens[i].type) + { + case JSMN_OBJECT: + end += tokens[i].size * 2; + break; + + case JSMN_ARRAY: + end += tokens[i].size; + break; + + case JSMN_PRIMITIVE: + case JSMN_STRING: + break; + + default: + return -1; + } + + i++; + } + + return i; +} + +static void cgltf_fill_float_array(float* out_array, int size, float value) +{ + for (int j = 0; j < size; ++j) + { + out_array[j] = value; + } +} + +static int cgltf_parse_json_float_array(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, float* out_array, int size) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); + if (tokens[i].size != size) + { + return CGLTF_ERROR_JSON; + } + ++i; + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); + out_array[j] = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + return i; +} + +static int cgltf_parse_json_string(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, char** out_string) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_STRING); + if (*out_string) + { + return CGLTF_ERROR_JSON; + } + int size = tokens[i].end - tokens[i].start; + char* result = (char*)options->memory.alloc(options->memory.user_data, size + 1); + if (!result) + { + return CGLTF_ERROR_NOMEM; + } + strncpy(result, (const char*)json_chunk + tokens[i].start, size); + result[size] = 0; + *out_string = result; + return i + 1; +} + +static int cgltf_parse_json_array(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, size_t element_size, void** out_array, cgltf_size* out_size) +{ + (void)json_chunk; + if (tokens[i].type != JSMN_ARRAY) + { + return tokens[i].type == JSMN_OBJECT ? CGLTF_ERROR_LEGACY : CGLTF_ERROR_JSON; + } + if (*out_array) + { + return CGLTF_ERROR_JSON; + } + int size = tokens[i].size; + void* result = cgltf_calloc(options, element_size, size); + if (!result) + { + return CGLTF_ERROR_NOMEM; + } + *out_array = result; + *out_size = size; + return i + 1; +} + +static int cgltf_parse_json_string_array(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, char*** out_array, cgltf_size* out_size) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(char*), (void**)out_array, out_size); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < *out_size; ++j) + { + i = cgltf_parse_json_string(options, tokens, i, json_chunk, j + (*out_array)); + if (i < 0) + { + return i; + } + } + return i; +} + +static void cgltf_parse_attribute_type(const char* name, cgltf_attribute_type* out_type, int* out_index) +{ + const char* us = strchr(name, '_'); + size_t len = us ? (size_t)(us - name) : strlen(name); + + if (len == 8 && strncmp(name, "POSITION", 8) == 0) + { + *out_type = cgltf_attribute_type_position; + } + else if (len == 6 && strncmp(name, "NORMAL", 6) == 0) + { + *out_type = cgltf_attribute_type_normal; + } + else if (len == 7 && strncmp(name, "TANGENT", 7) == 0) + { + *out_type = cgltf_attribute_type_tangent; + } + else if (len == 8 && strncmp(name, "TEXCOORD", 8) == 0) + { + *out_type = cgltf_attribute_type_texcoord; + } + else if (len == 5 && strncmp(name, "COLOR", 5) == 0) + { + *out_type = cgltf_attribute_type_color; + } + else if (len == 6 && strncmp(name, "JOINTS", 6) == 0) + { + *out_type = cgltf_attribute_type_joints; + } + else if (len == 7 && strncmp(name, "WEIGHTS", 7) == 0) + { + *out_type = cgltf_attribute_type_weights; + } + else + { + *out_type = cgltf_attribute_type_invalid; + } + + if (us && *out_type != cgltf_attribute_type_invalid) + { + *out_index = CGLTF_ATOI(us + 1); + } +} + +static int cgltf_parse_json_attribute_list(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_attribute** out_attributes, cgltf_size* out_attributes_count) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + if (*out_attributes) + { + return CGLTF_ERROR_JSON; + } + + *out_attributes_count = tokens[i].size; + *out_attributes = (cgltf_attribute*)cgltf_calloc(options, sizeof(cgltf_attribute), *out_attributes_count); + ++i; + + if (!*out_attributes) + { + return CGLTF_ERROR_NOMEM; + } + + for (cgltf_size j = 0; j < *out_attributes_count; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + i = cgltf_parse_json_string(options, tokens, i, json_chunk, &(*out_attributes)[j].name); + if (i < 0) + { + return CGLTF_ERROR_JSON; + } + + cgltf_parse_attribute_type((*out_attributes)[j].name, &(*out_attributes)[j].type, &(*out_attributes)[j].index); + + (*out_attributes)[j].data = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + + return i; +} + +static int cgltf_parse_json_extras(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_extras* out_extras) +{ + (void)json_chunk; + out_extras->start_offset = tokens[i].start; + out_extras->end_offset = tokens[i].end; + i = cgltf_skip_json(tokens, i); + return i; +} + +static int cgltf_parse_json_unprocessed_extension(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_extension* out_extension) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_STRING); + CGLTF_CHECK_TOKTYPE(tokens[i+1], JSMN_OBJECT); + if (out_extension->name) + { + return CGLTF_ERROR_JSON; + } + + cgltf_size name_length = tokens[i].end - tokens[i].start; + out_extension->name = (char*)options->memory.alloc(options->memory.user_data, name_length + 1); + if (!out_extension->name) + { + return CGLTF_ERROR_NOMEM; + } + strncpy(out_extension->name, (const char*)json_chunk + tokens[i].start, name_length); + out_extension->name[name_length] = 0; + i++; + + size_t start = tokens[i].start; + size_t size = tokens[i].end - start; + out_extension->data = (char*)options->memory.alloc(options->memory.user_data, size + 1); + if (!out_extension->data) + { + return CGLTF_ERROR_NOMEM; + } + strncpy(out_extension->data, (const char*)json_chunk + start, size); + out_extension->data[size] = '\0'; + + i = cgltf_skip_json(tokens, i); + + return i; +} + +static int cgltf_parse_json_unprocessed_extensions(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_size* out_extensions_count, cgltf_extension** out_extensions) +{ + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + if(*out_extensions) + { + return CGLTF_ERROR_JSON; + } + + int extensions_size = tokens[i].size; + *out_extensions_count = 0; + *out_extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); + + if (!*out_extensions) + { + return CGLTF_ERROR_NOMEM; + } + + ++i; + + for (int j = 0; j < extensions_size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + cgltf_size extension_index = (*out_extensions_count)++; + cgltf_extension* extension = &((*out_extensions)[extension_index]); + i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, extension); + + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_draco_mesh_compression(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_draco_mesh_compression* out_draco_mesh_compression) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "attributes") == 0) + { + i = cgltf_parse_json_attribute_list(options, tokens, i + 1, json_chunk, &out_draco_mesh_compression->attributes, &out_draco_mesh_compression->attributes_count); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "bufferView") == 0) + { + ++i; + out_draco_mesh_compression->buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_material_mapping_data(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_material_mapping* out_mappings, cgltf_size* offset) +{ + (void)options; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_ARRAY); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int obj_size = tokens[i].size; + ++i; + + int material = -1; + int variants_tok = -1; + cgltf_extras extras = {0, 0}; + + for (int k = 0; k < obj_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "material") == 0) + { + ++i; + material = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "variants") == 0) + { + variants_tok = i+1; + CGLTF_CHECK_TOKTYPE(tokens[variants_tok], JSMN_ARRAY); + + i = cgltf_skip_json(tokens, i+1); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &extras); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + if (material < 0 || variants_tok < 0) + { + return CGLTF_ERROR_JSON; + } + + if (out_mappings) + { + for (int k = 0; k < tokens[variants_tok].size; ++k) + { + int variant = cgltf_json_to_int(&tokens[variants_tok + 1 + k], json_chunk); + if (variant < 0) + return variant; + + out_mappings[*offset].material = CGLTF_PTRINDEX(cgltf_material, material); + out_mappings[*offset].variant = variant; + out_mappings[*offset].extras = extras; + + (*offset)++; + } + } + else + { + (*offset) += tokens[variants_tok].size; + } + } + + return i; +} + +static int cgltf_parse_json_material_mappings(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_primitive* out_prim) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "mappings") == 0) + { + if (out_prim->mappings) + { + return CGLTF_ERROR_JSON; + } + + cgltf_size mappings_offset = 0; + int k = cgltf_parse_json_material_mapping_data(options, tokens, i + 1, json_chunk, NULL, &mappings_offset); + if (k < 0) + { + return k; + } + + out_prim->mappings_count = mappings_offset; + out_prim->mappings = (cgltf_material_mapping*)cgltf_calloc(options, sizeof(cgltf_material_mapping), out_prim->mappings_count); + + mappings_offset = 0; + i = cgltf_parse_json_material_mapping_data(options, tokens, i + 1, json_chunk, out_prim->mappings, &mappings_offset); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_primitive(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_primitive* out_prim) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + out_prim->type = cgltf_primitive_type_triangles; + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "mode") == 0) + { + ++i; + out_prim->type + = (cgltf_primitive_type) + cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "indices") == 0) + { + ++i; + out_prim->indices = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "material") == 0) + { + ++i; + out_prim->material = CGLTF_PTRINDEX(cgltf_material, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "attributes") == 0) + { + i = cgltf_parse_json_attribute_list(options, tokens, i + 1, json_chunk, &out_prim->attributes, &out_prim->attributes_count); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "targets") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_morph_target), (void**)&out_prim->targets, &out_prim->targets_count); + if (i < 0) + { + return i; + } + + for (cgltf_size k = 0; k < out_prim->targets_count; ++k) + { + i = cgltf_parse_json_attribute_list(options, tokens, i, json_chunk, &out_prim->targets[k].attributes, &out_prim->targets[k].attributes_count); + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_prim->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + if(out_prim->extensions) + { + return CGLTF_ERROR_JSON; + } + + int extensions_size = tokens[i].size; + out_prim->extensions_count = 0; + out_prim->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); + + if (!out_prim->extensions) + { + return CGLTF_ERROR_NOMEM; + } + + ++i; + for (int k = 0; k < extensions_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_draco_mesh_compression") == 0) + { + out_prim->has_draco_mesh_compression = 1; + i = cgltf_parse_json_draco_mesh_compression(options, tokens, i + 1, json_chunk, &out_prim->draco_mesh_compression); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_variants") == 0) + { + i = cgltf_parse_json_material_mappings(options, tokens, i + 1, json_chunk, out_prim); + } + else + { + i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_prim->extensions[out_prim->extensions_count++])); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_mesh(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_mesh* out_mesh) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_mesh->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "primitives") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_primitive), (void**)&out_mesh->primitives, &out_mesh->primitives_count); + if (i < 0) + { + return i; + } + + for (cgltf_size prim_index = 0; prim_index < out_mesh->primitives_count; ++prim_index) + { + i = cgltf_parse_json_primitive(options, tokens, i, json_chunk, &out_mesh->primitives[prim_index]); + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "weights") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_float), (void**)&out_mesh->weights, &out_mesh->weights_count); + if (i < 0) + { + return i; + } + + i = cgltf_parse_json_float_array(tokens, i - 1, json_chunk, out_mesh->weights, (int)out_mesh->weights_count); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + ++i; + + out_mesh->extras.start_offset = tokens[i].start; + out_mesh->extras.end_offset = tokens[i].end; + + if (tokens[i].type == JSMN_OBJECT) + { + int extras_size = tokens[i].size; + ++i; + + for (int k = 0; k < extras_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "targetNames") == 0 && tokens[i+1].type == JSMN_ARRAY) + { + i = cgltf_parse_json_string_array(options, tokens, i + 1, json_chunk, &out_mesh->target_names, &out_mesh->target_names_count); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i); + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_mesh->extensions_count, &out_mesh->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_meshes(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_mesh), (void**)&out_data->meshes, &out_data->meshes_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->meshes_count; ++j) + { + i = cgltf_parse_json_mesh(options, tokens, i, json_chunk, &out_data->meshes[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static cgltf_component_type cgltf_json_to_component_type(jsmntok_t const* tok, const uint8_t* json_chunk) +{ + int type = cgltf_json_to_int(tok, json_chunk); + + switch (type) + { + case 5120: + return cgltf_component_type_r_8; + case 5121: + return cgltf_component_type_r_8u; + case 5122: + return cgltf_component_type_r_16; + case 5123: + return cgltf_component_type_r_16u; + case 5125: + return cgltf_component_type_r_32u; + case 5126: + return cgltf_component_type_r_32f; + default: + return cgltf_component_type_invalid; + } +} + +static int cgltf_parse_json_accessor_sparse(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_accessor_sparse* out_sparse) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0) + { + ++i; + out_sparse->count = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "indices") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int indices_size = tokens[i].size; + ++i; + + for (int k = 0; k < indices_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0) + { + ++i; + out_sparse->indices_buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) + { + ++i; + out_sparse->indices_byte_offset = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "componentType") == 0) + { + ++i; + out_sparse->indices_component_type = cgltf_json_to_component_type(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sparse->indices_extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sparse->indices_extensions_count, &out_sparse->indices_extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "values") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int values_size = tokens[i].size; + ++i; + + for (int k = 0; k < values_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0) + { + ++i; + out_sparse->values_buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) + { + ++i; + out_sparse->values_byte_offset = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sparse->values_extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sparse->values_extensions_count, &out_sparse->values_extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sparse->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sparse->extensions_count, &out_sparse->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_accessor(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_accessor* out_accessor) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_accessor->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0) + { + ++i; + out_accessor->buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) + { + ++i; + out_accessor->offset = + cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "componentType") == 0) + { + ++i; + out_accessor->component_type = cgltf_json_to_component_type(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "normalized") == 0) + { + ++i; + out_accessor->normalized = cgltf_json_to_bool(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0) + { + ++i; + out_accessor->count = + cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "type") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens+i, json_chunk, "SCALAR") == 0) + { + out_accessor->type = cgltf_type_scalar; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC2") == 0) + { + out_accessor->type = cgltf_type_vec2; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC3") == 0) + { + out_accessor->type = cgltf_type_vec3; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "VEC4") == 0) + { + out_accessor->type = cgltf_type_vec4; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT2") == 0) + { + out_accessor->type = cgltf_type_mat2; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT3") == 0) + { + out_accessor->type = cgltf_type_mat3; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "MAT4") == 0) + { + out_accessor->type = cgltf_type_mat4; + } + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "min") == 0) + { + ++i; + out_accessor->has_min = 1; + // note: we can't parse the precise number of elements since type may not have been computed yet + int min_size = tokens[i].size > 16 ? 16 : tokens[i].size; + i = cgltf_parse_json_float_array(tokens, i, json_chunk, out_accessor->min, min_size); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "max") == 0) + { + ++i; + out_accessor->has_max = 1; + // note: we can't parse the precise number of elements since type may not have been computed yet + int max_size = tokens[i].size > 16 ? 16 : tokens[i].size; + i = cgltf_parse_json_float_array(tokens, i, json_chunk, out_accessor->max, max_size); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "sparse") == 0) + { + out_accessor->is_sparse = 1; + i = cgltf_parse_json_accessor_sparse(options, tokens, i + 1, json_chunk, &out_accessor->sparse); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_accessor->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_accessor->extensions_count, &out_accessor->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_texture_transform(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_texture_transform* out_texture_transform) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "offset") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_texture_transform->offset, 2); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "rotation") == 0) + { + ++i; + out_texture_transform->rotation = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "scale") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_texture_transform->scale, 2); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "texCoord") == 0) + { + ++i; + out_texture_transform->texcoord = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_texture_view(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_texture_view* out_texture_view) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + out_texture_view->scale = 1.0f; + cgltf_fill_float_array(out_texture_view->transform.scale, 2, 1.0f); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "index") == 0) + { + ++i; + out_texture_view->texture = CGLTF_PTRINDEX(cgltf_texture, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "texCoord") == 0) + { + ++i; + out_texture_view->texcoord = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "scale") == 0) + { + ++i; + out_texture_view->scale = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "strength") == 0) + { + ++i; + out_texture_view->scale = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_texture_view->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + if(out_texture_view->extensions) + { + return CGLTF_ERROR_JSON; + } + + int extensions_size = tokens[i].size; + out_texture_view->extensions_count = 0; + out_texture_view->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); + + if (!out_texture_view->extensions) + { + return CGLTF_ERROR_NOMEM; + } + + ++i; + + for (int k = 0; k < extensions_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_texture_transform") == 0) + { + out_texture_view->has_transform = 1; + i = cgltf_parse_json_texture_transform(tokens, i + 1, json_chunk, &out_texture_view->transform); + } + else + { + i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_texture_view->extensions[out_texture_view->extensions_count++])); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_pbr_metallic_roughness(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_pbr_metallic_roughness* out_pbr) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "metallicFactor") == 0) + { + ++i; + out_pbr->metallic_factor = + cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "roughnessFactor") == 0) + { + ++i; + out_pbr->roughness_factor = + cgltf_json_to_float(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "baseColorFactor") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_pbr->base_color_factor, 4); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "baseColorTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, + &out_pbr->base_color_texture); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "metallicRoughnessTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, + &out_pbr->metallic_roughness_texture); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_pbr->extras); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_pbr_specular_glossiness(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_pbr_specular_glossiness* out_pbr) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "diffuseFactor") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_pbr->diffuse_factor, 4); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularFactor") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_pbr->specular_factor, 3); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "glossinessFactor") == 0) + { + ++i; + out_pbr->glossiness_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "diffuseTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_pbr->diffuse_texture); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularGlossinessTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_pbr->specular_glossiness_texture); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_clearcoat(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_clearcoat* out_clearcoat) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatFactor") == 0) + { + ++i; + out_clearcoat->clearcoat_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatRoughnessFactor") == 0) + { + ++i; + out_clearcoat->clearcoat_roughness_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_clearcoat->clearcoat_texture); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatRoughnessTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_clearcoat->clearcoat_roughness_texture); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "clearcoatNormalTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_clearcoat->clearcoat_normal_texture); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_ior(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_ior* out_ior) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + // Default values + out_ior->ior = 1.5f; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "ior") == 0) + { + ++i; + out_ior->ior = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_specular(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_specular* out_specular) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + // Default values + out_specular->specular_factor = 1.0f; + cgltf_fill_float_array(out_specular->specular_color_factor, 3, 1.0f); + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "specularFactor") == 0) + { + ++i; + out_specular->specular_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularColorFactor") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_specular->specular_color_factor, 3); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "specularTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_specular->specular_texture); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "specularColorTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_specular->specular_color_texture); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_transmission(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_transmission* out_transmission) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "transmissionFactor") == 0) + { + ++i; + out_transmission->transmission_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "transmissionTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_transmission->transmission_texture); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_volume(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_volume* out_volume) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "thicknessFactor") == 0) + { + ++i; + out_volume->thickness_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "thicknessTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_volume->thickness_texture); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "attenuationColor") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_volume->attenuation_color, 3); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "attenuationDistance") == 0) + { + ++i; + out_volume->attenuation_distance = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_sheen(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_sheen* out_sheen) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "sheenColorFactor") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_sheen->sheen_color_factor, 3); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "sheenColorTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_sheen->sheen_color_texture); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "sheenRoughnessFactor") == 0) + { + ++i; + out_sheen->sheen_roughness_factor = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "sheenRoughnessTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_sheen->sheen_roughness_texture); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_image(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_image* out_image) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "uri") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_image->uri); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "bufferView") == 0) + { + ++i; + out_image->buffer_view = CGLTF_PTRINDEX(cgltf_buffer_view, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "mimeType") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_image->mime_type); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_image->name); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_image->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_image->extensions_count, &out_image->extensions); + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_sampler(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_sampler* out_sampler) +{ + (void)options; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + out_sampler->wrap_s = 10497; + out_sampler->wrap_t = 10497; + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_sampler->name); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "magFilter") == 0) + { + ++i; + out_sampler->mag_filter + = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "minFilter") == 0) + { + ++i; + out_sampler->min_filter + = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapS") == 0) + { + ++i; + out_sampler->wrap_s + = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "wrapT") == 0) + { + ++i; + out_sampler->wrap_t + = cgltf_json_to_int(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sampler->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sampler->extensions_count, &out_sampler->extensions); + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_texture(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_texture* out_texture) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_texture->name); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "sampler") == 0) + { + ++i; + out_texture->sampler = CGLTF_PTRINDEX(cgltf_sampler, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "source") == 0) + { + ++i; + out_texture->image = CGLTF_PTRINDEX(cgltf_image, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_texture->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_texture->extensions_count, &out_texture->extensions); + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_material(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_material* out_material) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + cgltf_fill_float_array(out_material->pbr_metallic_roughness.base_color_factor, 4, 1.0f); + out_material->pbr_metallic_roughness.metallic_factor = 1.0f; + out_material->pbr_metallic_roughness.roughness_factor = 1.0f; + + cgltf_fill_float_array(out_material->pbr_specular_glossiness.diffuse_factor, 4, 1.0f); + cgltf_fill_float_array(out_material->pbr_specular_glossiness.specular_factor, 3, 1.0f); + out_material->pbr_specular_glossiness.glossiness_factor = 1.0f; + + cgltf_fill_float_array(out_material->volume.attenuation_color, 3, 1.0f); + out_material->volume.attenuation_distance = FLT_MAX; + + out_material->alpha_cutoff = 0.5f; + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_material->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "pbrMetallicRoughness") == 0) + { + out_material->has_pbr_metallic_roughness = 1; + i = cgltf_parse_json_pbr_metallic_roughness(options, tokens, i + 1, json_chunk, &out_material->pbr_metallic_roughness); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "emissiveFactor") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_material->emissive_factor, 3); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "normalTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, + &out_material->normal_texture); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "occlusionTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, + &out_material->occlusion_texture); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "emissiveTexture") == 0) + { + i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, + &out_material->emissive_texture); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "alphaMode") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens + i, json_chunk, "OPAQUE") == 0) + { + out_material->alpha_mode = cgltf_alpha_mode_opaque; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "MASK") == 0) + { + out_material->alpha_mode = cgltf_alpha_mode_mask; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "BLEND") == 0) + { + out_material->alpha_mode = cgltf_alpha_mode_blend; + } + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "alphaCutoff") == 0) + { + ++i; + out_material->alpha_cutoff = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "doubleSided") == 0) + { + ++i; + out_material->double_sided = + cgltf_json_to_bool(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_material->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + if(out_material->extensions) + { + return CGLTF_ERROR_JSON; + } + + int extensions_size = tokens[i].size; + ++i; + out_material->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); + out_material->extensions_count= 0; + + if (!out_material->extensions) + { + return CGLTF_ERROR_NOMEM; + } + + for (int k = 0; k < extensions_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_pbrSpecularGlossiness") == 0) + { + out_material->has_pbr_specular_glossiness = 1; + i = cgltf_parse_json_pbr_specular_glossiness(options, tokens, i + 1, json_chunk, &out_material->pbr_specular_glossiness); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_unlit") == 0) + { + out_material->unlit = 1; + i = cgltf_skip_json(tokens, i+1); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_clearcoat") == 0) + { + out_material->has_clearcoat = 1; + i = cgltf_parse_json_clearcoat(options, tokens, i + 1, json_chunk, &out_material->clearcoat); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_ior") == 0) + { + out_material->has_ior = 1; + i = cgltf_parse_json_ior(tokens, i + 1, json_chunk, &out_material->ior); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_specular") == 0) + { + out_material->has_specular = 1; + i = cgltf_parse_json_specular(options, tokens, i + 1, json_chunk, &out_material->specular); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_transmission") == 0) + { + out_material->has_transmission = 1; + i = cgltf_parse_json_transmission(options, tokens, i + 1, json_chunk, &out_material->transmission); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "KHR_materials_volume") == 0) + { + out_material->has_volume = 1; + i = cgltf_parse_json_volume(options, tokens, i + 1, json_chunk, &out_material->volume); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_sheen") == 0) + { + out_material->has_sheen = 1; + i = cgltf_parse_json_sheen(options, tokens, i + 1, json_chunk, &out_material->sheen); + } + else + { + i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_material->extensions[out_material->extensions_count++])); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_accessors(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_accessor), (void**)&out_data->accessors, &out_data->accessors_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->accessors_count; ++j) + { + i = cgltf_parse_json_accessor(options, tokens, i, json_chunk, &out_data->accessors[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_materials(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_material), (void**)&out_data->materials, &out_data->materials_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->materials_count; ++j) + { + i = cgltf_parse_json_material(options, tokens, i, json_chunk, &out_data->materials[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_images(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_image), (void**)&out_data->images, &out_data->images_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->images_count; ++j) + { + i = cgltf_parse_json_image(options, tokens, i, json_chunk, &out_data->images[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_textures(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_texture), (void**)&out_data->textures, &out_data->textures_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->textures_count; ++j) + { + i = cgltf_parse_json_texture(options, tokens, i, json_chunk, &out_data->textures[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_samplers(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_sampler), (void**)&out_data->samplers, &out_data->samplers_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->samplers_count; ++j) + { + i = cgltf_parse_json_sampler(options, tokens, i, json_chunk, &out_data->samplers[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_meshopt_compression(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_meshopt_compression* out_meshopt_compression) +{ + (void)options; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "buffer") == 0) + { + ++i; + out_meshopt_compression->buffer = CGLTF_PTRINDEX(cgltf_buffer, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) + { + ++i; + out_meshopt_compression->offset = cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0) + { + ++i; + out_meshopt_compression->size = cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteStride") == 0) + { + ++i; + out_meshopt_compression->stride = cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "count") == 0) + { + ++i; + out_meshopt_compression->count = cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "mode") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens+i, json_chunk, "ATTRIBUTES") == 0) + { + out_meshopt_compression->mode = cgltf_meshopt_compression_mode_attributes; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "TRIANGLES") == 0) + { + out_meshopt_compression->mode = cgltf_meshopt_compression_mode_triangles; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "INDICES") == 0) + { + out_meshopt_compression->mode = cgltf_meshopt_compression_mode_indices; + } + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "filter") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens+i, json_chunk, "NONE") == 0) + { + out_meshopt_compression->filter = cgltf_meshopt_compression_filter_none; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "OCTAHEDRAL") == 0) + { + out_meshopt_compression->filter = cgltf_meshopt_compression_filter_octahedral; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "QUATERNION") == 0) + { + out_meshopt_compression->filter = cgltf_meshopt_compression_filter_quaternion; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "EXPONENTIAL") == 0) + { + out_meshopt_compression->filter = cgltf_meshopt_compression_filter_exponential; + } + ++i; + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_buffer_view(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_buffer_view* out_buffer_view) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_buffer_view->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "buffer") == 0) + { + ++i; + out_buffer_view->buffer = CGLTF_PTRINDEX(cgltf_buffer, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteOffset") == 0) + { + ++i; + out_buffer_view->offset = + cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0) + { + ++i; + out_buffer_view->size = + cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteStride") == 0) + { + ++i; + out_buffer_view->stride = + cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "target") == 0) + { + ++i; + int type = cgltf_json_to_int(tokens+i, json_chunk); + switch (type) + { + case 34962: + type = cgltf_buffer_view_type_vertices; + break; + case 34963: + type = cgltf_buffer_view_type_indices; + break; + default: + type = cgltf_buffer_view_type_invalid; + break; + } + out_buffer_view->type = (cgltf_buffer_view_type)type; + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_buffer_view->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + if(out_buffer_view->extensions) + { + return CGLTF_ERROR_JSON; + } + + int extensions_size = tokens[i].size; + out_buffer_view->extensions_count = 0; + out_buffer_view->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); + + if (!out_buffer_view->extensions) + { + return CGLTF_ERROR_NOMEM; + } + + ++i; + for (int k = 0; k < extensions_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "EXT_meshopt_compression") == 0) + { + out_buffer_view->has_meshopt_compression = 1; + i = cgltf_parse_json_meshopt_compression(options, tokens, i + 1, json_chunk, &out_buffer_view->meshopt_compression); + } + else + { + i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_buffer_view->extensions[out_buffer_view->extensions_count++])); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_buffer_views(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_buffer_view), (void**)&out_data->buffer_views, &out_data->buffer_views_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->buffer_views_count; ++j) + { + i = cgltf_parse_json_buffer_view(options, tokens, i, json_chunk, &out_data->buffer_views[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_buffer(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_buffer* out_buffer) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_buffer->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "byteLength") == 0) + { + ++i; + out_buffer->size = + cgltf_json_to_int(tokens+i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "uri") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_buffer->uri); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_buffer->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_buffer->extensions_count, &out_buffer->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_buffers(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_buffer), (void**)&out_data->buffers, &out_data->buffers_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->buffers_count; ++j) + { + i = cgltf_parse_json_buffer(options, tokens, i, json_chunk, &out_data->buffers[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_skin(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_skin* out_skin) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_skin->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "joints") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_node*), (void**)&out_skin->joints, &out_skin->joints_count); + if (i < 0) + { + return i; + } + + for (cgltf_size k = 0; k < out_skin->joints_count; ++k) + { + out_skin->joints[k] = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "skeleton") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); + out_skin->skeleton = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "inverseBindMatrices") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); + out_skin->inverse_bind_matrices = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_skin->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_skin->extensions_count, &out_skin->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_skins(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_skin), (void**)&out_data->skins, &out_data->skins_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->skins_count; ++j) + { + i = cgltf_parse_json_skin(options, tokens, i, json_chunk, &out_data->skins[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_camera* out_camera) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_camera->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "type") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens + i, json_chunk, "perspective") == 0) + { + out_camera->type = cgltf_camera_type_perspective; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "orthographic") == 0) + { + out_camera->type = cgltf_camera_type_orthographic; + } + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "perspective") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int data_size = tokens[i].size; + ++i; + + out_camera->type = cgltf_camera_type_perspective; + + for (int k = 0; k < data_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "aspectRatio") == 0) + { + ++i; + out_camera->data.perspective.has_aspect_ratio = 1; + out_camera->data.perspective.aspect_ratio = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "yfov") == 0) + { + ++i; + out_camera->data.perspective.yfov = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "zfar") == 0) + { + ++i; + out_camera->data.perspective.has_zfar = 1; + out_camera->data.perspective.zfar = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "znear") == 0) + { + ++i; + out_camera->data.perspective.znear = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_camera->data.perspective.extras); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "orthographic") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int data_size = tokens[i].size; + ++i; + + out_camera->type = cgltf_camera_type_orthographic; + + for (int k = 0; k < data_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "xmag") == 0) + { + ++i; + out_camera->data.orthographic.xmag = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "ymag") == 0) + { + ++i; + out_camera->data.orthographic.ymag = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "zfar") == 0) + { + ++i; + out_camera->data.orthographic.zfar = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "znear") == 0) + { + ++i; + out_camera->data.orthographic.znear = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_camera->data.orthographic.extras); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_camera->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_camera->extensions_count, &out_camera->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_cameras(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_camera), (void**)&out_data->cameras, &out_data->cameras_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->cameras_count; ++j) + { + i = cgltf_parse_json_camera(options, tokens, i, json_chunk, &out_data->cameras[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_light(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_light* out_light) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_light->name); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "color") == 0) + { + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_light->color, 3); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "intensity") == 0) + { + ++i; + out_light->intensity = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "type") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens + i, json_chunk, "directional") == 0) + { + out_light->type = cgltf_light_type_directional; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "point") == 0) + { + out_light->type = cgltf_light_type_point; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "spot") == 0) + { + out_light->type = cgltf_light_type_spot; + } + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "range") == 0) + { + ++i; + out_light->range = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "spot") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int data_size = tokens[i].size; + ++i; + + for (int k = 0; k < data_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "innerConeAngle") == 0) + { + ++i; + out_light->spot_inner_cone_angle = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "outerConeAngle") == 0) + { + ++i; + out_light->spot_outer_cone_angle = cgltf_json_to_float(tokens + i, json_chunk); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_lights(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_light), (void**)&out_data->lights, &out_data->lights_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->lights_count; ++j) + { + i = cgltf_parse_json_light(options, tokens, i, json_chunk, &out_data->lights[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_node(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_node* out_node) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + out_node->rotation[3] = 1.0f; + out_node->scale[0] = 1.0f; + out_node->scale[1] = 1.0f; + out_node->scale[2] = 1.0f; + out_node->matrix[0] = 1.0f; + out_node->matrix[5] = 1.0f; + out_node->matrix[10] = 1.0f; + out_node->matrix[15] = 1.0f; + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_node->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "children") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_node*), (void**)&out_node->children, &out_node->children_count); + if (i < 0) + { + return i; + } + + for (cgltf_size k = 0; k < out_node->children_count; ++k) + { + out_node->children[k] = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "mesh") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); + out_node->mesh = CGLTF_PTRINDEX(cgltf_mesh, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "skin") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); + out_node->skin = CGLTF_PTRINDEX(cgltf_skin, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "camera") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); + out_node->camera = CGLTF_PTRINDEX(cgltf_camera, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "translation") == 0) + { + out_node->has_translation = 1; + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->translation, 3); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "rotation") == 0) + { + out_node->has_rotation = 1; + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->rotation, 4); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "scale") == 0) + { + out_node->has_scale = 1; + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->scale, 3); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "matrix") == 0) + { + out_node->has_matrix = 1; + i = cgltf_parse_json_float_array(tokens, i + 1, json_chunk, out_node->matrix, 16); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "weights") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_float), (void**)&out_node->weights, &out_node->weights_count); + if (i < 0) + { + return i; + } + + i = cgltf_parse_json_float_array(tokens, i - 1, json_chunk, out_node->weights, (int)out_node->weights_count); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_node->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + if(out_node->extensions) + { + return CGLTF_ERROR_JSON; + } + + int extensions_size = tokens[i].size; + out_node->extensions_count= 0; + out_node->extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); + + if (!out_node->extensions) + { + return CGLTF_ERROR_NOMEM; + } + + ++i; + + for (int k = 0; k < extensions_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_lights_punctual") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int data_size = tokens[i].size; + ++i; + + for (int m = 0; m < data_size; ++m) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "light") == 0) + { + ++i; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); + out_node->light = CGLTF_PTRINDEX(cgltf_light, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_node->extensions[out_node->extensions_count++])); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_nodes(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_node), (void**)&out_data->nodes, &out_data->nodes_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->nodes_count; ++j) + { + i = cgltf_parse_json_node(options, tokens, i, json_chunk, &out_data->nodes[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_scene(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_scene* out_scene) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_scene->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "nodes") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_node*), (void**)&out_scene->nodes, &out_scene->nodes_count); + if (i < 0) + { + return i; + } + + for (cgltf_size k = 0; k < out_scene->nodes_count; ++k) + { + out_scene->nodes[k] = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_scene->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_scene->extensions_count, &out_scene->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_scenes(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_scene), (void**)&out_data->scenes, &out_data->scenes_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->scenes_count; ++j) + { + i = cgltf_parse_json_scene(options, tokens, i, json_chunk, &out_data->scenes[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_animation_sampler(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_animation_sampler* out_sampler) +{ + (void)options; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "input") == 0) + { + ++i; + out_sampler->input = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "output") == 0) + { + ++i; + out_sampler->output = CGLTF_PTRINDEX(cgltf_accessor, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "interpolation") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens + i, json_chunk, "LINEAR") == 0) + { + out_sampler->interpolation = cgltf_interpolation_type_linear; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "STEP") == 0) + { + out_sampler->interpolation = cgltf_interpolation_type_step; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "CUBICSPLINE") == 0) + { + out_sampler->interpolation = cgltf_interpolation_type_cubic_spline; + } + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sampler->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_sampler->extensions_count, &out_sampler->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_animation_channel(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_animation_channel* out_channel) +{ + (void)options; + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "sampler") == 0) + { + ++i; + out_channel->sampler = CGLTF_PTRINDEX(cgltf_animation_sampler, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "target") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int target_size = tokens[i].size; + ++i; + + for (int k = 0; k < target_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "node") == 0) + { + ++i; + out_channel->target_node = CGLTF_PTRINDEX(cgltf_node, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "path") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens+i, json_chunk, "translation") == 0) + { + out_channel->target_path = cgltf_animation_path_type_translation; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "rotation") == 0) + { + out_channel->target_path = cgltf_animation_path_type_rotation; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "scale") == 0) + { + out_channel->target_path = cgltf_animation_path_type_scale; + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "weights") == 0) + { + out_channel->target_path = cgltf_animation_path_type_weights; + } + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_channel->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_channel->extensions_count, &out_channel->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_animation(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_animation* out_animation) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_animation->name); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "samplers") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_animation_sampler), (void**)&out_animation->samplers, &out_animation->samplers_count); + if (i < 0) + { + return i; + } + + for (cgltf_size k = 0; k < out_animation->samplers_count; ++k) + { + i = cgltf_parse_json_animation_sampler(options, tokens, i, json_chunk, &out_animation->samplers[k]); + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "channels") == 0) + { + i = cgltf_parse_json_array(options, tokens, i + 1, json_chunk, sizeof(cgltf_animation_channel), (void**)&out_animation->channels, &out_animation->channels_count); + if (i < 0) + { + return i; + } + + for (cgltf_size k = 0; k < out_animation->channels_count; ++k) + { + i = cgltf_parse_json_animation_channel(options, tokens, i, json_chunk, &out_animation->channels[k]); + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_animation->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_animation->extensions_count, &out_animation->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_animations(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_animation), (void**)&out_data->animations, &out_data->animations_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->animations_count; ++j) + { + i = cgltf_parse_json_animation(options, tokens, i, json_chunk, &out_data->animations[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_variant(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_material_variant* out_variant) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "name") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_variant->name); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_variant->extras); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +static int cgltf_parse_json_variants(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + i = cgltf_parse_json_array(options, tokens, i, json_chunk, sizeof(cgltf_material_variant), (void**)&out_data->variants, &out_data->variants_count); + if (i < 0) + { + return i; + } + + for (cgltf_size j = 0; j < out_data->variants_count; ++j) + { + i = cgltf_parse_json_variant(options, tokens, i, json_chunk, &out_data->variants[j]); + if (i < 0) + { + return i; + } + } + return i; +} + +static int cgltf_parse_json_asset(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_asset* out_asset) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "copyright") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->copyright); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "generator") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->generator); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "version") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->version); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "minVersion") == 0) + { + i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_asset->min_version); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_asset->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + i = cgltf_parse_json_unprocessed_extensions(options, tokens, i, json_chunk, &out_asset->extensions_count, &out_asset->extensions); + } + else + { + i = cgltf_skip_json(tokens, i+1); + } + + if (i < 0) + { + return i; + } + } + + if (out_asset->version && CGLTF_ATOF(out_asset->version) < 2) + { + return CGLTF_ERROR_LEGACY; + } + + return i; +} + +cgltf_size cgltf_num_components(cgltf_type type) { + switch (type) + { + case cgltf_type_vec2: + return 2; + case cgltf_type_vec3: + return 3; + case cgltf_type_vec4: + return 4; + case cgltf_type_mat2: + return 4; + case cgltf_type_mat3: + return 9; + case cgltf_type_mat4: + return 16; + case cgltf_type_invalid: + case cgltf_type_scalar: + default: + return 1; + } +} + +static cgltf_size cgltf_component_size(cgltf_component_type component_type) { + switch (component_type) + { + case cgltf_component_type_r_8: + case cgltf_component_type_r_8u: + return 1; + case cgltf_component_type_r_16: + case cgltf_component_type_r_16u: + return 2; + case cgltf_component_type_r_32u: + case cgltf_component_type_r_32f: + return 4; + case cgltf_component_type_invalid: + default: + return 0; + } +} + +static cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type) +{ + cgltf_size component_size = cgltf_component_size(component_type); + if (type == cgltf_type_mat2 && component_size == 1) + { + return 8 * component_size; + } + else if (type == cgltf_type_mat3 && (component_size == 1 || component_size == 2)) + { + return 12 * component_size; + } + return component_size * cgltf_num_components(type); +} + +static int cgltf_fixup_pointers(cgltf_data* out_data); + +static int cgltf_parse_json_root(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_data* out_data) +{ + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int size = tokens[i].size; + ++i; + + for (int j = 0; j < size; ++j) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "asset") == 0) + { + i = cgltf_parse_json_asset(options, tokens, i + 1, json_chunk, &out_data->asset); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "meshes") == 0) + { + i = cgltf_parse_json_meshes(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "accessors") == 0) + { + i = cgltf_parse_json_accessors(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "bufferViews") == 0) + { + i = cgltf_parse_json_buffer_views(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "buffers") == 0) + { + i = cgltf_parse_json_buffers(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "materials") == 0) + { + i = cgltf_parse_json_materials(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "images") == 0) + { + i = cgltf_parse_json_images(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "textures") == 0) + { + i = cgltf_parse_json_textures(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "samplers") == 0) + { + i = cgltf_parse_json_samplers(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "skins") == 0) + { + i = cgltf_parse_json_skins(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "cameras") == 0) + { + i = cgltf_parse_json_cameras(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "nodes") == 0) + { + i = cgltf_parse_json_nodes(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "scenes") == 0) + { + i = cgltf_parse_json_scenes(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "scene") == 0) + { + ++i; + out_data->scene = CGLTF_PTRINDEX(cgltf_scene, cgltf_json_to_int(tokens + i, json_chunk)); + ++i; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "animations") == 0) + { + i = cgltf_parse_json_animations(options, tokens, i + 1, json_chunk, out_data); + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_data->extras); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + if(out_data->data_extensions) + { + return CGLTF_ERROR_JSON; + } + + int extensions_size = tokens[i].size; + out_data->data_extensions_count = 0; + out_data->data_extensions = (cgltf_extension*)cgltf_calloc(options, sizeof(cgltf_extension), extensions_size); + + if (!out_data->data_extensions) + { + return CGLTF_ERROR_NOMEM; + } + + ++i; + + for (int k = 0; k < extensions_size; ++k) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_lights_punctual") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int data_size = tokens[i].size; + ++i; + + for (int m = 0; m < data_size; ++m) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "lights") == 0) + { + i = cgltf_parse_json_lights(options, tokens, i + 1, json_chunk, out_data); + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "KHR_materials_variants") == 0) + { + ++i; + + CGLTF_CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + + int data_size = tokens[i].size; + ++i; + + for (int m = 0; m < data_size; ++m) + { + CGLTF_CHECK_KEY(tokens[i]); + + if (cgltf_json_strcmp(tokens + i, json_chunk, "variants") == 0) + { + i = cgltf_parse_json_variants(options, tokens, i + 1, json_chunk, out_data); + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + } + else + { + i = cgltf_parse_json_unprocessed_extension(options, tokens, i, json_chunk, &(out_data->data_extensions[out_data->data_extensions_count++])); + } + + if (i < 0) + { + return i; + } + } + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensionsUsed") == 0) + { + i = cgltf_parse_json_string_array(options, tokens, i + 1, json_chunk, &out_data->extensions_used, &out_data->extensions_used_count); + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensionsRequired") == 0) + { + i = cgltf_parse_json_string_array(options, tokens, i + 1, json_chunk, &out_data->extensions_required, &out_data->extensions_required_count); + } + else + { + i = cgltf_skip_json(tokens, i + 1); + } + + if (i < 0) + { + return i; + } + } + + return i; +} + +cgltf_result cgltf_parse_json(cgltf_options* options, const uint8_t* json_chunk, cgltf_size size, cgltf_data** out_data) +{ + jsmn_parser parser = { 0, 0, 0 }; + + if (options->json_token_count == 0) + { + int token_count = jsmn_parse(&parser, (const char*)json_chunk, size, NULL, 0); + + if (token_count <= 0) + { + return cgltf_result_invalid_json; + } + + options->json_token_count = token_count; + } + + jsmntok_t* tokens = (jsmntok_t*)options->memory.alloc(options->memory.user_data, sizeof(jsmntok_t) * (options->json_token_count + 1)); + + if (!tokens) + { + return cgltf_result_out_of_memory; + } + + jsmn_init(&parser); + + int token_count = jsmn_parse(&parser, (const char*)json_chunk, size, tokens, options->json_token_count); + + if (token_count <= 0) + { + options->memory.free(options->memory.user_data, tokens); + return cgltf_result_invalid_json; + } + + // this makes sure that we always have an UNDEFINED token at the end of the stream + // for invalid JSON inputs this makes sure we don't perform out of bound reads of token data + tokens[token_count].type = JSMN_UNDEFINED; + + cgltf_data* data = (cgltf_data*)options->memory.alloc(options->memory.user_data, sizeof(cgltf_data)); + + if (!data) + { + options->memory.free(options->memory.user_data, tokens); + return cgltf_result_out_of_memory; + } + + memset(data, 0, sizeof(cgltf_data)); + data->memory = options->memory; + data->file = options->file; + + int i = cgltf_parse_json_root(options, tokens, 0, json_chunk, data); + + options->memory.free(options->memory.user_data, tokens); + + if (i < 0) + { + cgltf_free(data); + + switch (i) + { + case CGLTF_ERROR_NOMEM: return cgltf_result_out_of_memory; + case CGLTF_ERROR_LEGACY: return cgltf_result_legacy_gltf; + default: return cgltf_result_invalid_gltf; + } + } + + if (cgltf_fixup_pointers(data) < 0) + { + cgltf_free(data); + return cgltf_result_invalid_gltf; + } + + data->json = (const char*)json_chunk; + data->json_size = size; + + *out_data = data; + + return cgltf_result_success; +} + +static int cgltf_fixup_pointers(cgltf_data* data) +{ + for (cgltf_size i = 0; i < data->meshes_count; ++i) + { + for (cgltf_size j = 0; j < data->meshes[i].primitives_count; ++j) + { + CGLTF_PTRFIXUP(data->meshes[i].primitives[j].indices, data->accessors, data->accessors_count); + CGLTF_PTRFIXUP(data->meshes[i].primitives[j].material, data->materials, data->materials_count); + + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].attributes_count; ++k) + { + CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].attributes[k].data, data->accessors, data->accessors_count); + } + + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].targets_count; ++k) + { + for (cgltf_size m = 0; m < data->meshes[i].primitives[j].targets[k].attributes_count; ++m) + { + CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].targets[k].attributes[m].data, data->accessors, data->accessors_count); + } + } + + if (data->meshes[i].primitives[j].has_draco_mesh_compression) + { + CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].draco_mesh_compression.buffer_view, data->buffer_views, data->buffer_views_count); + for (cgltf_size m = 0; m < data->meshes[i].primitives[j].draco_mesh_compression.attributes_count; ++m) + { + CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].draco_mesh_compression.attributes[m].data, data->accessors, data->accessors_count); + } + } + + for (cgltf_size k = 0; k < data->meshes[i].primitives[j].mappings_count; ++k) + { + CGLTF_PTRFIXUP_REQ(data->meshes[i].primitives[j].mappings[k].material, data->materials, data->materials_count); + } + } + } + + for (cgltf_size i = 0; i < data->accessors_count; ++i) + { + CGLTF_PTRFIXUP(data->accessors[i].buffer_view, data->buffer_views, data->buffer_views_count); + + if (data->accessors[i].is_sparse) + { + CGLTF_PTRFIXUP_REQ(data->accessors[i].sparse.indices_buffer_view, data->buffer_views, data->buffer_views_count); + CGLTF_PTRFIXUP_REQ(data->accessors[i].sparse.values_buffer_view, data->buffer_views, data->buffer_views_count); + } + + if (data->accessors[i].buffer_view) + { + data->accessors[i].stride = data->accessors[i].buffer_view->stride; + } + + if (data->accessors[i].stride == 0) + { + data->accessors[i].stride = cgltf_calc_size(data->accessors[i].type, data->accessors[i].component_type); + } + } + + for (cgltf_size i = 0; i < data->textures_count; ++i) + { + CGLTF_PTRFIXUP(data->textures[i].image, data->images, data->images_count); + CGLTF_PTRFIXUP(data->textures[i].sampler, data->samplers, data->samplers_count); + } + + for (cgltf_size i = 0; i < data->images_count; ++i) + { + CGLTF_PTRFIXUP(data->images[i].buffer_view, data->buffer_views, data->buffer_views_count); + } + + for (cgltf_size i = 0; i < data->materials_count; ++i) + { + CGLTF_PTRFIXUP(data->materials[i].normal_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].emissive_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].occlusion_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].pbr_metallic_roughness.base_color_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].pbr_specular_glossiness.diffuse_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].pbr_specular_glossiness.specular_glossiness_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].clearcoat.clearcoat_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].clearcoat.clearcoat_roughness_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].clearcoat.clearcoat_normal_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].specular.specular_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].specular.specular_color_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].transmission.transmission_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].volume.thickness_texture.texture, data->textures, data->textures_count); + + CGLTF_PTRFIXUP(data->materials[i].sheen.sheen_color_texture.texture, data->textures, data->textures_count); + CGLTF_PTRFIXUP(data->materials[i].sheen.sheen_roughness_texture.texture, data->textures, data->textures_count); + } + + for (cgltf_size i = 0; i < data->buffer_views_count; ++i) + { + CGLTF_PTRFIXUP_REQ(data->buffer_views[i].buffer, data->buffers, data->buffers_count); + + if (data->buffer_views[i].has_meshopt_compression) + { + CGLTF_PTRFIXUP_REQ(data->buffer_views[i].meshopt_compression.buffer, data->buffers, data->buffers_count); + } + } + + for (cgltf_size i = 0; i < data->skins_count; ++i) + { + for (cgltf_size j = 0; j < data->skins[i].joints_count; ++j) + { + CGLTF_PTRFIXUP_REQ(data->skins[i].joints[j], data->nodes, data->nodes_count); + } + + CGLTF_PTRFIXUP(data->skins[i].skeleton, data->nodes, data->nodes_count); + CGLTF_PTRFIXUP(data->skins[i].inverse_bind_matrices, data->accessors, data->accessors_count); + } + + for (cgltf_size i = 0; i < data->nodes_count; ++i) + { + for (cgltf_size j = 0; j < data->nodes[i].children_count; ++j) + { + CGLTF_PTRFIXUP_REQ(data->nodes[i].children[j], data->nodes, data->nodes_count); + + if (data->nodes[i].children[j]->parent) + { + return CGLTF_ERROR_JSON; + } + + data->nodes[i].children[j]->parent = &data->nodes[i]; + } + + CGLTF_PTRFIXUP(data->nodes[i].mesh, data->meshes, data->meshes_count); + CGLTF_PTRFIXUP(data->nodes[i].skin, data->skins, data->skins_count); + CGLTF_PTRFIXUP(data->nodes[i].camera, data->cameras, data->cameras_count); + CGLTF_PTRFIXUP(data->nodes[i].light, data->lights, data->lights_count); + } + + for (cgltf_size i = 0; i < data->scenes_count; ++i) + { + for (cgltf_size j = 0; j < data->scenes[i].nodes_count; ++j) + { + CGLTF_PTRFIXUP_REQ(data->scenes[i].nodes[j], data->nodes, data->nodes_count); + + if (data->scenes[i].nodes[j]->parent) + { + return CGLTF_ERROR_JSON; + } + } + } + + CGLTF_PTRFIXUP(data->scene, data->scenes, data->scenes_count); + + for (cgltf_size i = 0; i < data->animations_count; ++i) + { + for (cgltf_size j = 0; j < data->animations[i].samplers_count; ++j) + { + CGLTF_PTRFIXUP_REQ(data->animations[i].samplers[j].input, data->accessors, data->accessors_count); + CGLTF_PTRFIXUP_REQ(data->animations[i].samplers[j].output, data->accessors, data->accessors_count); + } + + for (cgltf_size j = 0; j < data->animations[i].channels_count; ++j) + { + CGLTF_PTRFIXUP_REQ(data->animations[i].channels[j].sampler, data->animations[i].samplers, data->animations[i].samplers_count); + CGLTF_PTRFIXUP(data->animations[i].channels[j].target_node, data->nodes, data->nodes_count); + } + } + + return 0; +} + +/* + * -- jsmn.c start -- + * Source: https://github.com/zserge/jsmn + * License: MIT + * + * Copyright (c) 2010 Serge A. Zaitsev + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * Allocates a fresh unused token from the token pull. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, + jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, + int start, int end) { + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t' : case '\r' : case '\n' : case ' ' : + case ',' : case ']' : case '}' : + goto found; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + parser->pos--; + return 0; +} + +/** + * Fills next token with JSON string. + */ +static int jsmn_parse_string(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + + int start = parser->pos; + + parser->pos++; + + /* Skip starting quote */ + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + if (tokens == NULL) { + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + int i; + parser->pos++; + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': case '/' : case '\\' : case 'b' : + case 'f' : case 'r' : case 'n' : case 't' : + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) { + /* If it isn't a hex character we have an error */ + if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +static int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, + jsmntok_t *tokens, size_t num_tokens) { + int r; + int i; + jsmntok_t *token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; + switch (c) { + case '{': case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) + return JSMN_ERROR_NOMEM; + if (parser->toksuper != -1) { + tokens[parser->toksuper].size++; +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': case ']': + if (tokens == NULL) + break; + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + if(token->type != type || parser->toksuper == -1) { + return JSMN_ERROR_INVAL; + } + break; + } + token = &tokens[token->parent]; + } +#else + for (i = parser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + parser->toksuper = -1; + token->end = parser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) return JSMN_ERROR_INVAL; + for (; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + parser->toksuper = i; + break; + } + } +#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + case '\t' : case '\r' : case '\n' : case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { +#ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; +#else + for (i = parser->toknext - 1; i >= 0; i--) { + if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { + if (tokens[i].start != -1 && tokens[i].end == -1) { + parser->toksuper = i; + break; + } + } + } +#endif + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': case '0': case '1' : case '2': case '3' : case '4': + case '5': case '6': case '7' : case '8': case '9': + case 't': case 'f': case 'n' : + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +static void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} +/* + * -- jsmn.c end -- + */ + +#endif /* #ifdef CGLTF_IMPLEMENTATION */ + +/* cgltf is distributed under MIT license: + * + * Copyright (c) 2018 Johannes Kuhlmann + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ diff --git a/ios/include/filamat/Enums.h b/ios/include/filamat/Enums.h new file mode 100644 index 00000000..ea626e81 --- /dev/null +++ b/ios/include/filamat/Enums.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_ENUMMANAGER_H +#define TNT_ENUMMANAGER_H + +#include +#include +#include + +#include + +namespace filamat { + +using Property = MaterialBuilder::Property; +using UniformType = MaterialBuilder::UniformType; +using SamplerType = MaterialBuilder::SamplerType; +using SubpassType = MaterialBuilder::SubpassType; +using SamplerFormat = MaterialBuilder::SamplerFormat; +using ParameterPrecision = MaterialBuilder::ParameterPrecision; +using OutputTarget = MaterialBuilder::OutputTarget; +using OutputQualifier = MaterialBuilder::VariableQualifier; +using OutputType = MaterialBuilder::OutputType; + +// Convenience methods to convert std::string to Enum and also iterate over Enum values. +class Enums { +public: + + // Returns true if string "s" is a valid string representation of an element of enum T. + template + static bool isValid(const std::string& s) noexcept { + std::unordered_map& map = getMap(); + return map.find(s) != map.end(); + } + + // Return enum matching its string representation. Returns undefined if s is not a valid enum T + // value. You should always call isValid() first to validate a string before calling toEnum(). + template + static T toEnum(const std::string& s) noexcept { + std::unordered_map& map = getMap(); + return map.at(s); + } + + template + static std::string toString(T t) noexcept; + + // Return a map of all values in an enum with their string representation. + template + static std::unordered_map& map() noexcept { + std::unordered_map& map = getMap(); + return map; + }; + +private: + template + static std::unordered_map& getMap() noexcept; + + static std::unordered_map mStringToProperty; + static std::unordered_map mStringToUniformType; + static std::unordered_map mStringToSamplerType; + static std::unordered_map mStringToSubpassType; + static std::unordered_map mStringToSamplerFormat; + static std::unordered_map mStringToSamplerPrecision; + static std::unordered_map mStringToOutputTarget; + static std::unordered_map mStringToOutputQualifier; + static std::unordered_map mStringToOutputType; +}; + +template +std::string Enums::toString(T t) noexcept { + std::unordered_map& map = getMap(); + auto result = std::find_if(map.begin(), map.end(), [t](auto& pair) { + return pair.second == t; + }); + if (result != map.end()) { + return result->first; + } + return ""; +} + +} // namespace filamat + +#endif //TNT_ENUMMANAGER_H diff --git a/ios/include/filamat/IncludeCallback.h b/ios/include/filamat/IncludeCallback.h new file mode 100644 index 00000000..659ba289 --- /dev/null +++ b/ios/include/filamat/IncludeCallback.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMAT_INCLUDER_H +#define TNT_FILAMAT_INCLUDER_H + +#include + +#include + +namespace filamat { + +struct IncludeResult { + // The include name of the root file, as if it were being included. + // I.e., 'foobar.h' in the case of #include "foobar.h" + const utils::CString includeName; + + // The following fields should be filled out by the IncludeCallback when processing an include, + // or when calling resolveIncludes for the root file. + + // The full contents of the include file. This may contain additional, recursive include + // directives. + utils::CString text; + + // The line number for the first line of text (first line is 0). + size_t lineNumberOffset = 0; + + // The name of the include file. This gets passed as "includerName" for any includes inside of + // source. This field isn't used by the include system; it's up to the callback to give meaning + // to this value and interpret it accordingly. In the case of DirIncluder, this is an empty + // string to represent the root include file, and a canonical path for subsequent included + // files. + utils::CString name; +}; + +/** + * A callback invoked by the include system when an #include "file.h" directive is found. + * + * For example, if a file main.h includes file.h on line 10, then IncludeCallback would be called + * with the following: + * includeCallback("main.h", {.includeName = "file.h" }) + * It's then up to the IncludeCallback to fill out the .text, .name, and (optionally) + * lineNumberOffset fields. + * + * @param includedBy is the value that was given to IncludeResult.name for this source file, or + * the empty string for the root source file. + * @param result is the IncludeResult that the callback should fill out. + * @return true, if the include was resolved successfully, false otherwise. + * + * For an example of implementing this callback, see tools/matc/src/matc/DirIncluder.h. + */ +using IncludeCallback = std::function; + +} // namespace filamat + +#endif diff --git a/ios/include/filamat/MaterialBuilder.h b/ios/include/filamat/MaterialBuilder.h new file mode 100644 index 00000000..9416d530 --- /dev/null +++ b/ios/include/filamat/MaterialBuilder.h @@ -0,0 +1,746 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! \file + +#ifndef TNT_FILAMAT_MATERIAL_PACKAGE_BUILDER_H +#define TNT_FILAMAT_MATERIAL_PACKAGE_BUILDER_H + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +namespace utils { +class JobSystem; +} + +namespace filamat { + +struct MaterialInfo; +class ChunkContainer; +struct Variant; + +class UTILS_PUBLIC MaterialBuilderBase { +public: + /** + * High-level hint that works in concert with TargetApi to determine the shader models (used to + * generate GLSL) and final output representations (spirv and/or text). + */ + enum class Platform { + DESKTOP, + MOBILE, + ALL + }; + + enum class TargetApi : uint8_t { + OPENGL = 0x01u, + VULKAN = 0x02u, + METAL = 0x04u, + ALL = OPENGL | VULKAN | METAL + }; + + enum class TargetLanguage { + GLSL, // GLSL with OpenGL semantics + SPIRV // GLSL with Vulkan semantics + }; + + enum class Optimization { + NONE, + PREPROCESSOR, + SIZE, + PERFORMANCE + }; + + /** + * Initialize MaterialBuilder. + * + * init must be called first before building any materials. + */ + static void init(); + + /** + * Release internal MaterialBuilder resources. + * + * Call shutdown when finished building materials to release all internal resources. After + * calling shutdown, another call to MaterialBuilder::init must precede another material build. + */ + static void shutdown(); + +protected: + // Looks at platform and target API, then decides on shader models and output formats. + void prepare(bool vulkanSemantics); + + using ShaderModel = filament::backend::ShaderModel; + Platform mPlatform = Platform::DESKTOP; + TargetApi mTargetApi = (TargetApi) 0; + Optimization mOptimization = Optimization::PERFORMANCE; + bool mPrintShaders = false; + bool mGenerateDebugInfo = false; + utils::bitset32 mShaderModels; + struct CodeGenParams { + int shaderModel; + TargetApi targetApi; + TargetLanguage targetLanguage; + }; + std::vector mCodeGenPermutations; + // For finding properties and running semantic analysis, we always use the same code gen + // permutation. This is the first permutation generated with default arguments passed to matc. + const CodeGenParams mSemanticCodeGenParams = { + .shaderModel = (int) ShaderModel::GL_ES_30, + .targetApi = TargetApi::OPENGL, + .targetLanguage = TargetLanguage::SPIRV + }; + uint8_t mVariantFilter = 0; + + // Keeps track of how many times MaterialBuilder::init() has been called without a call to + // MaterialBuilder::shutdown(). Internally, glslang does something similar. We keep track for + // ourselves so we can inform the user if MaterialBuilder::init() hasn't been called before + // attempting to build a material. + static std::atomic materialBuilderClients; +}; + +// Utility function that looks at an Engine backend to determine TargetApi +inline constexpr MaterialBuilderBase::TargetApi targetApiFromBackend( + filament::backend::Backend backend) noexcept { + using filament::backend::Backend; + using TargetApi = MaterialBuilderBase::TargetApi; + switch (backend) { + case Backend::DEFAULT: return TargetApi::ALL; + case Backend::OPENGL: return TargetApi::OPENGL; + case Backend::VULKAN: return TargetApi::VULKAN; + case Backend::METAL: return TargetApi::METAL; + case Backend::NOOP: return TargetApi::OPENGL; + } +} + +/** + * MaterialBuilder builds Filament materials from shader code. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * #include + * using namespace filamat; + * + * // Must be called before any materials can be built. + * MaterialBuilder::init(); + + * MaterialBuilder builder; + * builder + * .name("My material") + * .material("void material (inout MaterialInputs material) {" + * " prepareMaterial(material);" + * " material.baseColor.rgb = float3(1.0, 0.0, 0.0);" + * "}") + * .shading(MaterialBuilder::Shading::LIT) + * .targetApi(MaterialBuilder::TargetApi::ALL) + * .platform(MaterialBuilder::Platform::ALL); + + * Package package = builder.build(); + * if (package.isValid()) { + * // success! + * } + + * // Call when finished building all materials to release internal + * // MaterialBuilder resources. + * MaterialBuilder::shutdown(); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * @see filament::Material + */ +class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { +public: + MaterialBuilder(); + + static constexpr size_t MATERIAL_VARIABLES_COUNT = 4; + enum class Variable : uint8_t { + CUSTOM0, + CUSTOM1, + CUSTOM2, + CUSTOM3 + // when adding more variables, make sure to update MATERIAL_VARIABLES_COUNT + }; + + using MaterialDomain = filament::MaterialDomain; + using RefractionMode = filament::RefractionMode; + using RefractionType = filament::RefractionType; + + using ShaderQuality = filament::ShaderQuality; + using BlendingMode = filament::BlendingMode; + using Shading = filament::Shading; + using Interpolation = filament::Interpolation; + using VertexDomain = filament::VertexDomain; + using TransparencyMode = filament::TransparencyMode; + using SpecularAmbientOcclusion = filament::SpecularAmbientOcclusion; + + using UniformType = filament::backend::UniformType; + using SamplerType = filament::backend::SamplerType; + using SubpassType = filament::backend::SubpassType; + using SamplerFormat = filament::backend::SamplerFormat; + using ParameterPrecision = filament::backend::Precision; + using CullingMode = filament::backend::CullingMode; + + enum class VariableQualifier : uint8_t { + OUT + }; + + enum class OutputTarget : uint8_t { + COLOR, + DEPTH + }; + + enum class OutputType : uint8_t { + FLOAT, + FLOAT2, + FLOAT3, + FLOAT4 + }; + + struct PreprocessorDefine { + std::string name; + std::string value; + + PreprocessorDefine(const std::string& name, const std::string& value) : + name(name), value(value) {} + }; + using PreprocessorDefineList = std::vector; + + //! Set the name of this material. + MaterialBuilder& name(const char* name) noexcept; + + //! Set the file name of this material file. Used in error reporting. + MaterialBuilder& fileName(const char* name) noexcept; + + //! Set the shading model. + MaterialBuilder& shading(Shading shading) noexcept; + + //! Set the interpolation mode. + MaterialBuilder& interpolation(Interpolation interpolation) noexcept; + + //! Add a parameter (i.e., a uniform) to this material. + MaterialBuilder& parameter(UniformType type, ParameterPrecision precision, + const char* name) noexcept; + + //! Add a parameter (i.e., a uniform) to this material. + MaterialBuilder& parameter(UniformType type, const char* name) noexcept { + return parameter(type, ParameterPrecision::DEFAULT, name); + } + + //! Add a parameter array to this material. + MaterialBuilder& parameter(UniformType type, size_t size, + ParameterPrecision precision, const char* name) noexcept; + + //! Add a parameter array to this material. + MaterialBuilder& parameter(UniformType type, size_t size, const char* name) noexcept { + return parameter(type, size, ParameterPrecision::DEFAULT, name); + } + + /** + * Add a sampler parameter to this material. + * + * When SamplerType::SAMPLER_EXTERNAL is specifed, format and precision are ignored. + */ + MaterialBuilder& parameter(SamplerType samplerType, SamplerFormat format, + ParameterPrecision precision, const char* name) noexcept; + /// @copydoc parameter(SamplerType, SamplerFormat, ParameterPrecision, const char*) + MaterialBuilder& parameter(SamplerType samplerType, SamplerFormat format, + const char* name) noexcept; + /// @copydoc parameter(SamplerType, SamplerFormat, ParameterPrecision, const char*) + MaterialBuilder& parameter(SamplerType samplerType, ParameterPrecision precision, + const char* name) noexcept; + /// @copydoc parameter(SamplerType, SamplerFormat, ParameterPrecision, const char*) + MaterialBuilder& parameter(SamplerType samplerType, const char* name) noexcept; + + //! Custom variables (all float4). + MaterialBuilder& variable(Variable v, const char* name) noexcept; + + /** + * Require a specified attribute. + * + * position is always required and normal depends on the shading model. + */ + MaterialBuilder& require(filament::VertexAttribute attribute) noexcept; + + //! Specify the domain that this material will operate in. + MaterialBuilder& materialDomain(MaterialDomain materialDomain) noexcept; + + /** + * Set the code content of this material. + * + * Surface Domain + * -------------- + * + * Materials in the SURFACE domain must declare a function: + * ~~~~~ + * void material(inout MaterialInputs material) { + * prepareMaterial(material); + * material.baseColor.rgb = float3(1.0, 0.0, 0.0); + * } + * ~~~~~ + * this function *must* call `prepareMaterial(material)` before it returns. + * + * Post-process Domain + * ------------------- + * + * Materials in the POST_PROCESS domain must declare a function: + * ~~~~~ + * void postProcess(inout PostProcessInputs postProcess) { + * postProcess.color = float4(1.0); + * } + * ~~~~~ + * + * @param code The source code of the material. + * @param line The line number offset of the material, where 0 is the first line. Used for error + * reporting + */ + MaterialBuilder& material(const char* code, size_t line = 0) noexcept; + + /** + * Set the callback used for resolving include directives. + * The default is no callback, which disallows all includes. + */ + MaterialBuilder& includeCallback(IncludeCallback callback) noexcept; + + /** + * Set the vertex code content of this material. + * + * Surface Domain + * -------------- + * + * Materials in the SURFACE domain must declare a function: + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * void materialVertex(inout MaterialVertexInputs material) { + * + * } + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Post-process Domain + * ------------------- + * + * Materials in the POST_PROCESS domain must declare a function: + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * void postProcessVertex(inout PostProcessVertexInputs postProcess) { + * + * } + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + * @param code The source code of the material. + * @param line The line number offset of the material, where 0 is the first line. Used for error + * reporting + */ + MaterialBuilder& materialVertex(const char* code, size_t line = 0) noexcept; + + + MaterialBuilder& quality(ShaderQuality quality) noexcept; + + //! Set the blending mode for this material. + MaterialBuilder& blending(BlendingMode blending) noexcept; + + /** + * Set the blending mode of the post-lighting color for this material. + * Only OPAQUE, TRANSPARENT and ADD are supported, the default is TRANSPARENT. + * This setting requires the material property "postLightingColor" to be set. + */ + MaterialBuilder& postLightingBlending(BlendingMode blending) noexcept; + + //! Set the vertex domain for this material. + MaterialBuilder& vertexDomain(VertexDomain domain) noexcept; + + /** + * How triangles are culled by default (doesn't affect points or lines, BACK by default). + * Material instances can override this. + */ + MaterialBuilder& culling(CullingMode culling) noexcept; + + //! Enable / disable color-buffer write (enabled by default, material instances can override). + MaterialBuilder& colorWrite(bool enable) noexcept; + + //! Enable / disable depth-buffer write (enabled by default for opaque, disabled for others, material instances can override). + MaterialBuilder& depthWrite(bool enable) noexcept; + + //! Enable / disable depth based culling (enabled by default, material instances can override). + MaterialBuilder& depthCulling(bool enable) noexcept; + + /** + * Double-sided materials don't cull faces, equivalent to culling(CullingMode::NONE). + * doubleSided() overrides culling() if called. + * When called with "false", this enables the capability for a run-time toggle. + */ + MaterialBuilder& doubleSided(bool doubleSided) noexcept; + + /** + * Any fragment with an alpha below this threshold is clipped (MASKED blending mode only). + * The mask threshold can also be controlled by using the float material parameter called + * `_maskThreshold`, or by calling + * @ref filament::MaterialInstance::setMaskThreshold "MaterialInstance::setMaskThreshold". + */ + MaterialBuilder& maskThreshold(float threshold) noexcept; + + //! The material output is multiplied by the shadowing factor (UNLIT model only). + MaterialBuilder& shadowMultiplier(bool shadowMultiplier) noexcept; + + //! This material casts transparent shadows. The blending mode must be TRANSPARENT or FADE. + MaterialBuilder& transparentShadow(bool transparentShadow) noexcept; + + /** + * Reduces specular aliasing for materials that have low roughness. Turning this feature on also + * helps preserve the shapes of specular highlights as an object moves away from the camera. + * When turned on, two float material parameters are added to control the effect: + * `_specularAAScreenSpaceVariance` and `_specularAAThreshold`. You can also use + * @ref filament::MaterialInstance::setSpecularAntiAliasingVariance + * "MaterialInstance::setSpecularAntiAliasingVariance" and + * @ref filament::MaterialInstance::setSpecularAntiAliasingThreshold + * "setSpecularAntiAliasingThreshold" + * + * Disabled by default. + */ + MaterialBuilder& specularAntiAliasing(bool specularAntiAliasing) noexcept; + + /** + * Sets the screen-space variance of the filter kernel used when applying specular + * anti-aliasing. The default value is set to 0.15. The specified value should be between 0 and + * 1 and will be clamped if necessary. + */ + MaterialBuilder& specularAntiAliasingVariance(float screenSpaceVariance) noexcept; + + /** + * Sets the clamping threshold used to suppress estimation errors when applying specular + * anti-aliasing. The default value is set to 0.2. The specified value should be between 0 and 1 + * and will be clamped if necessary. + */ + MaterialBuilder& specularAntiAliasingThreshold(float threshold) noexcept; + + /** + * Enables or disables the index of refraction (IoR) change caused by the clear coat layer when + * present. When the IoR changes, the base color is darkened. Disabling this feature preserves + * the base color as initially specified. + * + * Enabled by default. + */ + MaterialBuilder& clearCoatIorChange(bool clearCoatIorChange) noexcept; + + //! Enable / disable flipping of the Y coordinate of UV attributes, enabled by default. + MaterialBuilder& flipUV(bool flipUV) noexcept; + + //! Enable / disable multi-bounce ambient occlusion, disabled by default on mobile. + MaterialBuilder& multiBounceAmbientOcclusion(bool multiBounceAO) noexcept; + + //! Set the specular ambient occlusion technique. Disabled by default on mobile. + MaterialBuilder& specularAmbientOcclusion(SpecularAmbientOcclusion specularAO) noexcept; + + //! Specify the refraction + MaterialBuilder& refractionMode(RefractionMode refraction) noexcept; + + //! Specify the refraction type + MaterialBuilder& refractionType(RefractionType refractionType) noexcept; + + //! Specifies how transparent objects should be rendered (default is DEFAULT). + MaterialBuilder& transparencyMode(TransparencyMode mode) noexcept; + + /** + * Enable / disable custom surface shading. Custom surface shading requires the LIT + * shading model. In addition, the following function must be defined in the fragment + * block: + * + * ~~~~~ + * vec3 surfaceShading(const MaterialInputs materialInputs, + * const ShadingData shadingData, const LightData lightData) { + * + * return vec3(1.0); // Compute surface shading with custom BRDF, etc. + * } + * ~~~~~ + * + * This function is invoked once per light. Please refer to the materials documentation + * for more information about the different parameters. + * + * @param customSurfaceShading Enables or disables custom surface shading + */ + MaterialBuilder& customSurfaceShading(bool customSurfaceShading) noexcept; + + /** + * Specifies desktop vs mobile; works in concert with TargetApi to determine the shader models + * (used to generate code) and final output representations (spirv and/or text). + */ + MaterialBuilder& platform(Platform platform) noexcept; + + /** + * Specifies OpenGL, Vulkan, or Metal. + * This can be called repeatedly to build for multiple APIs. + * Works in concert with Platform to determine the shader models (used to generate code) and + * final output representations (spirv and/or text). + * If linking against filamat_lite, only `OPENGL` is allowed. + */ + MaterialBuilder& targetApi(TargetApi targetApi) noexcept; + + /** + * Specifies the level of optimization to apply to the shaders (default is PERFORMANCE). + * If linking against filamat_lite, this _must_ be called with Optimization::NONE. + */ + MaterialBuilder& optimization(Optimization optimization) noexcept; + + // TODO: this is present here for matc's "--print" flag, but ideally does not belong inside + // MaterialBuilder. + //! If true, will output the generated GLSL shader code to stdout. + MaterialBuilder& printShaders(bool printShaders) noexcept; + + //! If true, will include debugging information in generated SPIRV. + MaterialBuilder& generateDebugInfo(bool generateDebugInfo) noexcept; + + //! Specifies a list of variants that should be filtered out during code generation. + MaterialBuilder& variantFilter(uint8_t variantFilter) noexcept; + + //! Adds a new preprocessor macro definition to the shader code. Can be called repeatedly. + MaterialBuilder& shaderDefine(const char* name, const char* value) noexcept; + + //! Add a new fragment shader output variable. Only valid for materials in the POST_PROCESS domain. + MaterialBuilder& output(VariableQualifier qualifier, OutputTarget target, + OutputType type, const char* name, int location = -1) noexcept; + + MaterialBuilder& enableFramebufferFetch() noexcept; + + /** + * Build the material. If you are using the Filament engine with this library, you should use + * the job system provided by Engine. + */ + Package build(utils::JobSystem& jobSystem) noexcept; + +public: + // The methods and types below are for internal use + /// @cond never + + /** + * Add a subpass parameter to this material. + */ + MaterialBuilder& parameter(SubpassType subpassType, SamplerFormat format, ParameterPrecision + precision, const char* name) noexcept; + MaterialBuilder& parameter(SubpassType subpassType, SamplerFormat format, const char* name) + noexcept; + MaterialBuilder& parameter(SubpassType subpassType, ParameterPrecision precision, + const char* name) noexcept; + MaterialBuilder& parameter(SubpassType subpassType, const char* name) noexcept; + + struct Parameter { + Parameter() noexcept : parameterType(INVALID) {} + + // Sampler + Parameter(const char* paramName, SamplerType t, SamplerFormat f, ParameterPrecision p) + : name(paramName), size(1), precision(p), samplerType(t), format(f), parameterType(SAMPLER) { } + + // Uniform + Parameter(const char* paramName, UniformType t, size_t typeSize, ParameterPrecision p) + : name(paramName), size(typeSize), uniformType(t), precision(p), parameterType(UNIFORM) { } + + // Subpass + Parameter(const char* paramName, SubpassType t, SamplerFormat f, ParameterPrecision p) + : name(paramName), size(1), precision(p), subpassType(t), format(f), parameterType(SUBPASS) { } + + utils::CString name; + size_t size; + UniformType uniformType; + ParameterPrecision precision; + SamplerType samplerType; + SubpassType subpassType; + SamplerFormat format; + enum { + INVALID, + UNIFORM, + SAMPLER, + SUBPASS + } parameterType; + + bool isSampler() const { return parameterType == SAMPLER; } + bool isUniform() const { return parameterType == UNIFORM; } + bool isSubpass() const { return parameterType == SUBPASS; } + }; + + struct Output { + Output() noexcept = default; + Output(const char* outputName, VariableQualifier qualifier, OutputTarget target, + OutputType type, int location) noexcept + : name(outputName), qualifier(qualifier), target(target), type(type), + location(location) { } + + utils::CString name; + VariableQualifier qualifier; + OutputTarget target; + OutputType type; + int location; + }; + + static constexpr size_t MATERIAL_PROPERTIES_COUNT = filament::MATERIAL_PROPERTIES_COUNT; + using Property = filament::Property; + + using PropertyList = bool[MATERIAL_PROPERTIES_COUNT]; + using VariableList = utils::CString[MATERIAL_VARIABLES_COUNT]; + using OutputList = std::vector; + + static constexpr size_t MAX_COLOR_OUTPUT = filament::backend::MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; + static constexpr size_t MAX_DEPTH_OUTPUT = 1; + static_assert(MAX_COLOR_OUTPUT == 8, + "When updating MRT::TARGET_COUNT, manually update post_process_inputs.fs" + " and post_process.fs"); + + // Preview the first shader generated by the given CodeGenParams. + // This is used to run Static Code Analysis before generating a package. + const std::string peek(filament::backend::ShaderType type, + const CodeGenParams& params, const PropertyList& properties) noexcept; + + // Returns true if any of the parameter samplers is of type samplerExternal + bool hasExternalSampler() const noexcept; + + static constexpr size_t MAX_PARAMETERS_COUNT = 48; + static constexpr size_t MAX_SUBPASS_COUNT = 1; + using ParameterList = Parameter[MAX_PARAMETERS_COUNT]; + + // returns the number of parameters declared in this material + uint8_t getParameterCount() const noexcept { return mParameterCount; } + + // returns a list of at least getParameterCount() parameters + const ParameterList& getParameters() const noexcept { return mParameters; } + + uint8_t getVariantFilter() const { return mVariantFilter; } + + /// @endcond + +private: + void prepareToBuild(MaterialInfo& info) noexcept; + + // Return true if the shader is syntactically and semantically valid. + // This method finds all the properties defined in the fragment and + // vertex shaders of the material. + bool findAllProperties() noexcept; + // Multiple calls to findProperties accumulate the property sets across fragment + // and vertex shaders in mProperties. + bool findProperties(filament::backend::ShaderType type, + MaterialBuilder::PropertyList& p) noexcept; + bool runSemanticAnalysis() noexcept; + + bool checkLiteRequirements() noexcept; + + void writeCommonChunks(ChunkContainer& container, MaterialInfo& info) const noexcept; + void writeSurfaceChunks(ChunkContainer& container) const noexcept; + + bool generateShaders( + utils::JobSystem& jobSystem, + const std::vector& variants, ChunkContainer& container, + const MaterialInfo& info) const noexcept; + + bool isLit() const noexcept { return mShading != filament::Shading::UNLIT; } + + utils::CString mMaterialName; + utils::CString mFileName; + + class ShaderCode { + public: + void setLineOffset(size_t offset) noexcept { mLineOffset = offset; } + void setUnresolved(const utils::CString& code) noexcept { + mIncludesResolved = false; + mCode = code; + } + + // Resolve all the #include directives, returns true if successful. + bool resolveIncludes(IncludeCallback callback, const utils::CString& fileName) noexcept; + + const utils::CString& getResolved() const noexcept { + assert(mIncludesResolved); + return mCode; + } + + size_t getLineOffset() const noexcept { return mLineOffset; } + + private: + utils::CString mCode; + size_t mLineOffset = 0; + bool mIncludesResolved = false; + }; + + ShaderCode mMaterialCode; + ShaderCode mMaterialVertexCode; + + IncludeCallback mIncludeCallback = nullptr; + + PropertyList mProperties; + ParameterList mParameters; + VariableList mVariables; + OutputList mOutputs; + + ShaderQuality mShaderQuality = ShaderQuality::DEFAULT; + BlendingMode mBlendingMode = BlendingMode::OPAQUE; + BlendingMode mPostLightingBlendingMode = BlendingMode::TRANSPARENT; + CullingMode mCullingMode = CullingMode::BACK; + Shading mShading = Shading::LIT; + MaterialDomain mMaterialDomain = MaterialDomain::SURFACE; + RefractionMode mRefractionMode = RefractionMode::NONE; + RefractionType mRefractionType = RefractionType::SOLID; + Interpolation mInterpolation = Interpolation::SMOOTH; + VertexDomain mVertexDomain = VertexDomain::OBJECT; + TransparencyMode mTransparencyMode = TransparencyMode::DEFAULT; + + filament::AttributeBitset mRequiredAttributes; + + float mMaskThreshold = 0.4f; + float mSpecularAntiAliasingVariance = 0.15f; + float mSpecularAntiAliasingThreshold = 0.2f; + + bool mShadowMultiplier = false; + bool mTransparentShadow = false; + + uint8_t mParameterCount = 0; + + bool mDoubleSided = false; + bool mDoubleSidedCapability = false; + bool mColorWrite = true; + bool mDepthTest = true; + bool mDepthWrite = true; + bool mDepthWriteSet = false; + + bool mSpecularAntiAliasing = false; + bool mClearCoatIorChange = true; + + bool mFlipUV = true; + + bool mMultiBounceAO = false; + bool mMultiBounceAOSet = false; + + SpecularAmbientOcclusion mSpecularAO = SpecularAmbientOcclusion::NONE; + bool mSpecularAOSet = false; + + bool mCustomSurfaceShading = false; + + bool mEnableFramebufferFetch = false; + + PreprocessorDefineList mDefines; +}; + +} // namespace filamat + +template<> struct utils::EnableBitMaskOperators + : public std::true_type {}; + +#endif diff --git a/ios/include/filamat/Package.h b/ios/include/filamat/Package.h new file mode 100644 index 00000000..93e74a58 --- /dev/null +++ b/ios/include/filamat/Package.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMAT_PACKAGE_H +#define TNT_FILAMAT_PACKAGE_H + +#include +#include +#include + +#include +#include + +#include + +namespace filamat { + +class UTILS_PUBLIC Package { +public: + Package() = default; + + // Regular constructor + explicit Package(size_t size) : mSize(size) { + mPayload = new uint8_t[size]; + } + + Package(const void* src, size_t size) : Package(size) { + memcpy(mPayload, src, size); + } + + // Move Constructor + Package(Package&& other) noexcept : mPayload(other.mPayload), mSize(other.mSize), + mValid(other.mValid) { + other.mPayload = nullptr; + other.mSize = 0; + other.mValid = false; + } + + // Move assignment + Package& operator=(Package&& other) noexcept { + std::swap(mPayload, other.mPayload); + std::swap(mSize, other.mSize); + std::swap(mValid, other.mValid); + return *this; + } + + // Copy assignment operator disallowed. + Package& operator=(const Package& other) = delete; + + // Copy constructor disallowed. + Package(const Package& other) = delete; + + ~Package() { + delete[] mPayload; + } + + uint8_t* getData() const noexcept { + return mPayload; + } + + size_t getSize() const noexcept { + return mSize; + } + + uint8_t* getEnd() const noexcept { + return mPayload + mSize; + } + + void setValid(bool valid) noexcept { + mValid = valid; + } + + bool isValid() const noexcept { + return mValid; + } + + static Package invalidPackage() { + Package package(0); + package.setValid(false); + return package; + } + +private: + uint8_t* mPayload = nullptr; + size_t mSize = 0; + bool mValid = true; +}; + +} // namespace filamat +#endif diff --git a/ios/include/filament-iblprefilter/IBLPrefilterContext.h b/ios/include/filament-iblprefilter/IBLPrefilterContext.h new file mode 100644 index 00000000..560005f8 --- /dev/null +++ b/ios/include/filament-iblprefilter/IBLPrefilterContext.h @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_IBL_PREFILTER_IBLPREFILTER_H +#define TNT_IBL_PREFILTER_IBLPREFILTER_H + +#include +#include + +#include + +namespace filament { +class Engine; +class View; +class Scene; +class Renderer; +class Material; +class MaterialInstance; +class VertexBuffer; +class IndexBuffer; +class Camera; +class Texture; +} // namespace filament + +/** + * IBLPrefilterContext creates and initializes GPU state common to all environment map filters + * supported. Typically, only one instance per filament Engine of this object needs to exist. + * + * Usage Example: + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * #include + * using namespace filament; + * + * Engine* engine = Engine::create(); + * + * IBLPrefilterContext context(engine); + * IBLPrefilterContext::SpecularFilter filter(context); + * Texture* texture = filter(environment_cubemap); + * + * IndirectLight* indirectLight = IndirectLight::Builder() + * .reflections(texture) + * .build(engine); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +class UTILS_PUBLIC IBLPrefilterContext { +public: + + /** + * Creates an IBLPrefilter context. + * @param engine filament engine to use + */ + IBLPrefilterContext(filament::Engine& engine); + + /** + * Destroys all GPU resources created during initialization. + */ + ~IBLPrefilterContext() noexcept; + + // not copyable + IBLPrefilterContext(IBLPrefilterContext const&) = delete; + IBLPrefilterContext& operator=(IBLPrefilterContext const&) = delete; + + // movable + IBLPrefilterContext(IBLPrefilterContext&& rhs) noexcept; + IBLPrefilterContext& operator=(IBLPrefilterContext&& rhs); + + // ------------------------------------------------------------------------------------------- + + /** + * EquirectangularToCubemap is use to convert an equirectangluar image to a cubemap. + */ + class EquirectangularToCubemap { + public: + /** + * Creates a EquirectangularToCubemap processor. + * @param context IBLPrefilterContext to use + */ + explicit EquirectangularToCubemap(IBLPrefilterContext& context); + + /** + * Destroys all GPU resources created during initialization. + */ + ~EquirectangularToCubemap() noexcept; + + EquirectangularToCubemap(EquirectangularToCubemap const&) = delete; + EquirectangularToCubemap& operator=(EquirectangularToCubemap const&) = delete; + EquirectangularToCubemap(EquirectangularToCubemap&& rhs) noexcept; + EquirectangularToCubemap& operator=(EquirectangularToCubemap&& rhs); + + /** + * Converts an equirectangular image to a cubemap. + * @param equirectangular Texture to convert to a cubemap. + * - Can't be null. + * - Must be a 2d texture + * - Must have equirectangular geometry, that is width == 2*height. + * - Must be allocated with all mip levels. + * - Must be SAMPLEABLE + * @param outCubemap Output cubemap. If null the texture is automatically created + * with default parameters (size of 256 with 5 levels). + * - Must be a cubemap + * - Must have SAMPLEABLE and COLOR_ATTACHMENT usage bits + * @return returns outCubemap + */ + filament::Texture* operator()( + filament::Texture const* equirectangular, + filament::Texture* outCubemap = nullptr); + + private: + IBLPrefilterContext& mContext; + filament::Material* mEquirectMaterial = nullptr; + }; + + + /** + * SpecularFilter is a GPU based implementation of the specular probe pre-integration filter. + * An instance of SpecularFilter is needed per filter configuration. A filter configuration + * contains the filter's kernel and sample count. + */ + class SpecularFilter { + public: + enum class Kernel : uint8_t { + D_GGX, // Trowbridge-reitz distribution + }; + + /** + * Filter configuration. + */ + struct Config { + uint16_t sampleCount = 1024u; //!< filter sample count (max 2048) + uint8_t levelCount = 5u; //!< number of roughness levels + Kernel kernel = Kernel::D_GGX; //!< filter kernel + }; + + /** + * Filtering options for the current environment. + */ + struct Options { + float hdrLinear = 1024.0f; //!< no HDR compression up to this value + float hdrMax = 16384.0f; //!< HDR compression between hdrLinear and hdrMax + float lodOffset = 1.0f; //!< Good values are 1.0 or 2.0. Higher values help with heavily HDR inputs. + bool generateMipmap = true; //!< set to false if the environment map already has mipmaps + }; + + /** + * Creates a SpecularFilter processor. + * @param context IBLPrefilterContext to use + * @param config Configuration of the filter + */ + SpecularFilter(IBLPrefilterContext& context, Config config); + + /** + * Creates a filter with the default configuration. + * @param context IBLPrefilterContext to use + */ + explicit SpecularFilter(IBLPrefilterContext& context); + + /** + * Destroys all GPU resources created during initialization. + */ + ~SpecularFilter() noexcept; + + SpecularFilter(SpecularFilter const&) = delete; + SpecularFilter& operator=(SpecularFilter const&) = delete; + SpecularFilter(SpecularFilter&& rhs) noexcept; + SpecularFilter& operator=(SpecularFilter&& rhs); + + /** + * Generates a prefiltered cubemap. + * @param options Options for this environment + * @param environmentCubemap Environment cubemap (input). Can't be null. + * This cubemap must be SAMPLEABLE and must have all its + * levels allocated. If Options.generateMipmap is true, + * the mipmap levels will be overwritten, otherwise + * it is assumed that all levels are correctly initialized. + * @param outReflectionsTexture Output prefiltered texture or, if null, it is + * automatically created with some default parameters. + * outReflectionsTexture must be a cubemap, it must have + * at least COLOR_ATTACHMENT and SAMPLEABLE usages and at + * least the same number of levels than requested by Config. + * @return returns outReflectionsTexture + */ + filament::Texture* operator()(Options options, + filament::Texture const* environmentCubemap, + filament::Texture* outReflectionsTexture = nullptr); + + /** + * Generates a prefiltered cubemap. + * @param environmentCubemap Environment cubemap (input). Can't be null. + * This cubemap must be SAMPLEABLE and must have all its + * levels allocated. All mipmap levels will be overwritten. + * @param outReflectionsTexture Output prefiltered texture or, if null, it is + * automatically created with some default parameters. + * outReflectionsTexture must be a cubemap, it must have + * at least COLOR_ATTACHMENT and SAMPLEABLE usages and at + * least the same number of levels than requested by Config. + * @return returns outReflectionsTexture + */ + filament::Texture* operator()( + filament::Texture const* environmentCubemap, + filament::Texture* outReflectionsTexture = nullptr); + + // TODO: option for progressive filtering + + // TODO: add a callback for when the processing is done? + + private: + filament::Texture* createReflectionsTexture(); + IBLPrefilterContext& mContext; + filament::Material* mKernelMaterial = nullptr; + filament::Texture* mKernelTexture = nullptr; + uint32_t mSampleCount = 0u; + uint8_t mLevelCount = 1u; + }; + +private: + friend class Filter; + filament::Engine& mEngine; + filament::Renderer* mRenderer{}; + filament::Scene* mScene{}; + filament::VertexBuffer* mVertexBuffer{}; + filament::IndexBuffer* mIndexBuffer{}; + filament::Camera* mCamera{}; + utils::Entity mFullScreenQuadEntity{}; + utils::Entity mCameraEntity{}; + filament::View* mView{}; + filament::Material* mIntegrationMaterial{}; +}; + +#endif //TNT_IBL_PREFILTER_IBLPREFILTER_H diff --git a/ios/include/filament/Box.h b/ios/include/filament/Box.h new file mode 100644 index 00000000..f4cdb5fa --- /dev/null +++ b/ios/include/filament/Box.h @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! \file + +#ifndef TNT_FILAMENT_BOX_H +#define TNT_FILAMENT_BOX_H + +#include + +#include + +#include +#include + +namespace filament { + +/** + * An axis aligned 3D box represented by its center and half-extent. + */ +class UTILS_PUBLIC Box { +public: + /** Center of the 3D box */ + math::float3 center = {}; + + /** Half extent from the center on all 3 axis */ + math::float3 halfExtent = {}; + + /** + * Whether the box is empty, i.e.: it's volume is null. + * @return true if the volume of the box is null + */ + constexpr bool isEmpty() const noexcept { + return length2(halfExtent) == 0; + } + + /** + * Computes the lowest coordinates corner of the box. + * @return center - halfExtent + */ + constexpr math::float3 getMin() const noexcept { + return center - halfExtent; + } + + /** + * Computes the largest coordinates corner of the box. + * @return center + halfExtent + */ + constexpr math::float3 getMax() const noexcept { + return center + halfExtent; + } + + /** + * Initializes the 3D box from its min / max coordinates on each axis + * @param min lowest coordinates corner of the box + * @param max largest coordinates corner of the box + * @return This bounding box + */ + Box& set(const math::float3& min, const math::float3& max) noexcept { + // float3 ctor needed for visual studio + center = (max + min) * math::float3(0.5f); + halfExtent = (max - min) * math::float3(0.5f); + return *this; + } + + /** + * Computes the bounding box of the union of two boxes + * @param box The box to be combined with + * @return The bounding box of the union of *this and box + */ + Box& unionSelf(const Box& box) noexcept { + set(min(getMin(), box.getMin()), max(getMax(), box.getMax())); + return *this; + } + + /** + * Translates the box *to* a given center position + * @param tr position to translate the box to + * @return A box centered in \p tr with the same extent than *this + */ + constexpr Box translateTo(const math::float3& tr) const noexcept { + return Box{ tr, halfExtent }; + } + + /** + * Computes the smallest bounding sphere of the box. + * @return The smallest sphere defined by its center (.xyz) and radius (.w) that contains *this + */ + math::float4 getBoundingSphere() const noexcept { + return { center, length(halfExtent) }; + } + + /** + * Computes the bounding box of a box transformed by a rigid transform + * @param box the box to transform + * @param m a 4x4 matrix that must be a rigid transform + * @return the bounding box of the transformed box. + * Result is undefined if \p m is not a rigid transform + */ + friend Box rigidTransform(Box const& box, const math::mat4f& m) noexcept; + + /** + * Computes the bounding box of a box transformed by a rigid transform + * @param box the box to transform + * @param m a 3x3 matrix that must be a rigid transform + * @return the bounding box of the transformed box. + * Result is undefined if \p m is not a rigid transform + */ + friend Box rigidTransform(Box const& box, const math::mat3f& m) noexcept; +}; + +/** + * An axis aligned box represented by its min and max coordinates + */ +struct UTILS_PUBLIC Aabb { + + /** min coordinates */ + math::float3 min = std::numeric_limits::max(); + + /** max coordinates */ + math::float3 max = std::numeric_limits::lowest(); + + /** + * Computes the center of the box. + * @return (max + min)/2 + */ + math::float3 center() const noexcept { + // float3 ctor needed for visual studio + return (max + min) * math::float3(0.5f); + } + + /** + * Computes the half-extent of the box. + * @return (max - min)/2 + */ + math::float3 extent() const noexcept { + // float3 ctor needed for visual studio + return (max - min) * math::float3(0.5f); + } + + /** + * Whether the box is empty, i.e.: it's volume is null or negative. + * @return true if min >= max, i.e: the volume of the box is null or negative + */ + bool isEmpty() const noexcept { + return any(greaterThanEqual(min, max)); + } + + struct Corners { + using value_type = math::float3; + value_type const* begin() const { return vertices; } + value_type const* end() const { return vertices + 8; } + value_type * begin() { return vertices; } + value_type * end() { return vertices + 8; } + value_type const* data() const { return vertices; } + value_type * data() { return vertices; } + size_t size() const { return 8; } + value_type vertices[8]; + }; + + /** + * Returns the 8 corner vertices of the AABB. + */ + Corners getCorners() const; + + /** + * Returns whether the box contains a given point. + * + * @param p the point to test + * @return the maximum signed distance to the box. Negative if p is in the box + */ + float contains(math::float3 p) const noexcept; + + /** + * Applies an affine transformation to the AABB. + * + * @param m the 4x4 transformation to apply + * @return the transformed box + */ + Aabb transform(const math::mat4f& m) const noexcept; +}; + +} // namespace filament + +#endif // TNT_FILAMENT_BOX_H diff --git a/ios/include/filament/BufferObject.h b/ios/include/filament/BufferObject.h new file mode 100644 index 00000000..1ede31b8 --- /dev/null +++ b/ios/include/filament/BufferObject.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! \file + +#ifndef TNT_FILAMENT_BUFFEROBJECT_H +#define TNT_FILAMENT_BUFFEROBJECT_H + +#include + +#include + +#include + +#include + +namespace filament { + +class FBufferObject; + +class Engine; + +/** + * A generic GPU buffer containing data. + * + * Usage of this BufferObject is optional. For simple use cases it is not necessary. It is useful + * only when you need to share data between multiple VertexBuffer instances. It also allows you to + * efficiently swap-out the buffers in VertexBuffer. + * + * NOTE: For now this is only used for vertex data, but in the future we may use it for other things + * (e.g. compute). + * + * @see VertexBuffer + */ +class UTILS_PUBLIC BufferObject : public FilamentAPI { + struct BuilderDetails; + +public: + using BufferDescriptor = backend::BufferDescriptor; + using BindingType = backend::BufferObjectBinding; + + class Builder : public BuilderBase { + friend struct BuilderDetails; + public: + Builder() noexcept; + Builder(Builder const& rhs) noexcept; + Builder(Builder&& rhs) noexcept; + ~Builder() noexcept; + Builder& operator=(Builder const& rhs) noexcept; + Builder& operator=(Builder&& rhs) noexcept; + + /** + * Size of the buffer in bytes. + * @param byteCount Maximum number of bytes the BufferObject can hold. + * @return A reference to this Builder for chaining calls. + */ + Builder& size(uint32_t byteCount) noexcept; + + /** + * The binding type for this buffer object. (defaults to VERTEX) + * @param BindingType Distinguishes between SSBO, VBO, etc. For now this must be VERTEX. + * @return A reference to this Builder for chaining calls. + */ + Builder& bindingType(BindingType bindingType) noexcept; + + /** + * Creates the BufferObject and returns a pointer to it. After creation, the buffer + * object is uninitialized. Use BufferObject::setBuffer() to initialize it. + * + * @param engine Reference to the filament::Engine to associate this BufferObject with. + * + * @return pointer to the newly created object or nullptr if exceptions are disabled and + * an error occurred. + * + * @exception utils::PostConditionPanic if a runtime error occurred, such as running out of + * memory or other resources. + * @exception utils::PreConditionPanic if a parameter to a builder function was invalid. + * + * @see IndexBuffer::setBuffer + */ + BufferObject* build(Engine& engine); + private: + friend class FBufferObject; + }; + + /** + * Asynchronously copy-initializes a region of this BufferObject from the data provided. + * + * @param engine Reference to the filament::Engine associated with this BufferObject. + * @param buffer A BufferDescriptor representing the data used to initialize the BufferObject. + * @param byteOffset Offset in bytes into the BufferObject + */ + void setBuffer(Engine& engine, BufferDescriptor&& buffer, uint32_t byteOffset = 0); + + /** + * Returns the size of this BufferObject in elements. + * @return The maximum capacity of the BufferObject. + */ + size_t getByteCount() const noexcept; +}; + +} // namespace filament + +#endif // TNT_FILAMENT_BUFFEROBJECT_H diff --git a/ios/include/filament/Camera.h b/ios/include/filament/Camera.h new file mode 100644 index 00000000..f0574020 --- /dev/null +++ b/ios/include/filament/Camera.h @@ -0,0 +1,516 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! \file + +#ifndef TNT_FILAMENT_CAMERA_H +#define TNT_FILAMENT_CAMERA_H + +#include + +#include + +#include +#include +#include + +namespace utils { +class Entity; +} // namespace utils + +namespace filament { + +/** + * Camera represents the eye through which the scene is viewed. + * + * A Camera has a position and orientation and controls the projection and exposure parameters. + * + * Creation and destruction + * ======================== + * + * In Filament, Camera is a component that must be associated with an entity. To do so, + * use Engine::createCamera(Entity). A Camera component is destroyed using + * Engine::destroyCameraComponent(Entity). + * + * ~~~~~~~~~~~{.cpp} + * filament::Engine* engine = filament::Engine::create(); + * + * utils::Entity myCameraEntity = utils::EntityManager::get().create(); + * filament::Camera* myCamera = engine->createCamera(myCameraEntity); + * myCamera->setProjection(45, 16.0/9.0, 0.1, 1.0); + * myCamera->lookAt({0, 1.60, 1}, {0, 0, 0}); + * engine->destroyCameraComponent(myCamera); + * ~~~~~~~~~~~ + * + * + * Coordinate system + * ================= + * + * The camera coordinate system defines the *view space*. The camera points towards its -z axis + * and is oriented such that its top side is in the direction of +y, and its right side in the + * direction of +x. + * + * @note + * Since the *near* and *far* planes are defined by the distance from the camera, + * their respective coordinates are -\p distance(near) and -\p distance(far). + * + * Clipping planes + * =============== + * + * The camera defines six *clipping planes* which together create a *clipping volume*. The + * geometry outside this volume is clipped. + * + * The clipping volume can either be a box or a frustum depending on which projection is used, + * respectively Projection.ORTHO or Projection.PERSPECTIVE. The six planes are specified either + * directly or indirectly using setProjection(). + * + * The six planes are: + * - left + * - right + * - bottom + * - top + * - near + * - far + * + * @note + * To increase the depth-buffer precision, the *far* clipping plane is always assumed to be at + * infinity for rendering. That is, it is not used to clip geometry during rendering. + * However, it is used during the culling phase (objects entirely behind the *far* + * plane are culled). + * + * + * Choosing the *near* plane distance + * ================================== + * + * The *near* plane distance greatly affects the depth-buffer resolution. + * + * Example: Precision at 1m, 10m, 100m and 1Km for various near distances assuming a 32-bit float + * depth-buffer + * + * near (m) | 1 m | 10 m | 100 m | 1 Km + * -----------:|:------:|:-------:|:--------:|:--------: + * 0.001 | 7.2e-5 | 0.0043 | 0.4624 | 48.58 + * 0.01 | 6.9e-6 | 0.0001 | 0.0430 | 4.62 + * 0.1 | 3.6e-7 | 7.0e-5 | 0.0072 | 0.43 + * 1.0 | 0 | 3.8e-6 | 0.0007 | 0.07 + * + * + * As can be seen in the table above, the depth-buffer precision drops rapidly with the + * distance to the camera. + * Make sure to pick the highest *near* plane distance possible. + * + * + * Exposure + * ======== + * + * The Camera is also used to set the scene's exposure, just like with a real camera. The lights + * intensity and the Camera exposure interact to produce the final scene's brightness. + * + * + * + * \see Frustum, View + */ +class UTILS_PUBLIC Camera : public FilamentAPI { +public: + //! Denotes the projection type used by this camera. \see setProjection + enum class Projection : int { + PERSPECTIVE, //!< perspective projection, objects get smaller as they are farther + ORTHO //!< orthonormal projection, preserves distances + }; + + //! Denotes a field-of-view direction. \see setProjection + enum class Fov : int { + VERTICAL, //!< the field-of-view angle is defined on the vertical axis + HORIZONTAL //!< the field-of-view angle is defined on the horizontal axis + }; + + /** Sets the projection matrix from a frustum defined by six planes. + * + * @param projection type of #Projection to use. + * + * @param left distance in world units from the camera to the left plane, + * at the near plane. + * Precondition: \p left != \p right. + * + * @param right distance in world units from the camera to the right plane, + * at the near plane. + * Precondition: \p left != \p right. + * + * @param bottom distance in world units from the camera to the bottom plane, + * at the near plane. + * Precondition: \p bottom != \p top. + * + * @param top distance in world units from the camera to the top plane, + * at the near plane. + * Precondition: \p left != \p right. + * + * @param near distance in world units from the camera to the near plane. The near plane's + * position in view space is z = -\p near. + * Precondition: \p near > 0 for PROJECTION::PERSPECTIVE or + * \p near != far for PROJECTION::ORTHO + * + * @param far distance in world units from the camera to the far plane. The far plane's + * position in view space is z = -\p far. + * Precondition: \p far > near for PROJECTION::PERSPECTIVE or + * \p far != near for PROJECTION::ORTHO + * + * @attention these parameters are silently modified to meet the preconditions above. + * + * @see Projection, Frustum + */ + void setProjection(Projection projection, + double left, double right, + double bottom, double top, + double near, double far) noexcept; + + /** Sets the projection matrix from the field-of-view. + * + * @param fovInDegrees full field-of-view in degrees. 0 < \p fov < 180. + * @param aspect aspect ratio \f$ \frac{width}{height} \f$. \p aspect > 0. + * @param near distance in world units from the camera to the near plane. \p near > 0. + * @param far distance in world units from the camera to the far plane. \p far > \p near. + * @param direction direction of the \p fovInDegrees parameter. + * + * @see Fov. + */ + void setProjection(double fovInDegrees, double aspect, double near, double far, + Fov direction = Fov::VERTICAL) noexcept; + + /** Sets the projection matrix from the focal length. + * + * @param focalLengthInMillimeters lens's focal length in millimeters. \p focalLength > 0. + * @param aspect aspect ratio \f$ \frac{width}{height} \f$. \p aspect > 0. + * @param near distance in world units from the camera to the near plane. \p near > 0. + * @param far distance in world units from the camera to the far plane. \p far > \p near. + */ + void setLensProjection(double focalLengthInMillimeters, + double aspect, double near, double far) noexcept; + + /** Sets a custom projection matrix. + * + * The projection matrix must be of one of the following form: + * a 0 tx 0 a 0 0 tx + * 0 b ty 0 0 b 0 ty + * 0 0 tz c 0 0 c tz + * 0 0 -1 0 0 0 0 1 + * + * The projection matrix must define an NDC system that must match the OpenGL convention, + * that is all 3 axis are mapped to [-1, 1]. + * + * @param projection custom projection matrix used for rendering and culling + * @param near distance in world units from the camera to the near plane. \p near > 0. + * @param far distance in world units from the camera to the far plane. \p far > \p near. + */ + void setCustomProjection(math::mat4 const& projection, double near, double far) noexcept; + + /** Sets the projection matrix. + * + * The projection matrices must be of one of the following form: + * a 0 tx 0 a 0 0 tx + * 0 b ty 0 0 b 0 ty + * 0 0 tz c 0 0 c tz + * 0 0 -1 0 0 0 0 1 + * + * The projection matrices must define an NDC system that must match the OpenGL convention, + * that is all 3 axis are mapped to [-1, 1]. + * + * @param projection custom projection matrix used for rendering + * @param projectionForCulling custom projection matrix used for culling + * @param near distance in world units from the camera to the near plane. \p near > 0. + * @param far distance in world units from the camera to the far plane. \p far > \p near. + */ + void setCustomProjection(math::mat4 const& projection, math::mat4 const& projectionForCulling, + double near, double far) noexcept; + + /** Sets an additional matrix that scales the projection matrix. + * + * This is useful to adjust the aspect ratio of the camera independent from its projection. + * First, pass an aspect of 1.0 to setProjection. Then set the scaling with the desired aspect + * ratio: + * + * const double aspect = width / height; + * + * // with Fov::HORIZONTAL passed to setProjection: + * camera->setScaling(double4 {1.0, aspect}); + * + * // with Fov::VERTICAL passed to setProjection: + * camera->setScaling(double4 {1.0 / aspect, 1.0}); + * + * + * By default, this is an identity matrix. + * + * @param scaling diagonal of the 2x2 scaling matrix to be applied after the projection matrix. + * + * @see setProjection, setLensProjection, setCustomProjection + */ + void setScaling(math::double2 scaling) noexcept; + + /** + * Sets an additional matrix that shifts the projection matrix. + * By default, this is an identity matrix. + * + * @param shift x and y translation added to the projection matrix, specified in NDC + * coordinates, that is, if the translation must be specified in pixels, + * shift must be scaled by 1.0 / { viewport.width, viewport.height }. + * + * @see setProjection, setLensProjection, setCustomProjection + */ + void setShift(math::double2 shift) noexcept; + + /** Returns the scaling amount used to scale the projection matrix. + * + * @return the diagonal of the scaling matrix applied after the projection matrix. + * + * @see setScaling + */ + math::double4 getScaling() const noexcept; + + /** Returns the shift amount used to translate the projection matrix. + * + * @return the 2D translation x and y offsets applied after the projection matrix. + * + * @see setShift + */ + math::double2 getShift() const noexcept; + + /** Returns the projection matrix used for rendering. + * + * The projection matrix used for rendering always has its far plane set to infinity. This + * is why it may differ from the matrix set through setProjection() or setLensProjection(). + * + * @return The projection matrix used for rendering + * + * @see setProjection, setLensProjection, setCustomProjection, getCullingProjectionMatrix + */ + math::mat4 getProjectionMatrix() const noexcept; + + + /** Returns the projection matrix used for culling (far plane is finite). + * + * @return The projection matrix set by setProjection or setLensProjection. + * + * @see setProjection, setLensProjection, getProjectionMatrix + */ + math::mat4 getCullingProjectionMatrix() const noexcept; + + + //! Returns the frustum's near plane + float getNear() const noexcept; + + //! Returns the frustum's far plane used for culling + float getCullingFar() const noexcept; + + /** Sets the camera's view matrix. + * + * Helper method to set the camera's entity transform component. + * It has the same effect as calling: + * + * ~~~~~~~~~~~{.cpp} + * engine.getTransformManager().setTransform( + * engine.getTransformManager().getInstance(camera->getEntity()), view); + * ~~~~~~~~~~~ + * + * @param view The camera position and orientation provided as a rigid transform matrix. + * + * @note The Camera "looks" towards its -z axis + * + * @warning \p view must be a rigid transform + */ + void setModelMatrix(const math::mat4& view) noexcept; + void setModelMatrix(const math::mat4f& view) noexcept; //!< \overload + + /** Sets the camera's view matrix + * + * @param eye The position of the camera in world space. + * @param center The point in world space the camera is looking at. + * @param up A unit vector denoting the camera's "up" direction. + */ + void lookAt(const math::float3& eye, + const math::float3& center, + const math::float3& up) noexcept; + + /** Sets the camera's view matrix, assuming up is along the y axis + * + * @param eye The position of the camera in world space. + * @param center The point in world space the camera is looking at. + */ + void lookAt(const math::float3& eye, + const math::float3& center) noexcept; + + /** Returns the camera's model matrix + * + * Helper method to return the camera's entity transform component. + * It has the same effect as calling: + * + * ~~~~~~~~~~~{.cpp} + * engine.getTransformManager().getWorldTransform( + * engine.getTransformManager().getInstance(camera->getEntity())); + * ~~~~~~~~~~~ + * + * @return The camera's pose in world space as a rigid transform. Parent transforms, if any, + * are taken into account. + */ + math::mat4 getModelMatrix() const noexcept; + + //! Returns the camera's view matrix (inverse of the model matrix) + math::mat4 getViewMatrix() const noexcept; + + //! Returns the camera's position in world space + math::float3 getPosition() const noexcept; + + //! Returns the camera's normalized left vector + math::float3 getLeftVector() const noexcept; + + //! Returns the camera's normalized up vector + math::float3 getUpVector() const noexcept; + + //! Returns the camera's forward vector + math::float3 getForwardVector() const noexcept; + + //! Returns the camera's field of view in degrees + float getFieldOfViewInDegrees(Fov direction) const noexcept; + + //! Returns a Frustum object in world space + class Frustum getFrustum() const noexcept; + + //! Returns the entity representing this camera + utils::Entity getEntity() const noexcept; + + /** Sets this camera's exposure (default is f/16, 1/125s, 100 ISO) + * + * The exposure ultimately controls the scene's brightness, just like with a real camera. + * The default values provide adequate exposure for a camera placed outdoors on a sunny day + * with the sun at the zenith. + * + * @param aperture Aperture in f-stops, clamped between 0.5 and 64. + * A lower \p aperture value *increases* the exposure, leading to + * a brighter scene. Realistic values are between 0.95 and 32. + * + * @param shutterSpeed Shutter speed in seconds, clamped between 1/25,000 and 60. + * A lower shutter speed increases the exposure. Realistic values are + * between 1/8000 and 30. + * + * @param sensitivity Sensitivity in ISO, clamped between 10 and 204,800. + * A higher \p sensitivity increases the exposure. Realistic values are + * between 50 and 25600. + * + * @note + * With the default parameters, the scene must contain at least one Light of intensity + * similar to the sun (e.g.: a 100,000 lux directional light). + * + * @see LightManager, Exposure + */ + void setExposure(float aperture, float shutterSpeed, float sensitivity) noexcept; + + /** Sets this camera's exposure directly. Calling this method will set the aperture + * to 1.0, the shutter speed to 1.2 and the sensitivity will be computed to match + * the requested exposure (for a desired exposure of 1.0, the sensitivity will be + * set to 100 ISO). + * + * This method is useful when trying to match the lighting of other engines or tools. + * Many engines/tools use unit-less light intensities, which can be matched by setting + * the exposure manually. This can be typically achieved by setting the exposure to + * 1.0. + */ + void setExposure(float exposure) noexcept { + setExposure(1.0f, 1.2f, 100.0f * (1.0f / exposure)); + } + + //! returns this camera's aperture in f-stops + float getAperture() const noexcept; + + //! returns this camera's shutter speed in seconds + float getShutterSpeed() const noexcept; + + //! returns this camera's sensitivity in ISO + float getSensitivity() const noexcept; + + //! returns the focal length in meters [m] for a 35mm camera + double getFocalLength() const noexcept; + + /** + * Sets the camera focus distance. This is used by the Depth-of-field PostProcessing effect. + * @param distance Distance from the camera to the plane of focus in world units. + * Must be positive and larger than the near clipping plane. + */ + void setFocusDistance(float distance) noexcept; + + //! Returns the focus distance in world units + float getFocusDistance() const noexcept; + + /** + * Returns the inverse of a projection matrix. + * + * \param p the projection matrix to inverse + * \returns the inverse of the projection matrix \p p + * + * \warning the projection matrix to invert must have one of the form below: + * - perspective projection + * + * \f$ + * \left( + * \begin{array}{cccc} + * a & 0 & tx & 0 \\ + * 0 & b & ty & 0 \\ + * 0 & 0 & tz & c \\ + * 0 & 0 & -1 & 0 \\ + * \end{array} + * \right) + * \f$ + * + * - orthographic projection + * + * \f$ + * \left( + * \begin{array}{cccc} + * a & 0 & 0 & tx \\ + * 0 & b & 0 & ty \\ + * 0 & 0 & c & tz \\ + * 0 & 0 & 0 & 1 \\ + * \end{array} + * \right) + * \f$ + */ + static math::mat4 inverseProjection(const math::mat4& p) noexcept; + + /** + * Returns the inverse of a projection matrix. + * @see inverseProjection(const math::mat4&) + */ + static math::mat4f inverseProjection(const math::mat4f& p) noexcept; + + /** + * Helper to compute the effective focal length taking into account the focus distance + * + * @param focalLength focal length in any unit (e.g. [m] or [mm]) + * @param focusDistance focus distance in same unit as focalLength + * @return the effective focal length in same unit as focalLength + */ + static double computeEffectiveFocalLength(double focalLength, double focusDistance) noexcept; + + /** + * Helper to compute the effective field-of-view taking into account the focus distance + * + * @param fovInDegrees full field of view in degrees + * @param focusDistance focus distance in meters [m] + * @return effective full field of view in degrees + */ + static double computeEffectiveFov(double fovInDegrees, double focusDistance) noexcept; +}; + +} // namespace filament + +#endif // TNT_FILAMENT_CAMERA_H diff --git a/ios/include/filament/Color.h b/ios/include/filament/Color.h new file mode 100644 index 00000000..17de6a23 --- /dev/null +++ b/ios/include/filament/Color.h @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! \file + +#ifndef TNT_FILAMENT_COLOR_H +#define TNT_FILAMENT_COLOR_H + +#include + +#include +#include + +namespace filament { + +//! RGB color in linear space +using LinearColor = math::float3; + +//! RGB color in sRGB space +using sRGBColor = math::float3; + +//! RGBA color in linear space, with alpha +using LinearColorA = math::float4; + +//! RGBA color in sRGB space, with alpha +using sRGBColorA = math::float4; + +//! types of RGB colors +enum class RgbType : uint8_t { + sRGB, //!< the color is defined in sRGB space + LINEAR, //!< the color is defined in linear space +}; + +//! types of RGBA colors +enum class RgbaType : uint8_t { + /** + * the color is defined in sRGB space and the RGB values + * have not been premultiplied by the alpha (for instance, a 50% + * transparent red is <1,0,0,0.5>) + */ + sRGB, + /** + * the color is defined in linear space and the RGB values + * have not been premultiplied by the alpha (for instance, a 50% + * transparent red is <1,0,0,0.5>) + */ + LINEAR, + /** + * the color is defined in sRGB space and the RGB values + * have been premultiplied by the alpha (for instance, a 50% + * transparent red is <0.5,0,0,0.5>) + */ + PREMULTIPLIED_sRGB, + /** + * the color is defined in linear space and the RGB values + * have been premultiplied by the alpha (for instance, a 50% + * transparent red is <0.5,0,0,0.5>) + */ + PREMULTIPLIED_LINEAR +}; + +//! type of color conversion to use when converting to/from sRGB and linear spaces +enum ColorConversion { + ACCURATE, //!< accurate conversion using the sRGB standard + FAST //!< fast conversion using a simple gamma 2.2 curve +}; + +/** + * Utilities to manipulate and convert colors + */ +class UTILS_PUBLIC Color { +public: + //! converts an RGB color to linear space, the conversion depends on the specified type + static LinearColor toLinear(RgbType type, math::float3 color); + + //! converts an RGBA color to linear space, the conversion depends on the specified type + static LinearColorA toLinear(RgbaType type, math::float4 color); + + //! converts an RGB color in sRGB space to an RGB color in linear space + template + static LinearColor toLinear(sRGBColor const& color); + + //! converts an RGB color in linear space to an RGB color in sRGB space + template + static sRGBColor toSRGB(LinearColor const& color); + + /** + * converts an RGBA color in sRGB space to an RGBA color in linear space + * the alpha component is left unmodified + */ + template + static LinearColorA toLinear(sRGBColorA const& color); + + /** + * converts an RGBA color in linear space to an RGBA color in sRGB space + * the alpha component is left unmodified + */ + template + static sRGBColorA toSRGB(LinearColorA const& color); + + /** + * converts a correlated color temperature to a linear RGB color in sRGB + * space the temperature must be expressed in kelvin and must be in the + * range 1,000K to 15,000K + */ + static LinearColor cct(float K); + + /** + * converts a CIE standard illuminant series D to a linear RGB color in + * sRGB space the temperature must be expressed in kelvin and must be in + * the range 4,000K to 25,000K + */ + static LinearColor illuminantD(float K); + + /** + * computes the Beer-Lambert absorption coefficients from the specified + * transmittance color and distance. The computed absorption will guarantee + * the white light will become the specified color at the specified distance. + * The output of this function can be used as the absorption parameter of + * materials that use refraction. + * + * @param color the desired linear RGB color in sRGB space + * @param distance the distance at which white light should become the specified color + * + * @return absorption coefficients for the Beer-Lambert law + */ + static math::float3 absorptionAtDistance(LinearColor const& color, float distance); + +private: + static math::float3 sRGBToLinear(math::float3 color) noexcept; + static math::float3 linearToSRGB(math::float3 color) noexcept; +}; + +// Use the default implementation from the header +template<> +inline LinearColor Color::toLinear(sRGBColor const& color) { + return pow(color, 2.2f); +} + +template<> +inline LinearColorA Color::toLinear(sRGBColorA const& color) { + return LinearColorA{pow(color.rgb, 2.2f), color.a}; +} + +template<> +inline LinearColor Color::toLinear(sRGBColor const& color) { + return sRGBToLinear(color); +} + +template<> +inline LinearColorA Color::toLinear(sRGBColorA const& color) { + return LinearColorA{sRGBToLinear(color.rgb), color.a}; +} + +// Use the default implementation from the header +template<> +inline sRGBColor Color::toSRGB(LinearColor const& color) { + return pow(color, 1.0f / 2.2f); +} + +template<> +inline sRGBColorA Color::toSRGB(LinearColorA const& color) { + return sRGBColorA{pow(color.rgb, 1.0f / 2.2f), color.a}; +} + +template<> +inline sRGBColor Color::toSRGB(LinearColor const& color) { + return linearToSRGB(color); +} + +template<> +inline sRGBColorA Color::toSRGB(LinearColorA const& color) { + return sRGBColorA{linearToSRGB(color.rgb), color.a}; +} + +inline LinearColor Color::toLinear(RgbType type, math::float3 color) { + return (type == RgbType::LINEAR) ? color : Color::toLinear(color); +} + +// converts an RGBA color to linear space +// the conversion depends on the specified type +inline LinearColorA Color::toLinear(RgbaType type, math::float4 color) { + switch (type) { + case RgbaType::sRGB: + return Color::toLinear(color) * math::float4{color.a, color.a, color.a, 1.0f}; + case RgbaType::LINEAR: + return color * math::float4{color.a, color.a, color.a, 1.0f}; + case RgbaType::PREMULTIPLIED_sRGB: + return Color::toLinear(color); + case RgbaType::PREMULTIPLIED_LINEAR: + return color; + } +} + +} // namespace filament + +#endif // TNT_FILAMENT_COLOR_H diff --git a/ios/include/filament/ColorGrading.h b/ios/include/filament/ColorGrading.h new file mode 100644 index 00000000..be25267b --- /dev/null +++ b/ios/include/filament/ColorGrading.h @@ -0,0 +1,405 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! \file + +#ifndef TNT_FILAMENT_COLOR_GRADING_H +#define TNT_FILAMENT_COLOR_GRADING_H + +#include +#include + +#include + +#include + +namespace filament { + +class Engine; +class FColorGrading; + +/** + * ColorGrading is used to transform (either to modify or correct) the colors of the HDR buffer + * rendered by Filament. Color grading transforms are applied after lighting, and after any lens + * effects (bloom for instance), and include tone mapping. + * + * Creation, usage and destruction + * =============================== + * + * A ColorGrading object is created using the ColorGrading::Builder and destroyed by calling + * Engine::destroy(const ColorGrading*). A ColorGrading object is meant to be set on a View. + * + * ~~~~~~~~~~~{.cpp} + * filament::Engine* engine = filament::Engine::create(); + * + * filament::ColorGrading* colorGrading = filament::ColorGrading::Builder() + * .toneMapping(filament::ColorGrading::ToneMapping::ACES) + * .build(*engine); + * + * myView->setColorGrading(colorGrading); + * + * engine->destroy(colorGrading); + * ~~~~~~~~~~~ + * + * Performance + * =========== + * + * Creating a new ColorGrading object may be more expensive than other Filament objects as a + * 3D LUT may need to be generated. The generation of a 3D LUT, if necessary, may happen on + * the CPU. + * + * Ordering + * ======== + * + * The various transforms held by ColorGrading are applied in the following order: + * - Exposure + * - White balance + * - Channel mixer + * - Shadows/mid-tones/highlights + * - Slope/offset/power (CDL) + * - Contrast + * - Vibrance + * - Saturation + * - Curves + * - Tone mapping + * - Luminance scaling + * + * Defaults + * ======== + * + * Here are the default color grading options: + * - Exposure: 0.0 + * - White balance: temperature 0, and tint 0 + * - Channel mixer: red {1,0,0}, green {0,1,0}, blue {0,0,1} + * - Shadows/mid-tones/highlights: shadows {1,1,1,0}, mid-tones {1,1,1,0}, highlights {1,1,1,0}, + * ranges {0,0.333,0.550,1} + * - Slope/offset/power: slope 1.0, offset 0.0, and power 1.0 + * - Contrast: 1.0 + * - Vibrance: 1.0 + * - Saturation: 1.0 + * - Curves: gamma {1,1,1}, midPoint {1,1,1}, and scale {1,1,1} + * - Tone mapping: ACESLegacyToneMapper + * - Luminance scaling: false + * + * @see View + */ +class UTILS_PUBLIC ColorGrading : public FilamentAPI { + struct BuilderDetails; +public: + enum class QualityLevel : uint8_t { + LOW, + MEDIUM, + HIGH, + ULTRA + }; + + /** + * List of available tone-mapping operators. + * + * @deprecated Use Builder::toneMapper(ToneMapper*) instead + */ + enum class UTILS_DEPRECATED ToneMapping : uint8_t { + LINEAR = 0, //!< Linear tone mapping (i.e. no tone mapping) + ACES_LEGACY = 1, //!< ACES tone mapping, with a brightness modifier to match Filament's legacy tone mapper + ACES = 2, //!< ACES tone mapping + FILMIC = 3, //!< Filmic tone mapping, modelled after ACES but applied in sRGB space + DISPLAY_RANGE = 4, //!< Tone mapping used to validate/debug scene exposure + }; + + //! Use Builder to construct a ColorGrading object instance + class Builder : public BuilderBase { + friend struct BuilderDetails; + public: + Builder() noexcept; + Builder(Builder const& rhs) noexcept; + Builder(Builder&& rhs) noexcept; + ~Builder() noexcept; + Builder& operator=(Builder const& rhs) noexcept; + Builder& operator=(Builder&& rhs) noexcept; + + /** + * Sets the quality level of the color grading. When color grading is implemented using + * a 3D LUT, the quality level may impact the resolution and bit depth of the backing + * 3D texture. For instance, a low quality level will use a 16x16x16 10 bit LUT, a medium + * quality level will use a 32x32x32 10 bit LUT, a high quality will use a 32x32x32 16 bit + * LUT, and a ultra quality will use a 64x64x64 16 bit LUT. + * + * The default quality is medium. + * + * @param qualityLevel The desired quality of the color grading process + * + * @return This Builder, for chaining calls + */ + Builder& quality(QualityLevel qualityLevel) noexcept; + + /** + * Selects the tone mapping operator to apply to the HDR color buffer as the last + * operation of the color grading post-processing step. + * + * The default tone mapping operator is ACESLegacyToneMapper. + * + * The specified tone mapper must have a lifecycle that exceeds the lifetime of + * this builder. Since the build(Engine&) method is synchronous, it is safe to + * delete the tone mapper object after that finishes executing. + * + * @param toneMapper The tone mapping operator to apply to the HDR color buffer + * + * @return This Builder, for chaining calls + */ + Builder& toneMapper(const ToneMapper* toneMapper) noexcept; + + /** + * Selects the tone mapping operator to apply to the HDR color buffer as the last + * operation of the color grading post-processing step. + * + * The default tone mapping operator is ACES_LEGACY. + * + * @param toneMapping The tone mapping operator to apply to the HDR color buffer + * + * @return This Builder, for chaining calls + * + * @deprecated Use toneMapper(ToneMapper*) instead + */ + UTILS_DEPRECATED + Builder& toneMapping(ToneMapping toneMapping) noexcept; + + /** + * Enables or disables the luminance scaling component (LICH) from the exposure value + * invariant luminance system (EVILS). When this setting is enabled, pixels with high + * chromatic values will roll-off to white to offer a more natural rendering. This step + * also helps avoid undesirable hue skews caused by out of gamut colors clipped + * to the destination color gamut. + * + * When luminance scaling is enabled, tone mapping is performed on the luminance of each + * pixel instead of per-channel. + * + * @param luminanceScaling Enables or disables EVILS post-tone mapping + * + * @return This Builder, for chaining calls + */ + Builder& luminanceScaling(bool luminanceScaling) noexcept; + + /** + * Adjusts the exposure of this image. The exposure is specified in stops: + * each stop brightens (positive values) or darkens (negative values) the image by + * a factor of 2. This means that an exposure of 3 will brighten the image 8 times + * more than an exposure of 0 (2^3 = 8 and 2^0 = 1). Contrary to the camera's exposure, + * this setting is applied after all post-processing (bloom, etc.) are applied. + * + * @param exposure Value in EV stops. Can be negative, 0, or positive. + * + * @return This Builder, for chaining calls + */ + Builder& exposure(float exposure) noexcept; + + /** + * Adjusts the while balance of the image. This can be used to remove color casts + * and correct the appearance of the white point in the scene, or to alter the + * overall chromaticity of the image for artistic reasons (to make the image appear + * cooler or warmer for instance). + * + * The while balance adjustment is defined with two values: + * - Temperature, to modify the color temperature. This value will modify the colors + * on a blue/yellow axis. Lower values apply a cool color temperature, and higher + * values apply a warm color temperature. The lowest value, -1.0f, is equivalent to + * a temperature of 50,000K. The highest value, 1.0f, is equivalent to a temperature + * of 2,000K. + * - Tint, to modify the colors on a green/magenta axis. The lowest value, -1.0f, will + * apply a strong green cast, and the highest value, 1.0f, will apply a strong magenta + * cast. + * + * Both values are expected to be in the range [-1.0..+1.0]. Values outside of that + * range will be clipped to that range. + * + * @param temperature Modification on the blue/yellow axis, as a value between -1.0 and +1.0. + * @param tint Modification on the green/magenta axis, as a value between -1.0 and +1.0. + * + * @return This Builder, for chaining calls + */ + Builder& whiteBalance(float temperature, float tint) noexcept; + + /** + * The channel mixer adjustment modifies each output color channel using the specified + * mix of the source color channels. + * + * By default each output color channel is set to use 100% of the corresponding source + * channel and 0% of the other channels. For instance, the output red channel is set to + * {1.0, 0.0, 1.0} or 100% red, 0% green and 0% blue. + * + * Each output channel can add or subtract data from the source channel by using values + * in the range [-2.0..+2.0]. Values outside of that range will be clipped to that range. + * + * Using the channel mixer adjustment you can for instance create a monochrome output + * by setting all 3 output channels to the same mix. For instance: {0.4, 0.4, 0.2} for + * all 3 output channels(40% red, 40% green and 20% blue). + * + * More complex mixes can be used to create more complex effects. For instance, here is + * a mix that creates a sepia tone effect: + * - outRed = {0.255, 0.858, 0.087} + * - outGreen = {0.213, 0.715, 0.072} + * - outBlue = {0.170, 0.572, 0.058} + * + * @param outRed The mix of source RGB for the output red channel, between -2.0 and +2.0 + * @param outGreen The mix of source RGB for the output green channel, between -2.0 and +2.0 + * @param outBlue The mix of source RGB for the output blue channel, between -2.0 and +2.0 + * + * @return This Builder, for chaining calls + */ + Builder& channelMixer( + math::float3 outRed, math::float3 outGreen, math::float3 outBlue) noexcept; + + /** + * Adjusts the colors separately in 3 distinct tonal ranges or zones: shadows, mid-tones, + * and highlights. + * + * The tonal zones are by the ranges parameter: the x and y components define the beginning + * and end of the transition from shadows to mid-tones, and the z and w components define + * the beginning and end of the transition from mid-tones to highlights. + * + * A smooth transition is applied between the zones which means for instance that the + * correction color of the shadows range will partially apply to the mid-tones, and the + * other way around. This ensure smooth visual transitions in the final image. + * + * Each correction color is defined as a linear RGB color and a weight. The weight is a + * value (which may be positive or negative) that is added to the linear RGB color before + * mixing. This can be used to darken or brighten the selected tonal range. + * + * Shadows/mid-tones/highlights adjustment are performed linear space. + * + * @param shadows Linear RGB color (.rgb) and weight (.w) to apply to the shadows + * @param midtones Linear RGB color (.rgb) and weight (.w) to apply to the mid-tones + * @param highlights Linear RGB color (.rgb) and weight (.w) to apply to the highlights + * @param ranges Range of the shadows (x and y), and range of the highlights (z and w) + * + * @return This Builder, for chaining calls + */ + Builder& shadowsMidtonesHighlights( + math::float4 shadows, math::float4 midtones, math::float4 highlights, + math::float4 ranges) noexcept; + + /** + * Applies a slope, offset, and power, as defined by the ASC CDL (American Society of + * Cinematographers Color Decision List) to the image. The CDL can be used to adjust the + * colors of different tonal ranges in the image. + * + * The ASC CDL is similar to the lift/gamma/gain controls found in many color grading tools. + * Lift is equivalent to a combination of offset and slope, gain is equivalent to slope, + * and gamma is equivalent to power. + * + * The slope and power values must be strictly positive. Values less than or equal to 0 will + * be clamped to a small positive value, offset can be any positive or negative value. + * + * Version 1.2 of the ASC CDL adds saturation control, which is here provided as a separate + * API. See the saturation() method for more information. + * + * Slope/offset/power adjustments are performed in log space. + * + * @param slope Multiplier of the input color, must be a strictly positive number + * @param offset Added to the input color, can be a negative or positive number, including 0 + * @param power Power exponent of the input color, must be a strictly positive number + * + * @return This Builder, for chaining calls + */ + Builder& slopeOffsetPower(math::float3 slope, math::float3 offset, math::float3 power) noexcept; + + /** + * Adjusts the contrast of the image. Lower values decrease the contrast of the image + * (the tonal range is narrowed), and higher values increase the contrast of the image + * (the tonal range is widened). A value of 1.0 has no effect. + * + * The contrast is defined as a value in the range [0.0...2.0]. Values outside of that + * range will be clipped to that range. + * + * Contrast adjustment is performed in log space. + * + * @param contrast Contrast expansion, between 0.0 and 2.0. 1.0 leaves contrast unaffected + * + * @return This Builder, for chaining calls + */ + Builder& contrast(float contrast) noexcept; + + /** + * Adjusts the saturation of the image based on the input color's saturation level. + * Colors with a high level of saturation are less affected than colors with low saturation + * levels. + * + * Lower vibrance values decrease intensity of the colors present in the image, and + * higher values increase the intensity of the colors in the image. A value of 1.0 has + * no effect. + * + * The vibrance is defined as a value in the range [0.0...2.0]. Values outside of that + * range will be clipped to that range. + * + * Vibrance adjustment is performed in linear space. + * + * @param vibrance Vibrance, between 0.0 and 2.0. 1.0 leaves vibrance unaffected + * + * @return This Builder, for chaining calls + */ + Builder& vibrance(float vibrance) noexcept; + + /** + * Adjusts the saturation of the image. Lower values decrease intensity of the colors + * present in the image, and higher values increase the intensity of the colors in the + * image. A value of 1.0 has no effect. + * + * The saturation is defined as a value in the range [0.0...2.0]. Values outside of that + * range will be clipped to that range. + * + * Saturation adjustment is performed in linear space. + * + * @param saturation Saturation, between 0.0 and 2.0. 1.0 leaves saturation unaffected + * + * @return This Builder, for chaining calls + */ + Builder& saturation(float saturation) noexcept; + + /** + * Applies a curve to each RGB channel of the image. Each curve is defined by 3 values: + * a gamma value applied to the shadows only, a mid-point indicating where shadows stop + * and highlights start, and a scale factor for the highlights. + * + * The gamma and mid-point must be strictly positive values. If they are not, they will be + * clamped to a small positive value. The scale can be any negative of positive value. + * + * Curves are applied in linear space. + * + * @param shadowGamma Power value to apply to the shadows, must be strictly positive + * @param midPoint Mid-point defining where shadows stop and highlights start, must be strictly positive + * @param highlightScale Scale factor for the highlights, can be any negative or positive value + * + * @return This Builder, for chaining calls + */ + Builder& curves(math::float3 shadowGamma, math::float3 midPoint, math::float3 highlightScale) noexcept; + + /** + * Creates the ColorGrading object and returns a pointer to it. + * + * @param engine Reference to the filament::Engine to associate this ColorGrading with. + * + * @return pointer to the newly created object or nullptr if exceptions are disabled and + * an error occurred. + */ + ColorGrading* build(Engine& engine); + + private: + friend class FColorGrading; + }; +}; + +} // namespace filament + +#endif // TNT_FILAMENT_COLOR_GRADING_H diff --git a/ios/include/filament/DebugRegistry.h b/ios/include/filament/DebugRegistry.h new file mode 100644 index 00000000..018df14d --- /dev/null +++ b/ios/include/filament/DebugRegistry.h @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! \file + +#ifndef TNT_FILAMENT_DEBUG_H +#define TNT_FILAMENT_DEBUG_H + +#include + +#include + +#include + +#include + +namespace filament { + +/** + * A registry of runtime properties used exclusively for debugging + * + * Filament exposes a few properties that can be queried and set, which control certain debugging + * features of the engine. These properties can be set at runtime at anytime. + * + */ +class UTILS_PUBLIC DebugRegistry : public FilamentAPI { +public: + + /** + * Type of a property + */ + enum Type { + BOOL, INT, FLOAT, FLOAT2, FLOAT3, FLOAT4 + }; + + /** + * Information about a property + */ + struct Property { + const char* name; //!< property name + Type type; //!< property type + }; + + struct PropertyArray { + Property const* array; + size_t size; + }; + + /** + * Queries the list of all available properties. + * + * @return A pair containing a pointer to a Property array and the size of this array. + */ + PropertyArray getProperties() const noexcept; + + /** + * Queries whether a property exists + * @param name The name of the property to query + * @return true if the property exists, false otherwise + */ + bool hasProperty(const char* name) const noexcept; + + /** + * Queries the address of a property's data from its name + * @param name Name of the property we want the data address of + * @return Address of the data of the \p name property + * @{ + */ + void* getPropertyAddress(const char* name) noexcept; + + template + inline T* getPropertyAddress(const char* name) noexcept { + return static_cast(getPropertyAddress(name)); + } + + template + inline bool getPropertyAddress(const char* name, T** p) noexcept { + *p = getPropertyAddress(name); + return *p != nullptr; + } + /** @}*/ + + /** + * Set the value of a property + * @param name Name of the property to set the value of + * @param v Value to set + * @return true if the operation was successful, false otherwise. + * @{ + */ + bool setProperty(const char* name, bool v) noexcept; + bool setProperty(const char* name, int v) noexcept; + bool setProperty(const char* name, float v) noexcept; + bool setProperty(const char* name, math::float2 v) noexcept; + bool setProperty(const char* name, math::float3 v) noexcept; + bool setProperty(const char* name, math::float4 v) noexcept; + /** @}*/ + + /** + * Get the value of a property + * @param name Name of the property to get the value of + * @param v A pointer to a variable which will hold the result + * @return true if the call was successful and \p v was updated + * @{ + */ + bool getProperty(const char* name, bool* v) const noexcept; + bool getProperty(const char* name, int* v) const noexcept; + bool getProperty(const char* name, float* v) const noexcept; + bool getProperty(const char* name, math::float2* v) const noexcept; + bool getProperty(const char* name, math::float3* v) const noexcept; + bool getProperty(const char* name, math::float4* v) const noexcept; + /** @}*/ + +}; + + +} // namespace filament + +#endif /* TNT_FILAMENT_DEBUG_H */ diff --git a/ios/include/filament/Engine.h b/ios/include/filament/Engine.h new file mode 100644 index 00000000..d69d7364 --- /dev/null +++ b/ios/include/filament/Engine.h @@ -0,0 +1,543 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_ENGINE_H +#define TNT_FILAMENT_ENGINE_H + +#include + +#include + +namespace utils { +class Entity; +class EntityManager; +class JobSystem; +} // namespace utils + +namespace filament { + +class BufferObject; +class Camera; +class ColorGrading; +class DebugRegistry; +class Fence; +class IndexBuffer; +class IndirectLight; +class Material; +class MaterialInstance; +class Renderer; +class RenderTarget; +class Scene; +class Skybox; +class Stream; +class SwapChain; +class Texture; +class VertexBuffer; +class View; + +class LightManager; +class RenderableManager; +class TransformManager; + +/** + * Engine is filament's main entry-point. + * + * An Engine instance main function is to keep track of all resources created by the user and + * manage the rendering thread as well as the hardware renderer. + * + * To use filament, an Engine instance must be created first: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * #include + * using namespace filament; + * + * Engine* engine = Engine::create(); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Engine essentially represents (or is associated to) a hardware context + * (e.g. an OpenGL ES context). + * + * Rendering typically happens in an operating system's window (which can be full screen), such + * window is managed by a filament.Renderer. + * + * A typical filament render loop looks like this: + * + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * #include + * #include + * #include + * #include + * using namespace filament; + * + * Engine* engine = Engine::create(); + * SwapChain* swapChain = engine->createSwapChain(nativeWindow); + * Renderer* renderer = engine->createRenderer(); + * Scene* scene = engine->createScene(); + * View* view = engine->createView(); + * + * view->setScene(scene); + * + * do { + * // typically we wait for VSYNC and user input events + * if (renderer->beginFrame(swapChain)) { + * renderer->render(view); + * renderer->endFrame(); + * } + * } while (!quit); + * + * engine->destroy(view); + * engine->destroy(scene); + * engine->destroy(renderer); + * engine->destroy(swapChain); + * Engine::destroy(&engine); // clears engine* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Resource Tracking + * ================= + * + * Each Engine instance keeps track of all objects created by the user, such as vertex and index + * buffers, lights, cameras, etc... + * The user is expected to free those resources, however, leaked resources are freed when the + * engine instance is destroyed and a warning is emitted in the console. + * + * Thread safety + * ============= + * + * An Engine instance is not thread-safe. The implementation makes no attempt to synchronize + * calls to an Engine instance methods. + * If multi-threading is needed, synchronization must be external. + * + * Multi-threading + * =============== + * + * When created, the Engine instance starts a render thread as well as multiple worker threads, + * these threads have an elevated priority appropriate for rendering, based on the platform's + * best practices. The number of worker threads depends on the platform and is automatically + * chosen for best performance. + * + * On platforms with asymmetric cores (e.g. ARM's Big.Little), Engine makes some educated guesses + * as to which cores to use for the render thread and worker threads. For example, it'll try to + * keep an OpenGL ES thread on a Big core. + * + * Swap Chains + * =========== + * + * A swap chain represents an Operating System's *native* renderable surface. Typically it's a window + * or a view. Because a SwapChain is initialized from a native object, it is given to filament + * as a `void*`, which must be of the proper type for each platform filament is running on. + * + * @see SwapChain + * + * + * @see Renderer + */ +class UTILS_PUBLIC Engine { +public: + using Platform = backend::Platform; + using Backend = backend::Backend; + + /** + * Creates an instance of Engine + * + * @param backend Which driver backend to use. + * + * @param platform A pointer to an object that implements Platform. If this is + * provided, then this object is used to create the hardware context + * and expose platform features to it. + * + * If not provided (or nullptr is used), an appropriate Platform + * is created automatically. + * + * All methods of this interface are called from filament's + * render thread, which is different from the main thread. + * + * The lifetime of \p platform must exceed the lifetime of + * the Engine object. + * + * @param sharedGLContext A platform-dependant OpenGL context used as a shared context + * when creating filament's internal context. + * Setting this parameter will force filament to use the OpenGL + * implementation (instead of Vulkan for instance). + * + * + * @return A pointer to the newly created Engine, or nullptr if the Engine couldn't be created. + * + * nullptr if the GPU driver couldn't be initialized, for instance if it doesn't + * support the right version of OpenGL or OpenGL ES. + * + * @exception utils::PostConditionPanic can be thrown if there isn't enough memory to + * allocate the command buffer. If exceptions are disabled, this condition if fatal and + * this function will abort. + * + * \remark + * This method is thread-safe. + */ + static Engine* create(Backend backend = Backend::DEFAULT, + Platform* platform = nullptr, void* sharedGLContext = nullptr); + +#if UTILS_HAS_THREADING + /** + * A callback used with Engine::createAsync() called once the engine is initialized and it is + * safe to call Engine::getEngine(token). This callback is invoked from an arbitrary worker + * thread. Engine::getEngine() CANNOT be called from that thread, instead it must be called + * from the same thread than Engine::createAsync() was called from. + * + * @param user User provided parameter given in createAsync(). + * + * @param token An opaque token used to call Engine::getEngine(). + */ + using CreateCallback = void(void* user, void* token); + + /** + * Creates an instance of Engine asynchronously + * + * @param callback Callback called once the engine is initialized and it is safe to + * call Engine::getEngine. + * + * @param user A user provided pointer that is given back to callback unmodified. + * + * @param backend Which driver backend to use. + * + * @param platform A pointer to an object that implements Platform. If this is + * provided, then this object is used to create the hardware context + * and expose platform features to it. + * + * If not provided (or nullptr is used), an appropriate Platform + * is created automatically. + * + * All methods of this interface are called from filament's + * render thread, which is different from the main thread. + * + * The lifetime of \p platform must exceed the lifetime of + * the Engine object. + * + * @param sharedGLContext A platform-dependant OpenGL context used as a shared context + * when creating filament's internal context. + * Setting this parameter will force filament to use the OpenGL + * implementation (instead of Vulkan for instance). + */ + static void createAsync(CreateCallback callback, void* user, + Backend backend = Backend::DEFAULT, + Platform* platform = nullptr, void* sharedGLContext = nullptr); + + /** + * Retrieve an Engine* from createAsync(). This must be called from the same thread than + * Engine::createAsync() was called from. + * + * @param token An opaque token given in the createAsync() callback function. + * + * @return A pointer to the newly created Engine, or nullptr if the Engine couldn't be created. + * + * @exception utils::PostConditionPanic can be thrown if there isn't enough memory to + * allocate the command buffer. If exceptions are disabled, this condition if fatal and + * this function will abort. + */ + static Engine* getEngine(void* token); +#endif + + /** + * Destroy the Engine instance and all associated resources. + * + * Engine.destroy() should be called last and after all other resources have been destroyed, + * it ensures all filament resources are freed. + * + * Destroy performs the following tasks: + * 1. Destroy all internal software and hardware resources. + * 2. Free all user allocated resources that are not already destroyed and logs a warning. + * This indicates a "leak" in the user's code. + * 3. Terminate the rendering engine's thread. + * + * @param engine A pointer to the filament.Engine* to be destroyed. + * \p engine is cleared upon return. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * #include + * using namespace filament; + * + * Engine* engine = Engine::create(); + * Engine::destroy(&engine); // clears engine* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * \remark + * This method is thread-safe. + */ + static void destroy(Engine** engine); + + /** + * Destroy the Engine instance and all associated resources. + * + * Engine.destroy() should be called last and after all other resources have been destroyed, + * it ensures all filament resources are freed. + * + * Destroy performs the following tasks: + * 1. Destroy all internal software and hardware resources. + * 2. Free all user allocated resources that are not already destroyed and logs a warning. + * This indicates a "leak" in the user's code. + * 3. Terminate the rendering engine's thread. + * + * @param engine A pointer to the filament.Engine to be destroyed. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * #include + * using namespace filament; + * + * Engine* engine = Engine::create(); + * Engine::destroy(engine); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * \remark + * This method is thread-safe. + */ + static void destroy(Engine* engine); + + /** + * @return EntityManager used by filament + */ + utils::EntityManager& getEntityManager() noexcept; + + /** + * @return RenderableManager reference + */ + RenderableManager& getRenderableManager() noexcept; + + /** + * @return LightManager reference + */ + LightManager& getLightManager() noexcept; + + /** + * @return TransformManager reference + */ + TransformManager& getTransformManager() noexcept; + + /** + * Helper to enable accurate translations. + * If you need this Engine to handle a very large world space, one way to achieve this + * automatically is to enable accurate translations in the TransformManager. This helper + * provides a convenient way of doing that. + * This is typically called once just after creating the Engine. + */ + void enableAccurateTranslations() noexcept; + + /** + * Creates a SwapChain from the given Operating System's native window handle. + * + * @param nativeWindow An opaque native window handle. e.g.: on Android this is an + * `ANativeWindow*`. + * @param flags One or more configuration flags as defined in `SwapChain`. + * + * @return A pointer to the newly created SwapChain or nullptr if it couldn't be created. + * + * @see Renderer.beginFrame() + */ + SwapChain* createSwapChain(void* nativeWindow, uint64_t flags = 0) noexcept; + + + /** + * Creates a headless SwapChain. + * + * @param width Width of the drawing buffer in pixels. + * @param height Height of the drawing buffer in pixels. + * @param flags One or more configuration flags as defined in `SwapChain`. + * + * @return A pointer to the newly created SwapChain or nullptr if it couldn't be created. + * + * @see Renderer.beginFrame() + */ + SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags = 0) noexcept; + + /** + * Creates a renderer associated to this engine. + * + * A Renderer is intended to map to a *window* on screen. + * + * @return A pointer to the newly created Renderer or nullptr if it couldn't be created. + */ + Renderer* createRenderer() noexcept; + + /** + * Creates a View. + * + * @return A pointer to the newly created View or nullptr if it couldn't be created. + */ + View* createView() noexcept; + + /** + * Creates a Scene. + * + * @return A pointer to the newly created Scene or nullptr if it couldn't be created. + */ + Scene* createScene() noexcept; + + /** + * Creates a Camera component. + * + * @param entity Entity to add the camera component to. + * @return A pointer to the newly created Camera or nullptr if it couldn't be created. + */ + Camera* createCamera(utils::Entity entity) noexcept; + + /** + * Returns the Camera component of the given entity. + * + * @param entity An entity. + * @return A pointer to the Camera component for this entity or nullptr if the entity didn't + * have a Camera component. The pointer is valid until destroyCameraComponent() + * is called or the entity itself is destroyed. + */ + Camera* getCameraComponent(utils::Entity entity) noexcept; + + /** + * Destroys the Camera component associated with the given entity. + * + * @param entity An entity. + */ + void destroyCameraComponent(utils::Entity entity) noexcept; + + /** + * Creates a Fence. + * + * @return A pointer to the newly created Fence or nullptr if it couldn't be created. + */ + Fence* createFence() noexcept; + + bool destroy(const BufferObject* p); //!< Destroys a BufferObject object. + bool destroy(const VertexBuffer* p); //!< Destroys an VertexBuffer object. + bool destroy(const Fence* p); //!< Destroys a Fence object. + bool destroy(const IndexBuffer* p); //!< Destroys an IndexBuffer object. + bool destroy(const IndirectLight* p); //!< Destroys an IndirectLight object. + + /** + * Destroys a Material object + * @param p the material object to destroy + * @attention All MaterialInstance of the specified material must be destroyed before + * destroying it. + * @exception utils::PreConditionPanic is thrown if some MaterialInstances remain. + * no-op if exceptions are disabled and some MaterialInstances remain. + */ + bool destroy(const Material* p); + bool destroy(const MaterialInstance* p); //!< Destroys a MaterialInstance object. + bool destroy(const Renderer* p); //!< Destroys a Renderer object. + bool destroy(const Scene* p); //!< Destroys a Scene object. + bool destroy(const Skybox* p); //!< Destroys a SkyBox object. + bool destroy(const ColorGrading* p); //!< Destroys a ColorGrading object. + bool destroy(const SwapChain* p); //!< Destroys a SwapChain object. + bool destroy(const Stream* p); //!< Destroys a Stream object. + bool destroy(const Texture* p); //!< Destroys a Texture object. + bool destroy(const RenderTarget* p); //!< Destroys a RenderTarget object. + bool destroy(const View* p); //!< Destroys a View object. + void destroy(utils::Entity e); //!< Destroys all filament-known components from this entity + + /** + * Kicks the hardware thread (e.g. the OpenGL, Vulkan or Metal thread) and blocks until + * all commands to this point are executed. Note that this doesn't guarantee that the + * hardware is actually finished. + * + *

This is typically used right after destroying the SwapChain, + * in cases where a guarantee about the SwapChain destruction is needed in a + * timely fashion, such as when responding to Android's + * android.view.SurfaceHolder.Callback.surfaceDestroyed

+ */ + void flushAndWait(); + + void flush(); + + /** + * Returns the default Material. + * + * The default material is 80% white and uses the Material.Shading.LIT shading. + * + * @return A pointer to the default Material instance (a singleton). + */ + const Material* getDefaultMaterial() const noexcept; + + /** + * Returns the resolved backend. + */ + Backend getBackend() const noexcept; + + /** + * Returns the Platform object that belongs to this Engine. + * + * When Engine::create is called with no platform argument, Filament creates an appropriate + * Platform subclass automatically. The specific subclass created depends on the backend and + * OS. For example, when the OpenGL backend is used, the Platform object will be a descendant of + * OpenGLPlatform. + * + * dynamic_cast should be used to cast the returned Platform object into a specific subclass. + * Note that RTTI must be available to use dynamic_cast. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Platform* platform = engine->getPlatform(); + * // static_cast also works, but more dangerous. + * SpecificPlatform* specificPlatform = dynamic_cast(platform); + * specificPlatform->platformSpecificMethod(); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * When a custom Platform is passed to Engine::create, Filament will use it instead, and this + * method will return it. + * + * @return A pointer to the Platform object that was provided to Engine::create, or the + * Filament-created one. + */ + Platform* getPlatform() const noexcept; + + /** + * Allocate a small amount of memory directly in the command stream. The allocated memory is + * guaranteed to be preserved until the current command buffer is executed + * + * @param size size to allocate in bytes. This should be small (e.g. < 1 KB) + * @param alignment alignment requested + * @return a pointer to the allocated buffer or nullptr if no memory was available. + * + * @note there is no need to destroy this buffer, it will be freed automatically when + * the current command buffer is executed. + */ + void* streamAlloc(size_t size, size_t alignment = alignof(double)) noexcept; + + /** + * Invokes one iteration of the render loop, used only on single-threaded platforms. + * + * This should be called every time the windowing system needs to paint (e.g. at 60 Hz). + */ + void execute(); + + /** + * Retrieves the job system that the Engine has ownership over. + * + * @return JobSystem used by filament + */ + utils::JobSystem& getJobSystem() noexcept; + + DebugRegistry& getDebugRegistry() noexcept; + +protected: + //! \privatesection + Engine() noexcept = default; + ~Engine() = default; + +public: + //! \privatesection + Engine(Engine const&) = delete; + Engine(Engine&&) = delete; + Engine& operator=(Engine const&) = delete; + Engine& operator=(Engine&&) = delete; +}; + +} // namespace filament + +#endif // TNT_FILAMENT_ENGINE_H diff --git a/ios/include/filament/Exposure.h b/ios/include/filament/Exposure.h new file mode 100644 index 00000000..a1e545f7 --- /dev/null +++ b/ios/include/filament/Exposure.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! \file + +#ifndef TNT_FILAMENT_EXPOSURE_H +#define TNT_FILAMENT_EXPOSURE_H + +#include + +namespace filament { + +class Camera; + +/** + * A series of utilities to compute exposure, exposure value at ISO 100 (EV100), + * luminance and illuminance using a physically-based camera model. + */ +namespace Exposure { + +/** + * Returns the exposure value (EV at ISO 100) of the specified camera. + */ +UTILS_PUBLIC +float ev100(const Camera& camera) noexcept; + +/** + * Returns the exposure value (EV at ISO 100) of the specified exposure parameters. + */ +UTILS_PUBLIC +float ev100(float aperture, float shutterSpeed, float sensitivity) noexcept; + +/** + * Returns the exposure value (EV at ISO 100) for the given average luminance (in @f$ \frac{cd}{m^2} @f$). + */ +UTILS_PUBLIC +float ev100FromLuminance(float luminance) noexcept; + +/** +* Returns the exposure value (EV at ISO 100) for the given illuminance (in lux). +*/ +UTILS_PUBLIC +float ev100FromIlluminance(float illuminance) noexcept; + +/** + * Returns the photometric exposure for the specified camera. + */ +UTILS_PUBLIC +float exposure(const Camera& camera) noexcept; + +/** + * Returns the photometric exposure for the specified exposure parameters. + * This function is equivalent to calling `exposure(ev100(aperture, shutterSpeed, sensitivity))` + * but is slightly faster and offers higher precision. + */ +UTILS_PUBLIC +float exposure(float aperture, float shutterSpeed, float sensitivity) noexcept; + +/** + * Returns the photometric exposure for the given EV100. + */ +UTILS_PUBLIC +float exposure(float ev100) noexcept; + +/** + * Returns the incident luminance in @f$ \frac{cd}{m^2} @f$ for the specified camera acting as a spot meter. + */ +UTILS_PUBLIC +float luminance(const Camera& camera) noexcept; + +/** + * Returns the incident luminance in @f$ \frac{cd}{m^2} @f$ for the specified exposure parameters of + * a camera acting as a spot meter. + * This function is equivalent to calling `luminance(ev100(aperture, shutterSpeed, sensitivity))` + * but is slightly faster and offers higher precision. + */ +UTILS_PUBLIC +float luminance(float aperture, float shutterSpeed, float sensitivity) noexcept; + +/** + * Converts the specified EV100 to luminance in @f$ \frac{cd}{m^2} @f$. + * EV100 is not a measure of luminance, but an EV100 can be used to denote a + * luminance for which a camera would use said EV100 to obtain the nominally + * correct exposure + */ +UTILS_PUBLIC +float luminance(float ev100) noexcept; + +/** + * Returns the illuminance in lux for the specified camera acting as an incident light meter. + */ +UTILS_PUBLIC +float illuminance(const Camera& camera) noexcept; + +/** + * Returns the illuminance in lux for the specified exposure parameters of + * a camera acting as an incident light meter. + * This function is equivalent to calling `illuminance(ev100(aperture, shutterSpeed, sensitivity))` + * but is slightly faster and offers higher precision. + */ +UTILS_PUBLIC +float illuminance(float aperture, float shutterSpeed, float sensitivity) noexcept; + +/** + * Converts the specified EV100 to illuminance in lux. + * EV100 is not a measure of illuminance, but an EV100 can be used to denote an + * illuminance for which a camera would use said EV100 to obtain the nominally + * correct exposure. + */ +UTILS_PUBLIC +float illuminance(float ev100) noexcept; + +} // namespace exposure +} // namespace filament + +#endif // TNT_FILAMENT_EXPOSURE_H diff --git a/ios/include/filament/Fence.h b/ios/include/filament/Fence.h new file mode 100644 index 00000000..f54db873 --- /dev/null +++ b/ios/include/filament/Fence.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! \file + +#ifndef TNT_FILAMENT_FENCE_H +#define TNT_FILAMENT_FENCE_H + +#include + +#include + +#include + +namespace filament { + +/** + * Fence is used to synchronize rendering operations together, with the CPU or with compute. + * + * \note + * Currently Fence only provide client-side synchronization. + * + */ +class UTILS_PUBLIC Fence : public FilamentAPI { +public: + //! Special \p timeout value to disable wait()'s timeout. + static constexpr uint64_t FENCE_WAIT_FOR_EVER = backend::FENCE_WAIT_FOR_EVER; + + //! Error codes for Fence::wait() + using FenceStatus = backend::FenceStatus; + + /** Mode controls the behavior of the command stream when calling wait() + * + * @attention + * It would be unwise to call `wait(..., Mode::DONT_FLUSH)` from the same thread + * the Fence was created, as it would most certainly create a dead-lock. + */ + enum class Mode : uint8_t { + FLUSH, //!< The command stream is flushed + DONT_FLUSH //!< The command stream is not flushed + }; + + /** + * Client-side wait on the Fence. + * + * Blocks the current thread until the Fence signals. + * + * @param mode Whether the command stream is flushed before waiting or not. + * @param timeout Wait time out. Using a \p timeout of 0 is a way to query the state of the fence. + * A \p timeout value of FENCE_WAIT_FOR_EVER is used to disable the timeout. + * @return FenceStatus::CONDITION_SATISFIED on success, + * FenceStatus::TIMEOUT_EXPIRED if the time out expired or + * FenceStatus::ERROR in other cases. + * @see #Mode + */ + FenceStatus wait(Mode mode = Mode::FLUSH, uint64_t timeout = FENCE_WAIT_FOR_EVER); + + /** + * Client-side wait on a Fence and destroy the Fence. + * + * @param fence Fence object to wait on. + * + * @param mode Whether the command stream is flushed before waiting or not. + * + * @return FenceStatus::CONDITION_SATISFIED on success, + * FenceStatus::ERROR otherwise. + */ + static FenceStatus waitAndDestroy(Fence* fence, Mode mode = Mode::FLUSH); +}; + +} // namespace filament + +#endif // TNT_FILAMENT_FENCE_H diff --git a/ios/include/filament/FilamentAPI.h b/ios/include/filament/FilamentAPI.h new file mode 100644 index 00000000..8b89a8c3 --- /dev/null +++ b/ios/include/filament/FilamentAPI.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_FILAMENT_API_H +#define TNT_FILAMENT_FILAMENT_API_H + +#include + +#include + +namespace filament { + +/** + * \privatesection + * FilamentAPI is used to define an API in filament. + * It ensures the class defining the API can't be created, destroyed + * or copied by the caller. + */ +class UTILS_PUBLIC FilamentAPI { +protected: + // disallow creation on the stack + FilamentAPI() noexcept = default; + ~FilamentAPI() = default; + +public: + // disallow copy and assignment + FilamentAPI(FilamentAPI const&) = delete; + FilamentAPI(FilamentAPI&&) = delete; + FilamentAPI& operator=(FilamentAPI const&) = delete; + FilamentAPI& operator=(FilamentAPI&&) = delete; + + // allow placement-new allocation, don't use "noexcept", to avoid compiler null check + static void *operator new (size_t, void* p) { return p; } + + // prevent heap allocation + static void *operator new (size_t) = delete; + static void *operator new[] (size_t) = delete; + static void operator delete (void*) = delete; + static void operator delete[](void*) = delete; +}; + + +/** + * \privatesection + * BuilderBase is used to hide the implementation details of builders and ensure a higher + * level of backward binary compatibility. + * The actual implementation is in src/FilamentAPI-impl.h" + */ +template +class BuilderBase { +public: + // none of these methods must be implemented inline because it's important that their + // implementation be hidden from the public headers. + template + explicit BuilderBase(ARGS&& ...) noexcept; + BuilderBase() noexcept; + ~BuilderBase() noexcept; + BuilderBase(BuilderBase const& rhs) noexcept; + BuilderBase& operator = (BuilderBase const& rhs) noexcept; + + // move ctor and copy operator can be implemented inline and don't need to be exported + BuilderBase(BuilderBase&& rhs) noexcept : mImpl(rhs.mImpl) { rhs.mImpl = nullptr; } + BuilderBase& operator = (BuilderBase&& rhs) noexcept { + auto temp = mImpl; + mImpl = rhs.mImpl; + rhs.mImpl = temp; + return *this; + } + +protected: + T* mImpl = nullptr; + inline T* operator->() noexcept { return mImpl; } + inline T const* operator->() const noexcept { return mImpl; } +}; + +} // namespace filament + +#endif // TNT_FILAMENT_FILAMENT_API_H diff --git a/ios/include/filament/Frustum.h b/ios/include/filament/Frustum.h new file mode 100644 index 00000000..3ab31d0e --- /dev/null +++ b/ios/include/filament/Frustum.h @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! \file + +#ifndef TNT_FILAMENT_FRUSTUM_H +#define TNT_FILAMENT_FRUSTUM_H + +#include + +#include +#include + +#include // Because we define NEAR and FAR in the Plane enum. + +namespace filament { + +class Box; +class Culler; + +/** + * A frustum defined by six planes + */ +class UTILS_PUBLIC Frustum { +public: + enum class Plane : uint8_t { + LEFT, + RIGHT, + BOTTOM, + TOP, + FAR, + NEAR + }; + + Frustum() = default; + Frustum(const Frustum& rhs) = default; + Frustum(Frustum&& rhs) noexcept = default; + Frustum& operator=(const Frustum& rhs) = default; + Frustum& operator=(Frustum&& rhs) noexcept = default; + + /** + * Creates a frustum from a projection matrix (usually the projection * view matrix) + * @param pv a 4x4 projection matrix + */ + explicit Frustum(const math::mat4f& pv); + + /** + * Creates a frustum from 8 corner coordinates. + * @param corners the corners of the frustum + * + * The corners should be specified in this order: + * 0. far bottom left + * 1. far bottom right + * 2. far top left + * 3. far top right + * 4. near bottom left + * 5. near bottom right + * 6. near top left + * 7. near top right + * + * 2----3 + * /| /| + * 6----7 | + * | 0--|-1 far + * |/ |/ / + * 4----5 near + * + */ + explicit Frustum(const math::float3 corners[8]); + + /** + * Sets the frustum from the given projection matrix + * @param pv a 4x4 projection matrix + */ + void setProjection(const math::mat4f& pv); + + /** + * Returns the plane equation parameters with normalized normals + * @param plane Identifier of the plane to retrieve the equation of + * @return A plane equation encoded a float4 R such as R.x*x + R.y*y + R.z*z + R.w = 0 + */ + math::float4 getNormalizedPlane(Plane plane) const noexcept; + + /** + * Returns a copy of all six frustum planes in left, right, bottom, top, far, near order + * @param planes six plane equations encoded as in getNormalizedPlane() in + * left, right, bottom, top, far, near order + */ + void getNormalizedPlanes(math::float4 planes[6]) const noexcept; + + /** + * Returns all six frustum planes in left, right, bottom, top, far, near order + * @return six plane equations encoded as in getNormalizedPlane() in + * left, right, bottom, top, far, near order + */ + math::float4 const* getNormalizedPlanes() const noexcept { return mPlanes; } + + /** + * Returns whether a box intersects the frustum (i.e. is visible) + * @param box The box to test against the frustum + * @return true if the box may intersects the frustum, false otherwise. In some situations + * a box that doesn't intersect the frustum might be reported as though it does. However, + * a box that does intersect the frustum is always reported correctly (true). + */ + bool intersects(const Box& box) const noexcept; + + /** + * Returns whether a sphere intersects the frustum (i.e. is visible) + * @param sphere A sphere encoded as a center + radius. + * @return true if the sphere may intersects the frustum, false otherwise. In some situations + * a sphere that doesn't intersect the frustum might be reported as though it does. However, + * a sphere that does intersect the frustum is always reported correctly (true). + */ + bool intersects(const math::float4& sphere) const noexcept; + + /** + * Returns whether the frustum contains a given point. + * @param p the point to test + * @return the maximum signed distance to the frustum. Negative if p is inside. + */ + float contains(math::float3 p) const noexcept; + +private: + friend class Culler; + math::float4 mPlanes[6]; +}; + + +} // namespace filament + +#endif // TNT_FILAMENT_FRUSTUM_H diff --git a/ios/include/filament/IndexBuffer.h b/ios/include/filament/IndexBuffer.h new file mode 100644 index 00000000..09b47929 --- /dev/null +++ b/ios/include/filament/IndexBuffer.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! \file + +#ifndef TNT_FILAMENT_INDEXBUFFER_H +#define TNT_FILAMENT_INDEXBUFFER_H + +#include + +#include + +#include + +#include + +#include + +namespace filament { + +class FIndexBuffer; + +class Engine; + +/** + * A buffer containing vertex indices into a VertexBuffer. Indices can be 16 or 32 bit. + * The buffer itself is a GPU resource, therefore mutating the data can be relatively slow. + * Typically these buffers are constant. + * + * It is possible, and even encouraged, to use a single index buffer for several Renderables. + * + * @see VertexBuffer, RenderableManager + */ +class UTILS_PUBLIC IndexBuffer : public FilamentAPI { + struct BuilderDetails; + +public: + using BufferDescriptor = backend::BufferDescriptor; + + /** + * Type of the index buffer + */ + enum class IndexType : uint8_t { + USHORT = uint8_t(backend::ElementType::USHORT), //!< 16-bit indices + UINT = uint8_t(backend::ElementType::UINT), //!< 32-bit indices + }; + + class Builder : public BuilderBase { + friend struct BuilderDetails; + public: + Builder() noexcept; + Builder(Builder const& rhs) noexcept; + Builder(Builder&& rhs) noexcept; + ~Builder() noexcept; + Builder& operator=(Builder const& rhs) noexcept; + Builder& operator=(Builder&& rhs) noexcept; + + /** + * Size of the index buffer in elements. + * @param indexCount Number of indices the IndexBuffer can hold. + * @return A reference to this Builder for chaining calls. + */ + Builder& indexCount(uint32_t indexCount) noexcept; + + /** + * Type of the index buffer, 16-bit or 32-bit. + * @param indexType Type of indices stored in the IndexBuffer. + * @return A reference to this Builder for chaining calls. + */ + Builder& bufferType(IndexType indexType) noexcept; + + /** + * Creates the IndexBuffer object and returns a pointer to it. After creation, the index + * buffer is uninitialized. Use IndexBuffer::setBuffer() to initialize the IndexBuffer. + * + * @param engine Reference to the filament::Engine to associate this IndexBuffer with. + * + * @return pointer to the newly created object or nullptr if exceptions are disabled and + * an error occurred. + * + * @exception utils::PostConditionPanic if a runtime error occurred, such as running out of + * memory or other resources. + * @exception utils::PreConditionPanic if a parameter to a builder function was invalid. + * + * @see IndexBuffer::setBuffer + */ + IndexBuffer* build(Engine& engine); + private: + friend class FIndexBuffer; + }; + + /** + * Asynchronously copy-initializes a region of this IndexBuffer from the data provided. + * + * @param engine Reference to the filament::Engine to associate this IndexBuffer with. + * @param buffer A BufferDescriptor representing the data used to initialize the IndexBuffer. + * BufferDescriptor points to raw, untyped data that will be interpreted as + * either 16-bit or 32-bits indices based on the Type of this IndexBuffer. + * @param byteOffset Offset in *bytes* into the IndexBuffer + */ + void setBuffer(Engine& engine, BufferDescriptor&& buffer, uint32_t byteOffset = 0); + + /** + * Returns the size of this IndexBuffer in elements. + * @return The number of indices the IndexBuffer holds. + */ + size_t getIndexCount() const noexcept; +}; + +} // namespace filament + +#endif // TNT_FILAMENT_INDEXBUFFER_H diff --git a/ios/include/filament/IndirectLight.h b/ios/include/filament/IndirectLight.h new file mode 100644 index 00000000..166dcc2b --- /dev/null +++ b/ios/include/filament/IndirectLight.h @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! \file + +#ifndef TNT_FILAMENT_INDIRECT_LIGHT_H +#define TNT_FILAMENT_INDIRECT_LIGHT_H + +#include + +#include + +#include + +namespace filament { + +class Engine; +class Texture; + +class FIndirectLight; + +/** + * IndirectLight is used to simulate environment lighting, a form of global illumination. + * + * Environment lighting has a two components: + * 1. irradiance + * 2. reflections (specular component) + * + * Environments are usually captured as high-resolution HDR equirectangular images and processed + * by the **cmgen** tool to generate the data needed by IndirectLight. + * + * @note + * Currently IndirectLight is intended to be used for "distant probes", that is, to represent + * global illumination from a distant (i.e. at infinity) environment, such as the sky or distant + * mountains. Only a single IndirectLight can be used in a Scene. This limitation will be lifted + * in the future. + * + * Creation and destruction + * ======================== + * + * An IndirectLight object is created using the IndirectLight::Builder and destroyed by calling + * Engine::destroy(const IndirectLight*). + * + * ~~~~~~~~~~~{.cpp} + * filament::Engine* engine = filament::Engine::create(); + * + * filament::IndirectLight* environment = filament::IndirectLight::Builder() + * .reflections(cubemap) + * .build(*engine); + * + * engine->destroy(environment); + * ~~~~~~~~~~~ + * + * + * Irradiance + * ========== + * + * The irradiance represents the light that comes from the environment and shines an + * object's surface. + * + * The irradiance is calculated automatically from the Reflections (see below), and generally + * doesn't need to be provided explicitly. However, it can be provided separately from the + * Reflections as + * [spherical harmonics](https://en.wikipedia.org/wiki/Spherical_harmonics) (SH) of 1, 2 or + * 3 bands, respectively 1, 4 or 9 coefficients. + * + * @note + * Use the **cmgen** tool to generate the `SH` for a given environment. + * + * Reflections + * =========== + * + * The reflections on object surfaces (specular component) is calculated from a specially + * filtered cubemap pyramid generated by the **cmgen** tool. + * + * + * @see Scene, Light, Texture, Skybox + */ +class UTILS_PUBLIC IndirectLight : public FilamentAPI { + struct BuilderDetails; + +public: + + //! Use Builder to construct an IndirectLight object instance + class Builder : public BuilderBase { + friend struct BuilderDetails; + public: + Builder() noexcept; + Builder(Builder const& rhs) noexcept; + Builder(Builder&& rhs) noexcept; + ~Builder() noexcept; + Builder& operator=(Builder const& rhs) noexcept; + Builder& operator=(Builder&& rhs) noexcept; + + /** + * Set the reflections cubemap mipmap chain. + * + * @param cubemap A mip-mapped cubemap generated by **cmgen**. Each cubemap level + * encodes a the irradiance for a roughness level. + * + * @return This Builder, for chaining calls. + * + */ + Builder& reflections(Texture const* cubemap) noexcept; + + /** + * Sets the irradiance as Spherical Harmonics. + * + * The irradiance must be pre-convolved by \f$ \langle n \cdot l \rangle \f$ and + * pre-multiplied by the Lambertian diffuse BRDF \f$ \frac{1}{\pi} \f$ and + * specified as Spherical Harmonics coefficients. + * + * Additionally, these Spherical Harmonics coefficients must be pre-scaled by the + * reconstruction factors \f$ A_{l}^{m} \f$ below. + * + * The final coefficients can be generated using the `cmgen` tool. + * + * The index in the \p sh array is given by: + * + * `index(l, m) = l * (l + 1) + m` + * + * \f$ sh[index(l,m)] = L_{l}^{m} \frac{1}{\pi} A_{l}^{m} \hat{C_{l}} \f$ + * + * index | l | m | \f$ A_{l}^{m} \f$ | \f$ \hat{C_{l}} \f$ | \f$ \frac{1}{\pi} A_{l}^{m}\hat{C_{l}} \f$ | + * :-----:|:---:|:---:|:------------------:|:---------------------:|:--------------------------------------------: + * 0 | 0 | 0 | 0.282095 | 3.1415926 | 0.282095 + * 1 | 1 | -1 | -0.488602 | 2.0943951 | -0.325735 + * 2 | ^ | 0 | 0.488602 | ^ | 0.325735 + * 3 | ^ | 1 | -0.488602 | ^ | -0.325735 + * 4 | 2 | -2 | 1.092548 | 0.785398 | 0.273137 + * 5 | ^ | -1 | -1.092548 | ^ | -0.273137 + * 6 | ^ | 0 | 0.315392 | ^ | 0.078848 + * 7 | ^ | 1 | -1.092548 | ^ | -0.273137 + * 8 | ^ | 2 | 0.546274 | ^ | 0.136569 + * + * + * Only 1, 2 or 3 bands are allowed. + * + * @param bands Number of spherical harmonics bands. Must be 1, 2 or 3. + * @param sh Array containing the spherical harmonics coefficients. + * The size of the array must be \f$ bands^{2} \f$. + * (i.e. 1, 4 or 9 coefficients respectively). + * + * @return This Builder, for chaining calls. + * + * @note + * Because the coefficients are pre-scaled, `sh[0]` is the environment's + * average irradiance. + */ + Builder& irradiance(uint8_t bands, math::float3 const* sh) noexcept; + + /** + * Sets the irradiance from the radiance expressed as Spherical Harmonics. + * + * The radiance must be specified as Spherical Harmonics coefficients \f$ L_{l}^{m} \f$ + * + * The index in the \p sh array is given by: + * + * `index(l, m) = l * (l + 1) + m` + * + * \f$ sh[index(l,m)] = L_{l}^{m} \f$ + * + * index | l | m + * :-----:|:---:|:---: + * 0 | 0 | 0 + * 1 | 1 | -1 + * 2 | ^ | 0 + * 3 | ^ | 1 + * 4 | 2 | -2 + * 5 | ^ | -1 + * 6 | ^ | 0 + * 7 | ^ | 1 + * 8 | ^ | 2 + * + * @param bands Number of spherical harmonics bands. Must be 1, 2 or 3. + * @param sh Array containing the spherical harmonics coefficients. + * The size of the array must be \f$ bands^{2} \f$. + * (i.e. 1, 4 or 9 coefficients respectively). + * + * @return This Builder, for chaining calls. + */ + Builder& radiance(uint8_t bands, math::float3 const* sh) noexcept; + + /** + * Sets the irradiance as a cubemap. + * + * The irradiance can alternatively be specified as a cubemap instead of Spherical + * Harmonics coefficients. It may or may not be more efficient, depending on your + * hardware (essentially, it's trading ALU for bandwidth). + * + * @param cubemap Cubemap representing the Irradiance pre-convolved by + * \f$ \langle n \cdot l \rangle \f$. + * + * @return This Builder, for chaining calls. + * + * @note + * This irradiance cubemap can be generated with the **cmgen** tool. + * + * @see irradiance(uint8_t bands, math::float3 const* sh) + */ + Builder& irradiance(Texture const* cubemap) noexcept; + + /** + * (optional) Environment intensity. + * + * Because the environment is encoded usually relative to some reference, the + * range can be adjusted with this method. + * + * @param envIntensity Scale factor applied to the environment and irradiance such that + * the result is in lux, or lumen/m^2 (default = 30000) + * + * @return This Builder, for chaining calls. + */ + Builder& intensity(float envIntensity) noexcept; + + /** + * Specifies the rigid-body transformation to apply to the IBL. + * + * @param rotation 3x3 rotation matrix. Must be a rigid-body transform. + * + * @return This Builder, for chaining calls. + */ + Builder& rotation(math::mat3f const& rotation) noexcept; + + /** + * Creates the IndirectLight object and returns a pointer to it. + * + * @param engine Reference to the filament::Engine to associate this IndirectLight with. + * + * @return pointer to the newly created object or nullptr if exceptions are disabled and + * an error occurred. + * + * @exception utils::PostConditionPanic if a runtime error occurred, such as running out of + * memory or other resources. + * @exception utils::PreConditionPanic if a parameter to a builder function was invalid. + */ + IndirectLight* build(Engine& engine); + + private: + friend class FIndirectLight; + }; + + /** + * Sets the environment's intensity. + * + * Because the environment is encoded usually relative to some reference, the + * range can be adjusted with this method. + * + * @param intensity Scale factor applied to the environment and irradiance such that + * the result is in lux, or lumen/m^2(default = 30000) + */ + void setIntensity(float intensity) noexcept; + + /** + * Returns the environment's intensity in lux, or lumen/m^2. + */ + float getIntensity() const noexcept; + + /** + * Sets the rigid-body transformation to apply to the IBL. + * + * @param rotation 3x3 rotation matrix. Must be a rigid-body transform. + */ + void setRotation(math::mat3f const& rotation) noexcept; + + /** + * Returns the rigid-body transformation applied to the IBL. + */ + const math::mat3f& getRotation() const noexcept; + + /** + * Returns the associated reflection map, or null if it does not exist. + */ + Texture const* getReflectionsTexture() const noexcept; + + /** + * Returns the associated irradiance map, or null if it does not exist. + */ + Texture const* getIrradianceTexture() const noexcept; + + /** + * Helper to estimate the direction of the dominant light in the environment represented by + * spherical harmonics. + * + * This assumes that there is only a single dominant light (such as the sun in outdoors + * environments), if it's not the case the direction returned will be an average of the + * various lights based on their intensity. + * + * If there are no clear dominant light, as is often the case with low dynamic range (LDR) + * environments, this method may return a wrong or unexpected direction. + * + * The dominant light direction can be used to set a directional light's direction, + * for instance to produce shadows that match the environment. + * + * @param sh 3-band spherical harmonics + * + * @return A unit vector representing the direction of the dominant light + * + * @see LightManager::Builder::direction() + * @see getColorEstimate() + */ + static math::float3 getDirectionEstimate(const math::float3 sh[9]) noexcept; + + /** + * Helper to estimate the color and relative intensity of the environment represented by + * spherical harmonics in a given direction. + * + * This can be used to set the color and intensity of a directional light. In this case + * make sure to multiply this relative intensity by the the intensity of this indirect light. + * + * @param sh 3-band spherical harmonics + * @param direction a unit vector representing the direction of the light to estimate the + * color of. Typically this the value returned by getDirectionEstimate(). + * + * @return A vector of 4 floats where the first 3 components represent the linear color and + * the 4th component represents the intensity of the dominant light + * + * @see LightManager::Builder::color() + * @see LightManager::Builder::intensity() + * @see getDirectionEstimate, getIntensity, setIntensity + */ + static math::float4 getColorEstimate(const math::float3 sh[9], math::float3 direction) noexcept; + + + /** @deprecated use static versions instead */ + UTILS_DEPRECATED + math::float3 getDirectionEstimate() const noexcept; + + /** @deprecated use static versions instead */ + UTILS_DEPRECATED + math::float4 getColorEstimate(math::float3 direction) const noexcept; +}; + +} // namespace filament + +#endif // TNT_FILAMENT_INDIRECT_LIGHT_H diff --git a/ios/include/filament/LightManager.h b/ios/include/filament/LightManager.h new file mode 100644 index 00000000..eb542da2 --- /dev/null +++ b/ios/include/filament/LightManager.h @@ -0,0 +1,938 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_LIGHTMANAGER_H +#define TNT_FILAMENT_LIGHTMANAGER_H + +#include +#include + +#include +#include +#include + +#include + +namespace utils { + class Entity; +} // namespace utils + +namespace filament { + +class Engine; +class FEngine; +class FLightManager; + +/** + * LightManager allows to create a light source in the scene, such as a sun or street lights. + * + * At least one light must be added to a scene in order to see anything + * (unless the Material.Shading.UNLIT is used). + * + * + * Creation and destruction + * ======================== + * + * A Light component is created using the LightManager::Builder and destroyed by calling + * LightManager::destroy(utils::Entity). + * + * ~~~~~~~~~~~{.cpp} + * filament::Engine* engine = filament::Engine::create(); + * utils::Entity sun = utils::EntityManager.get().create(); + * + * filament::LightManager::Builder(Type::SUN) + * .castShadows(true) + * .build(*engine, sun); + * + * engine->getLightManager().destroy(sun); + * ~~~~~~~~~~~ + * + * + * Light types + * =========== + * + * Lights come in three flavors: + * - directional lights + * - point lights + * - spot lights + * + * + * Directional lights + * ------------------ + * + * Directional lights have a direction, but don't have a position. All light rays are + * parallel and come from infinitely far away and from everywhere. Typically a directional light + * is used to simulate the sun. + * + * Directional lights and spot lights are able to cast shadows. + * + * To create a directional light use Type.DIRECTIONAL or Type.SUN, both are similar, but the later + * also draws a sun's disk in the sky and its reflection on glossy objects. + * + * @warning Currently, only a single directional light is supported. If several directional lights + * are added to the scene, the dominant one will be used. + * + * @see Builder.direction(), Builder.sunAngularRadius() + * + * Point lights + * ------------ + * + * Unlike directional lights, point lights have a position but emit light in all directions. + * The intensity of the light diminishes with the inverse square of the distance to the light. + * Builder.falloff() controls distance beyond which the light has no more influence. + * + * A scene can have multiple point lights. + * + * @see Builder.position(), Builder.falloff() + * + * Spot lights + * ----------- + * + * Spot lights are similar to point lights but the light it emits is limited to a cone defined by + * Builder.spotLightCone() and the light's direction. + * + * A spot light is therefore defined by a position, a direction and inner and outer cones. The + * spot light's influence is limited to inside the outer cone. The inner cone defines the light's + * falloff attenuation. + * + * A physically correct spot light is a little difficult to use because changing the outer angle + * of the cone changes the illumination levels, as the same amount of light is spread over a + * changing volume. The coupling of illumination and the outer cone means that an artist cannot + * tweak the influence cone of a spot light without also changing the perceived illumination. + * It therefore makes sense to provide artists with a parameter to disable this coupling. This + * is the difference between Type.FOCUSED_SPOT and Type.SPOT. + * + * @see Builder.position(), Builder.direction(), Builder.falloff(), Builder.spotLightCone() + * + * Performance considerations + * ========================== + * + * Generally, adding lights to the scene hurts performance, however filament is designed to be + * able to handle hundreds of lights in a scene under certain conditions. Here are some tips + * to keep performances high. + * + * 1. Prefer spot lights to point lights and use the smallest outer cone angle possible. + * + * 2. Use the smallest possible falloff distance for point and spot lights. + * Performance is very sensitive to overlapping lights. The falloff distance essentially + * defines a sphere of influence for the light, so try to position point and spot lights + * such that they don't overlap too much. + * + * On the other hand, a scene can contain hundreds of non overlapping lights without + * incurring a significant overhead. + * + */ +class UTILS_PUBLIC LightManager : public FilamentAPI { + struct BuilderDetails; + +public: + using Instance = utils::EntityInstance; + + /** + * Returns the number of component in the LightManager, not that component are not + * guaranteed to be active. Use the EntityManager::isAlive() before use if needed. + * + * @return number of component in the LightManager + */ + size_t getComponentCount() const noexcept; + + /** + * Returns the list of Entity for all components. Use getComponentCount() to know the size + * of the list. + * @return a pointer to Entity + */ + utils::Entity const* getEntities() const noexcept; + + /** + * Returns whether a particular Entity is associated with a component of this LightManager + * @param e An Entity. + * @return true if this Entity has a component associated with this manager. + */ + bool hasComponent(utils::Entity e) const noexcept; + + /** + * Gets an Instance representing the Light component associated with the given Entity. + * @param e An Entity. + * @return An Instance object, which represents the Light component associated with the Entity e. + * @note Use Instance::isValid() to make sure the component exists. + * @see hasComponent() + */ + Instance getInstance(utils::Entity e) const noexcept; + + // destroys this component from the given entity + void destroy(utils::Entity e) noexcept; + + + //! Denotes the type of the light being created. + enum class Type : uint8_t { + SUN, //!< Directional light that also draws a sun's disk in the sky. + DIRECTIONAL, //!< Directional light, emits light in a given direction. + POINT, //!< Point light, emits light from a position, in all directions. + FOCUSED_SPOT, //!< Physically correct spot light. + SPOT, //!< Spot light with coupling of outer cone and illumination disabled. + }; + + /** + * Control the quality / performance of the shadow map associated to this light + */ + struct ShadowOptions { + /** Size of the shadow map in texels. Must be a power-of-two and larger or equal to 8. */ + uint32_t mapSize = 1024; + + /** + * Number of shadow cascades to use for this light. Must be between 1 and 4 (inclusive). + * A value greater than 1 turns on cascaded shadow mapping (CSM). + * Only applicable to Type.SUN or Type.DIRECTIONAL lights. + * + * When using shadow cascades, cascadeSplitPositions must also be set. + * + * @see ShadowOptions::cascadeSplitPositions + */ + uint8_t shadowCascades = 1; + + /** + * The split positions for shadow cascades. + * + * Cascaded shadow mapping (CSM) partitions the camera frustum into cascades. These values + * determine the planes along the camera's Z axis to split the frustum. The camera near + * plane is represented by 0.0f and the far plane represented by 1.0f. + * + * For example, if using 4 cascades, these values would set a uniform split scheme: + * { 0.25f, 0.50f, 0.75f } + * + * For N cascades, N - 1 split positions will be read from this array. + * + * Filament provides utility methods inside LightManager::ShadowCascades to help set these + * values. For example, to use a uniform split scheme: + * + * ~~~~~~~~~~~{.cpp} + * LightManager::ShadowCascades::computeUniformSplits(options.splitPositions, 4); + * ~~~~~~~~~~~ + * + * @see ShadowCascades::computeUniformSplits + * @see ShadowCascades::computeLogSplits + * @see ShadowCascades::computePracticalSplits + */ + float cascadeSplitPositions[3] = { 0.25f, 0.50f, 0.75f }; + + /** Constant bias in world units (e.g. meters) by which shadows are moved away from the + * light. 1mm by default. + * This is ignored when the View's ShadowType is set to VSM. + */ + float constantBias = 0.001f; + + /** Amount by which the maximum sampling error is scaled. The resulting value is used + * to move the shadow away from the fragment normal. Should be 1.0. + * This is ignored when the View's ShadowType is set to VSM. + */ + float normalBias = 1.0f; + + /** Distance from the camera after which shadows are clipped. This is used to clip + * shadows that are too far and wouldn't contribute to the scene much, improving + * performance and quality. This value is always positive. + * Use 0.0f to use the camera far distance. + */ + float shadowFar = 0.0f; + + /** Optimize the quality of shadows from this distance from the camera. Shadows will + * be rendered in front of this distance, but the quality may not be optimal. + * This value is always positive. Use 0.0f to use the camera near distance. + * The default of 1m works well with many scenes. The quality of shadows may drop + * rapidly when this value decreases. + */ + float shadowNearHint = 1.0f; + + /** Optimize the quality of shadows in front of this distance from the camera. Shadows + * will be rendered behind this distance, but the quality may not be optimal. + * This value is always positive. Use std::numerical_limits::infinity() to + * use the camera far distance. + */ + float shadowFarHint = 100.0f; + + /** + * Controls whether the shadow map should be optimized for resolution or stability. + * When set to true, all resolution enhancing features that can affect stability are + * disabling, resulting in significantly lower resolution shadows, albeit stable ones. + */ + bool stable = false; + + /** + * Constant bias in depth-resolution units by which shadows are moved away from the + * light. The default value of 0.5 is used to round depth values up. + * Generally this value shouldn't be changed or at least be small and positive. + */ + float polygonOffsetConstant = 0.5f; + + /** + * Bias based on the change in depth in depth-resolution units by which shadows are moved + * away from the light. The default value of 2.0 works well with SHADOW_SAMPLING_PCF_LOW. + * Generally this value is between 0.5 and the size in texel of the PCF filter. + * Setting this value correctly is essential for LISPSM shadow-maps. + */ + float polygonOffsetSlope = 2.0f; + + /** + * Whether screen-space contact shadows are used. This applies regardless of whether a + * Renderable is a shadow caster. + * Screen-space contact shadows are typically useful in large scenes. + * (off by default) + */ + bool screenSpaceContactShadows = false; + + /** + * Number of ray-marching steps for screen-space contact shadows (8 by default). + * + * CAUTION: this parameter is ignored for all lights except the directional/sun light, + * all other lights use the same value set for the directional/sun light. + * + */ + uint8_t stepCount = 8; + + /** + * Maximum shadow-occluder distance for screen-space contact shadows (world units). + * (30 cm by default) + * + * CAUTION: this parameter is ignored for all lights except the directional/sun light, + * all other lights use the same value set for the directional/sun light. + * + */ + float maxShadowDistance = 0.3f; + + /** + * Options available when the View's ShadowType is set to VSM. + * + * @warning This API is still experimental and subject to change. + * @see View::setShadowType + */ + struct Vsm { + /** + * The number of MSAA samples to use when rendering VSM shadow maps. + * Must be a power-of-two and greater than or equal to 1. A value of 1 effectively turns + * off MSAA. + * Higher values may not be available depending on the underlying hardware. + */ + uint8_t msaaSamples = 1; + + /** + * Blur width for the VSM blur. Zero do disable. + * The maximum value is 125. + */ + float blurWidth = 0.0f; + } vsm; + }; + + struct ShadowCascades { + /** + * Utility method to compute ShadowOptions::cascadeSplitPositions according to a uniform + * split scheme. + * + * @param splitPositions a float array of at least size (cascades - 1) to write the split + * positions into + * @param cascades the number of shadow cascades, at most 4 + */ + static void computeUniformSplits(float* splitPositions, uint8_t cascades); + + /** + * Utility method to compute ShadowOptions::cascadeSplitPositions according to a logarithmic + * split scheme. + * + * @param splitPositions a float array of at least size (cascades - 1) to write the split + * positions into + * @param cascades the number of shadow cascades, at most 4 + * @param near the camera near plane + * @param far the camera far plane + */ + static void computeLogSplits(float* splitPositions, uint8_t cascades, + float near, float far); + + /** + * Utility method to compute ShadowOptions::cascadeSplitPositions according to a practical + * split scheme. + * + * The practical split scheme uses uses a lambda value to interpolate between the logarithmic + * and uniform split schemes. Start with a lambda value of 0.5f and adjust for your scene. + * + * See: Zhang et al 2006, "Parallel-split shadow maps for large-scale virtual environments" + * + * @param splitPositions a float array of at least size (cascades - 1) to write the split + * positions into + * @param cascades the number of shadow cascades, at most 4 + * @param near the camera near plane + * @param far the camera far plane + * @param lambda a float in the range [0, 1] that interpolates between log and + * uniform split schemes + */ + static void computePracticalSplits(float* splitPositions, uint8_t cascades, + float near, float far, float lambda); + }; + + //! Use Builder to construct a Light object instance + class Builder : public BuilderBase { + friend struct BuilderDetails; + public: + /** + * Creates a light builder and set the light's #Type. + * + * @param type #Type of Light object to create. + */ + explicit Builder(Type type) noexcept; + Builder(Builder const& rhs) noexcept; + Builder(Builder&& rhs) noexcept; + ~Builder() noexcept; + Builder& operator=(Builder const& rhs) noexcept; + Builder& operator=(Builder&& rhs) noexcept; + + /** + * Enables or disables a light channel. Light channel 0 is enabled by default. + * + * @param channel Light channel to enable or disable, between 0 and 7. + * @param enable Whether to enable or disable the light channel. + * @return This Builder, for chaining calls. + */ + Builder& lightChannel(unsigned int channel, bool enable = true) noexcept; + + /** + * Whether this Light casts shadows (disabled by default) + * + * @param enable Enables or disables casting shadows from this Light. + * + * @return This Builder, for chaining calls. + * + * @warning + * - Only a Type.DIRECTIONAL, Type.SUN, Type.SPOT, or Type.FOCUSED_SPOT light can cast shadows + */ + Builder& castShadows(bool enable) noexcept; + + /** + * Sets the shadow-map options for this light. + * + * @return This Builder, for chaining calls. + */ + Builder& shadowOptions(const ShadowOptions& options) noexcept; + + /** + * Whether this light casts light (enabled by default) + * + * @param enable Enables or disables lighting from this Light. + * + * @return This Builder, for chaining calls. + * + * @note + * In some situations it can be useful to have a light in the scene that doesn't + * actually emit light, but does cast shadows. + */ + Builder& castLight(bool enable) noexcept; + + /** + * Sets the initial position of the light in world space. + * + * @param position Light's position in world space. The default is at the origin. + * + * @return This Builder, for chaining calls. + * + * @note + * The Light's position is ignored for directional lights (Type.DIRECTIONAL or Type.SUN) + */ + Builder& position(const math::float3& position) noexcept; + + /** + * Sets the initial direction of a light in world space. + * + * @param direction Light's direction in world space. Should be a unit vector. + * The default is {0,-1,0}. + * + * @return This Builder, for chaining calls. + * + * @note + * The Light's direction is ignored for Type.POINT lights. + */ + Builder& direction(const math::float3& direction) noexcept; + + /** + * Sets the initial color of a light. + * + * @param color Color of the light specified in the linear sRGB color-space. + * The default is white {1,1,1}. + * + * @return This Builder, for chaining calls. + */ + Builder& color(const LinearColor& color) noexcept; + + /** + * Sets the initial intensity of a light. + * @param intensity This parameter depends on the Light.Type: + * - For directional lights, it specifies the illuminance in *lux* + * (or *lumen/m^2*). + * - For point lights and spot lights, it specifies the luminous power + * in *lumen*. + * + * @return This Builder, for chaining calls. + * + * For example, the sun's illuminance is about 100,000 lux. + * + * This method overrides any prior calls to intensity or intensityCandela. + * + */ + Builder& intensity(float intensity) noexcept; + + /** + * Sets the initial intensity of a spot or point light in candela. + * + * @param intensity Luminous intensity in *candela*. + * + * @return This Builder, for chaining calls. + * + * @note + * This method is equivalent to calling intensity(float intensity) for directional lights + * (Type.DIRECTIONAL or Type.SUN). + * + * This method overrides any prior calls to intensity or intensityCandela. + */ + Builder& intensityCandela(float intensity) noexcept; + + /** + * Sets the initial intensity of a light in watts. + * + * @param watts Energy consumed by a lightbulb. It is related to the energy produced + * and ultimately the brightness by the \p efficiency parameter. + * This value is often available on the packaging of commercial + * lightbulbs. + * + * @param efficiency Efficiency in percent. This depends on the type of lightbulb used. + * + * Lightbulb type | Efficiency + * ----------------:|-----------: + * Incandescent | 2.2% + * Halogen | 7.0% + * LED | 8.7% + * Fluorescent | 10.7% + * + * @return This Builder, for chaining calls. + * + * + * @note + * This call is equivalent to `Builder::intensity(efficiency * 683 * watts);` + * + * This method overrides any prior calls to intensity or intensityCandela. + */ + Builder& intensity(float watts, float efficiency) noexcept; + + /** + * Set the falloff distance for point lights and spot lights. + * + * At the falloff distance, the light has no more effect on objects. + * + * The falloff distance essentially defines a *sphere of influence* around the light, and + * therefore has an impact on performance. Larger falloffs might reduce performance + * significantly, especially when many lights are used. + * + * Try to avoid having a large number of light's spheres of influence overlap. + * + * @param radius Falloff distance in world units. Default is 1 meter. + * + * @return This Builder, for chaining calls. + * + * @note + * The Light's falloff is ignored for directional lights (Type.DIRECTIONAL or Type.SUN) + */ + Builder& falloff(float radius) noexcept; + + /** + * Defines a spot light'st angular falloff attenuation. + * + * A spot light is defined by a position, a direction and two cones, \p inner and \p outer. + * These two cones are used to define the angular falloff attenuation of the spot light + * and are defined by the angle from the center axis to where the falloff begins (i.e. + * cones are defined by their half-angle). + * + * @param inner inner cone angle in *radians* between 0 and @f$ \pi/2 @f$ + * + * @param outer outer cone angle in *radians* between \p inner and @f$ \pi/2 @f$ + * + * @return This Builder, for chaining calls. + * + * @note + * The spot light cone is ignored for directional and point lights. + * + * @see Type.SPOT, Type.FOCUSED_SPOT + */ + Builder& spotLightCone(float inner, float outer) noexcept; + + /** + * Defines the angular radius of the sun, in degrees, between 0.25° and 20.0° + * + * The Sun as seen from Earth has an angular size of 0.526° to 0.545° + * + * @param angularRadius sun's radius in degree. Default is 0.545°. + * + * @return This Builder, for chaining calls. + */ + Builder& sunAngularRadius(float angularRadius) noexcept; + + /** + * Defines the halo radius of the sun. The radius of the halo is defined as a + * multiplier of the sun angular radius. + * + * @param haloSize radius multiplier. Default is 10.0. + * + * @return This Builder, for chaining calls. + */ + Builder& sunHaloSize(float haloSize) noexcept; + + /** + * Defines the halo falloff of the sun. The falloff is a dimensionless number + * used as an exponent. + * + * @param haloFalloff halo falloff. Default is 80.0. + * + * @return This Builder, for chaining calls. + */ + Builder& sunHaloFalloff(float haloFalloff) noexcept; + + enum Result { Error = -1, Success = 0 }; + + /** + * Adds the Light component to an entity. + * + * @param engine Reference to the filament::Engine to associate this light with. + * @param entity Entity to add the light component to. + * @return Success if the component was created successfully, Error otherwise. + * + * If exceptions are disabled and an error occurs, this function is a no-op. + * Success can be checked by looking at the return value. + * + * If this component already exists on the given entity, it is first destroyed as if + * destroy(utils::Entity e) was called. + * + * @warning + * Currently, only 2048 lights can be created on a given Engine. + * + * @exception utils::PostConditionPanic if a runtime error occurred, such as running out of + * memory or other resources. + * @exception utils::PreConditionPanic if a parameter to a builder function was invalid. + */ + Result build(Engine& engine, utils::Entity entity); + + private: + friend class FEngine; + friend class FLightManager; + }; + + static constexpr float EFFICIENCY_INCANDESCENT = 0.0220f; //!< Typical efficiency of an incandescent light bulb (2.2%) + static constexpr float EFFICIENCY_HALOGEN = 0.0707f; //!< Typical efficiency of an halogen light bulb (7.0%) + static constexpr float EFFICIENCY_FLUORESCENT = 0.0878f; //!< Typical efficiency of a fluorescent light bulb (8.7%) + static constexpr float EFFICIENCY_LED = 0.1171f; //!< Typical efficiency of a LED light bulb (11.7%) + + Type getType(Instance i) const noexcept; + + /** + * Helper function that returns if a light is a directional light + * + * @param i Instance of the component obtained from getInstance(). + * @return true is this light is a type of directional light + */ + inline bool isDirectional(Instance i) const noexcept { + Type type = getType(i); + return type == Type::DIRECTIONAL || type == Type::SUN; + } + + /** + * Helper function that returns if a light is a point light + * + * @param i Instance of the component obtained from getInstance(). + * @return true is this light is a type of point light + */ + inline bool isPointLight(Instance i) const noexcept { + return getType(i) == Type::POINT; + } + + /** + * Helper function that returns if a light is a spot light + * + * @param i Instance of the component obtained from getInstance(). + * @return true is this light is a type of spot light + */ + inline bool isSpotLight(Instance i) const noexcept { + Type type = getType(i); + return type == Type::SPOT || type == Type::FOCUSED_SPOT; + } + + /** + * Enables or disables a light channel. Light channel 0 is enabled by default. + * @param channel light channel to enable or disable, between 0 and 7. + * @param enable whether to enable (true) or disable (false) the specified light channel. + */ + void setLightChannel(Instance i, unsigned int channel, bool enable = true) noexcept; + + /** + * Returns whether a light channel is enabled on a specified light. + * @param i Instance of the component obtained from getInstance(). + * @param channel Light channel to query + * @return true if the light channel is enabled, false otherwise + */ + bool getLightChannel(Instance i, unsigned int channel) const noexcept; + + /** + * Dynamically updates the light's position. + * + * @param i Instance of the component obtained from getInstance(). + * @param position Light's position in world space. The default is at the origin. + * + * @see Builder.position() + */ + void setPosition(Instance i, const math::float3& position) noexcept; + + //! returns the light's position in world space + const math::float3& getPosition(Instance i) const noexcept; + + /** + * Dynamically updates the light's direction + * + * @param i Instance of the component obtained from getInstance(). + * @param direction Light's direction in world space. Should be a unit vector. + * The default is {0,-1,0}. + * + * @see Builder.direction() + */ + void setDirection(Instance i, const math::float3& direction) noexcept; + + //! returns the light's direction in world space + const math::float3& getDirection(Instance i) const noexcept; + + /** + * Dynamically updates the light's hue as linear sRGB + * + * @param i Instance of the component obtained from getInstance(). + * @param color Color of the light specified in the linear sRGB color-space. + * The default is white {1,1,1}. + * + * @see Builder.color(), getInstance() + */ + void setColor(Instance i, const LinearColor& color) noexcept; + + /** + * @param i Instance of the component obtained from getInstance(). + * @return the light's color in linear sRGB + */ + const math::float3& getColor(Instance i) const noexcept; + + /** + * Dynamically updates the light's intensity. The intensity can be negative. + * + * @param i Instance of the component obtained from getInstance(). + * @param intensity This parameter depends on the Light.Type: + * - For directional lights, it specifies the illuminance in *lux* + * (or *lumen/m^2*). + * - For point lights and spot lights, it specifies the luminous power + * in *lumen*. + * + * @see Builder.intensity() + */ + void setIntensity(Instance i, float intensity) noexcept; + + /** + * Dynamically updates the light's intensity. The intensity can be negative. + * + * @param i Instance of the component obtained from getInstance(). + * @param watts Energy consumed by a lightbulb. It is related to the energy produced + * and ultimately the brightness by the \p efficiency parameter. + * This value is often available on the packaging of commercial + * lightbulbs. + * @param efficiency Efficiency in percent. This depends on the type of lightbulb used. + * + * Lightbulb type | Efficiency + * ----------------:|-----------: + * Incandescent | 2.2% + * Halogen | 7.0% + * LED | 8.7% + * Fluorescent | 10.7% + * + * @see Builder.intensity(float watts, float efficiency) + */ + void setIntensity(Instance i, float watts, float efficiency) noexcept { + setIntensity(i, watts * 683.0f * efficiency); + } + + /** + * Dynamically updates the light's intensity in candela. The intensity can be negative. + * + * @param i Instance of the component obtained from getInstance(). + * @param intensity Luminous intensity in *candela*. + * + * @note + * This method is equivalent to calling setIntensity(float intensity) for directional lights + * (Type.DIRECTIONAL or Type.SUN). + * + * @see Builder.intensityCandela(float intensity) + */ + void setIntensityCandela(Instance i, float intensity) noexcept; + + /** + * returns the light's luminous intensity in candela. + * + * @param i Instance of the component obtained from getInstance(). + * + * @note for Type.FOCUSED_SPOT lights, the returned value depends on the \p outer cone angle. + * + * @return luminous intensity in candela. + */ + float getIntensity(Instance i) const noexcept; + + /** + * Set the falloff distance for point lights and spot lights. + * + * @param i Instance of the component obtained from getInstance(). + * @param radius falloff distance in world units. Default is 1 meter. + * + * @see Builder.falloff() + */ + void setFalloff(Instance i, float radius) noexcept; + + /** + * returns the falloff distance of this light. + * @param i Instance of the component obtained from getInstance(). + * @return the falloff distance of this light. + */ + float getFalloff(Instance i) const noexcept; + + /** + * Dynamically updates a spot light's cone as angles + * + * @param i Instance of the component obtained from getInstance(). + * @param inner inner cone angle in *radians* between 0 and pi/2 + * @param outer outer cone angle in *radians* between inner and pi/2 + * + * @see Builder.spotLightCone() + */ + void setSpotLightCone(Instance i, float inner, float outer) noexcept; + + /** + * returns the outer cone angle in *radians* between inner and pi/2. + * @param i Instance of the component obtained from getInstance(). + * @return the outer cone angle of this light. + */ + float getSpotLightOuterCone(Instance i) const noexcept; + + /** + * returns the inner cone angle in *radians* between 0 and pi/2. + * + * The value is recomputed from the initial values, thus is not precisely + * the same as the one passed to setSpotLightCone() or Builder.spotLightCone(). + * + * @param i Instance of the component obtained from getInstance(). + * @return the inner cone angle of this light. + */ + float getSpotLightInnerCone(Instance i) const noexcept; + + /** + * Dynamically updates the angular radius of a Type.SUN light + * + * The Sun as seen from Earth has an angular size of 0.526° to 0.545° + * + * @param i Instance of the component obtained from getInstance(). + * @param angularRadius sun's radius in degrees. Default is 0.545°. + */ + void setSunAngularRadius(Instance i, float angularRadius) noexcept; + + /** + * returns the angular radius if the sun in degrees. + * @param i Instance of the component obtained from getInstance(). + * @return the angular radius if the sun in degrees. + */ + float getSunAngularRadius(Instance i) const noexcept; + + /** + * Dynamically updates the halo radius of a Type.SUN light. The radius + * of the halo is defined as a multiplier of the sun angular radius. + * + * @param i Instance of the component obtained from getInstance(). + * @param haloSize radius multiplier. Default is 10.0. + */ + void setSunHaloSize(Instance i, float haloSize) noexcept; + + /** + * returns the halo size of a Type.SUN light as a multiplier of the + * sun angular radius. + * @param i Instance of the component obtained from getInstance(). + * @return the halo size + */ + float getSunHaloSize(Instance i) const noexcept; + + /** + * Dynamically updates the halo falloff of a Type.SUN light. The falloff + * is a dimensionless number used as an exponent. + * + * @param i Instance of the component obtained from getInstance(). + * @param haloFalloff halo falloff. Default is 80.0. + */ + void setSunHaloFalloff(Instance i, float haloFalloff) noexcept; + + /** + * returns the halo falloff of a Type.SUN light as a dimensionless value. + * @param i Instance of the component obtained from getInstance(). + * @return the halo falloff + */ + float getSunHaloFalloff(Instance i) const noexcept; + + /** + * returns the shadow-map options for a given light + * @param i Instance of the component obtained from getInstance(). + * @return A ShadowOption structure + */ + ShadowOptions const& getShadowOptions(Instance i) const noexcept; + + /** + * sets the shadow-map options for a given light + * @param i Instance of the component obtained from getInstance(). + * @param options A ShadowOption structure + */ + void setShadowOptions(Instance i, ShadowOptions const& options) noexcept; + + /** + * Whether this Light casts shadows (disabled by default) + * + * @param i Instance of the component obtained from getInstance(). + * @param shadowCaster Enables or disables casting shadows from this Light. + * + * @warning + * - Only a Type.DIRECTIONAL, Type.SUN, Type.SPOT, or Type.FOCUSED_SPOT light can cast shadows + */ + void setShadowCaster(Instance i, bool shadowCaster) noexcept; + + /** + * returns whether this light casts shadows. + * @param i Instance of the component obtained from getInstance(). + */ + bool isShadowCaster(Instance i) const noexcept; + + /** + * Helper to process all components with a given function + * @tparam F a void(Entity entity, Instance instance) + * @param func a function of type F + */ + template + void forEachComponent(F func) noexcept { + utils::Entity const* const pEntity = getEntities(); + for (size_t i = 0, c = getComponentCount(); i < c; i++) { + // Instance 0 is the invalid instance + func(pEntity[i], Instance(i + 1)); + } + } +}; + +} // namespace filament + +#endif // TNT_FILAMENT_LIGHTMANAGER_H diff --git a/ios/include/filament/Material.h b/ios/include/filament/Material.h new file mode 100644 index 00000000..a8722804 --- /dev/null +++ b/ios/include/filament/Material.h @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_MATERIAL_H +#define TNT_FILAMENT_MATERIAL_H + +#include +#include +#include +#include + +#include + +#include + +#include + +#include + +namespace utils { + class CString; +} // namespace utils + +namespace filament { + +class Texture; +class TextureSampler; + +class FEngine; +class FMaterial; + +class Engine; + +class UTILS_PUBLIC Material : public FilamentAPI { + struct BuilderDetails; + +public: + using BlendingMode = BlendingMode; + using Shading = Shading; + using Interpolation = Interpolation; + using VertexDomain = VertexDomain; + using TransparencyMode = TransparencyMode; + + using ParameterType = backend::UniformType; + using Precision = backend::Precision; + using SamplerType = backend::SamplerType; + using SamplerFormat = backend::SamplerFormat; + using CullingMode = backend::CullingMode; + using ShaderModel = backend::ShaderModel; + using SubpassType = backend::SubpassType; + + /** + * Holds information about a material parameter. + */ + struct ParameterInfo { + //! Name of the parameter. + const char* name; + //! Whether the parameter is a sampler (texture). + bool isSampler; + //! Whether the parameter is a subpass type. + bool isSubpass; + union { + //! Type of the parameter if the parameter is not a sampler. + ParameterType type; + //! Type of the parameter if the parameter is a sampler. + SamplerType samplerType; + //! Type of the parameter if the parameter is a subpass. + SubpassType subpassType; + }; + //! Size of the parameter when the parameter is an array. + uint32_t count; + //! Requested precision of the parameter. + Precision precision; + }; + + class Builder : public BuilderBase { + friend struct BuilderDetails; + public: + Builder() noexcept; + Builder(Builder const& rhs) noexcept; + Builder(Builder&& rhs) noexcept; + ~Builder() noexcept; + Builder& operator=(Builder const& rhs) noexcept; + Builder& operator=(Builder&& rhs) noexcept; + + /** + * Specifies the material data. The material data is a binary blob produced by + * libfilamat or by matc. + * + * @param payload Pointer to the material data, must stay valid until build() is called. + * @param size Size of the material data pointed to by "payload" in bytes. + */ + Builder& package(const void* payload, size_t size); + + /** + * Creates the Material object and returns a pointer to it. + * + * @param engine Reference to the filament::Engine to associate this Material with. + * + * @return pointer to the newly created object or nullptr if exceptions are disabled and + * an error occurred. + * + * @exception utils::PostConditionPanic if a runtime error occurred, such as running out of + * memory or other resources. + * @exception utils::PreConditionPanic if a parameter to a builder function was invalid. + */ + Material* build(Engine& engine); + private: + friend class FMaterial; + }; + + /** + * Creates a new instance of this material. Material instances should be freed using + * Engine::destroy(const MaterialInstance*). + * + * @param name Optional name to associate with the given material instance. If this is null, + * then the instance inherits the material's name. + * + * @return A pointer to the new instance. + */ + MaterialInstance* createInstance(const char* name = nullptr) const noexcept; + + //! Returns the name of this material as a null-terminated string. + const char* getName() const noexcept; + + //! Returns the shading model of this material. + Shading getShading() const noexcept; + + //! Returns the interpolation mode of this material. This affects how variables are interpolated. + Interpolation getInterpolation() const noexcept; + + //! Returns the blending mode of this material. + BlendingMode getBlendingMode() const noexcept; + + //! Returns the vertex domain of this material. + VertexDomain getVertexDomain() const noexcept; + + //! Returns the material domain of this material. + //! The material domain determines how the material is used. + MaterialDomain getMaterialDomain() const noexcept; + + //! Returns the default culling mode of this material. + CullingMode getCullingMode() const noexcept; + + //! Returns the transparency mode of this material. + //! This value only makes sense when the blending mode is transparent or fade. + TransparencyMode getTransparencyMode() const noexcept; + + //! Indicates whether instances of this material will, by default, write to the color buffer. + bool isColorWriteEnabled() const noexcept; + + //! Indicates whether instances of this material will, by default, write to the depth buffer. + bool isDepthWriteEnabled() const noexcept; + + //! Indicates whether instances of this material will, by default, use depth testing. + bool isDepthCullingEnabled() const noexcept; + + //! Indicates whether this material is double-sided. + bool isDoubleSided() const noexcept; + + //! Returns the alpha mask threshold used when the blending mode is set to masked. + float getMaskThreshold() const noexcept; + + //! Indicates whether this material uses the shadowing factor as a color multiplier. + //! This values only makes sense when the shading mode is unlit. + bool hasShadowMultiplier() const noexcept; + + //! Indicates whether this material has specular anti-aliasing enabled + bool hasSpecularAntiAliasing() const noexcept; + + //! Returns the screen-space variance for specular-antialiasing, this value is between 0 and 1. + float getSpecularAntiAliasingVariance() const noexcept; + + //! Returns the clamping threshold for specular-antialiasing, this value is between 0 and 1. + float getSpecularAntiAliasingThreshold() const noexcept; + + //! Returns the list of vertex attributes required by this material. + AttributeBitset getRequiredAttributes() const noexcept; + + //! Returns the refraction mode used by this material. + RefractionMode getRefractionMode() const noexcept; + + // Return the refraction type used by this material. + RefractionType getRefractionType() const noexcept; + + /** + * Returns the number of parameters declared by this material. + * The returned value can be 0. + */ + size_t getParameterCount() const noexcept; + + /** + * Gets information about this material's parameters. + * + * @param parameters A pointer to a list of ParameterInfo. + * The list must be at least "count" large + * @param count The number of parameters to retrieve. Must be >= 0 and can be > count. + * + * @return The number of parameters written to the parameters pointer. + */ + size_t getParameters(ParameterInfo* parameters, size_t count) const noexcept; + + //! Indicates whether a parameter of the given name exists on this material. + bool hasParameter(const char* name) const noexcept; + + //! Indicates whether an existing parameter is a sampler or not. + bool isSampler(const char* name) const noexcept; + + /** + * Sets the value of the given parameter on this material's default instance. + * + * @param name The name of the material parameter + * @param value The value of the material parameter + * + * @see getDefaultInstance() + */ + template + void setDefaultParameter(const char* name, T value) noexcept { + getDefaultInstance()->setParameter(name, value); + } + + /** + * Sets a texture and sampler parameters on this material's default instance. + * + * @param name The name of the material texture parameter + * @param texture The texture to set as parameter + * @param sampler The sampler to be used with this texture + * + * @see getDefaultInstance() + */ + void setDefaultParameter(const char* name, Texture const* texture, + TextureSampler const& sampler) noexcept { + getDefaultInstance()->setParameter(name, texture, sampler); + } + + /** + * Sets the color of the given parameter on this material's default instance. + * + * @param name The name of the material color parameter + * @param type Whether the color is specified in the linear or sRGB space + * @param color The color as a floating point red, green, blue tuple + * + * @see getDefaultInstance() + */ + void setDefaultParameter(const char* name, RgbType type, math::float3 color) noexcept { + getDefaultInstance()->setParameter(name, type, color); + } + + /** + * Sets the color of the given parameter on this material's default instance. + * + * @param name The name of the material color parameter + * @param type Whether the color is specified in the linear or sRGB space + * @param color The color as a floating point red, green, blue, alpha tuple + * + * @see getDefaultInstance() + */ + void setDefaultParameter(const char* name, RgbaType type, math::float4 color) noexcept { + getDefaultInstance()->setParameter(name, type, color); + } + + //! Returns this material's default instance. + MaterialInstance* getDefaultInstance() noexcept; + + //! Returns this material's default instance. + MaterialInstance const* getDefaultInstance() const noexcept; +}; + +} // namespace filament + +#endif // TNT_FILAMENT_MATERIAL_H diff --git a/ios/include/filament/MaterialChunkType.h b/ios/include/filament/MaterialChunkType.h new file mode 100644 index 00000000..56d6eb50 --- /dev/null +++ b/ios/include/filament/MaterialChunkType.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMAT_MATERIAL_CHUNK_TYPES_H +#define TNT_FILAMAT_MATERIAL_CHUNK_TYPES_H + +#include + +#include + +namespace filamat { + +// Pack an eight character string into a 64 bit integer. +constexpr inline uint64_t charTo64bitNum(const char str[9]) noexcept { + return + ( (static_cast(str[0]) << 56)) + | ((static_cast(str[1]) << 48) & 0x00FF000000000000U) + | ((static_cast(str[2]) << 40) & 0x0000FF0000000000U) + | ((static_cast(str[3]) << 32) & 0x000000FF00000000U) + | ((static_cast(str[4]) << 24) & 0x00000000FF000000U) + | ((static_cast(str[5]) << 16) & 0x0000000000FF0000U) + | ((static_cast(str[6]) << 8) & 0x000000000000FF00U) + | ( static_cast(str[7]) & 0x00000000000000FFU); +} + +enum UTILS_PUBLIC ChunkType : uint64_t { + Unknown = charTo64bitNum("UNKNOWN "), + MaterialUib = charTo64bitNum("MAT_UIB "), + MaterialSib = charTo64bitNum("MAT_SIB "), + MaterialSubpass = charTo64bitNum("MAT_SUB "), + MaterialGlsl = charTo64bitNum("MAT_GLSL"), + MaterialSpirv = charTo64bitNum("MAT_SPIR"), + MaterialMetal = charTo64bitNum("MAT_METL"), + MaterialShaderModels = charTo64bitNum("MAT_SMDL"), + MaterialSamplerBindings = charTo64bitNum("MAT_SAMP"), // no longer used + MaterialProperties = charTo64bitNum("MAT_PROP"), + + MaterialName = charTo64bitNum("MAT_NAME"), + MaterialVersion = charTo64bitNum("MAT_VERS"), + MaterialShading = charTo64bitNum("MAT_SHAD"), + MaterialBlendingMode = charTo64bitNum("MAT_BLEN"), + MaterialTransparencyMode = charTo64bitNum("MAT_TRMD"), + MaterialMaskThreshold = charTo64bitNum("MAT_THRS"), + MaterialShadowMultiplier = charTo64bitNum("MAT_SHML"), + MaterialSpecularAntiAliasing = charTo64bitNum("MAT_SPAA"), + MaterialSpecularAntiAliasingVariance = charTo64bitNum("MAT_SVAR"), + MaterialSpecularAntiAliasingThreshold = charTo64bitNum("MAT_STHR"), + MaterialClearCoatIorChange = charTo64bitNum("MAT_CIOR"), + MaterialDomain = charTo64bitNum("MAT_DOMN"), + MaterialRefraction = charTo64bitNum("MAT_REFM"), + MaterialRefractionType = charTo64bitNum("MAT_REFT"), + + MaterialRequiredAttributes = charTo64bitNum("MAT_REQA"), + MaterialDepthWriteSet = charTo64bitNum("MAT_DEWS"), + MaterialDoubleSidedSet = charTo64bitNum("MAT_DOSS"), + MaterialDoubleSided = charTo64bitNum("MAT_DOSI"), + + MaterialColorWrite = charTo64bitNum("MAT_CWRIT"), + MaterialDepthWrite = charTo64bitNum("MAT_DWRIT"), + MaterialDepthTest = charTo64bitNum("MAT_DTEST"), + MaterialCullingMode = charTo64bitNum("MAT_CUMO"), + + MaterialHasCustomDepthShader =charTo64bitNum("MAT_CSDP"), + + MaterialVertexDomain = charTo64bitNum("MAT_VEDO"), + MaterialInterpolation = charTo64bitNum("MAT_INTR"), + + DictionaryText = charTo64bitNum("DIC_TEXT"), + DictionarySpirv = charTo64bitNum("DIC_SPIR"), +}; + +} // namespace filamat + +#endif // TNT_FILAMAT_MATERIAL_CHUNK_TYPES_H diff --git a/ios/include/filament/MaterialEnums.h b/ios/include/filament/MaterialEnums.h new file mode 100644 index 00000000..4a7a35e8 --- /dev/null +++ b/ios/include/filament/MaterialEnums.h @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! \file + +#ifndef TNT_FILAMENT_MATERIAL_ENUM_H +#define TNT_FILAMENT_MATERIAL_ENUM_H + +#include + +#include +#include + +namespace filament { + +// update this when a new version of filament wouldn't work with older materials +static constexpr size_t MATERIAL_VERSION = 12; + +/** + * Supported shading models + */ +enum class Shading : uint8_t { + UNLIT, //!< no lighting applied, emissive possible + LIT, //!< default, standard lighting + SUBSURFACE, //!< subsurface lighting model + CLOTH, //!< cloth lighting model + SPECULAR_GLOSSINESS, //!< legacy lighting model +}; + +/** + * Attribute interpolation types in the fragment shader + */ +enum class Interpolation : uint8_t { + SMOOTH, //!< default, smooth interpolation + FLAT //!< flat interpolation +}; + +/** + * Shader quality, affect some global quality parameters + */ +enum class ShaderQuality : int8_t { + DEFAULT = -1, // LOW on mobile, HIGH on desktop + LOW = 0, // enable optimizations that can slightly affect correctness + NORMAL = 1, // normal quality, correctness honored + HIGH = 2 // higher quality (e.g. better upscaling, etc...) +}; + +/** + * Supported blending modes + */ +enum class BlendingMode : uint8_t { + //! material is opaque + OPAQUE, + //! material is transparent and color is alpha-pre-multiplied, affects diffuse lighting only + TRANSPARENT, + //! material is additive (e.g.: hologram) + ADD, + //! material is masked (i.e. alpha tested) + MASKED, + /** + * material is transparent and color is alpha-pre-multiplied, affects specular lighting + * when adding more entries, change the size of FRenderer::CommandKey::blending + */ + FADE, + //! material darkens what's behind it + MULTIPLY, + //! material brightens what's behind it + SCREEN, +}; + +/** + * How transparent objects are handled + */ +enum class TransparencyMode : uint8_t { + //! the transparent object is drawn honoring the raster state + DEFAULT, + /** + * the transparent object is first drawn in the depth buffer, + * then in the color buffer, honoring the culling mode, but ignoring the depth test function + */ + TWO_PASSES_ONE_SIDE, + + /** + * the transparent object is drawn twice in the color buffer, + * first with back faces only, then with front faces; the culling + * mode is ignored. Can be combined with two-sided lighting + */ + TWO_PASSES_TWO_SIDES +}; + +/** + * Supported types of vertex domains. + */ +enum class VertexDomain : uint8_t { + OBJECT, //!< vertices are in object space, default + WORLD, //!< vertices are in world space + VIEW, //!< vertices are in view space + DEVICE //!< vertices are in normalized device space + // when adding more entries, make sure to update VERTEX_DOMAIN_COUNT +}; + +/** + * Vertex attribute types + */ +enum VertexAttribute : uint8_t { + // Update hasIntegerTarget() in VertexBuffer when adding an attribute that will + // be read as integers in the shaders + + POSITION = 0, //!< XYZ position (float3) + TANGENTS = 1, //!< tangent, bitangent and normal, encoded as a quaternion (float4) + COLOR = 2, //!< vertex color (float4) + UV0 = 3, //!< texture coordinates (float2) + UV1 = 4, //!< texture coordinates (float2) + BONE_INDICES = 5, //!< indices of 4 bones, as unsigned integers (uvec4) + BONE_WEIGHTS = 6, //!< weights of the 4 bones (normalized float4) + // -- we have 1 unused slot here -- + CUSTOM0 = 8, + CUSTOM1 = 9, + CUSTOM2 = 10, + CUSTOM3 = 11, + CUSTOM4 = 12, + CUSTOM5 = 13, + CUSTOM6 = 14, + CUSTOM7 = 15, + + // Aliases for vertex morphing. + MORPH_POSITION_0 = CUSTOM0, + MORPH_POSITION_1 = CUSTOM1, + MORPH_POSITION_2 = CUSTOM2, + MORPH_POSITION_3 = CUSTOM3, + MORPH_TANGENTS_0 = CUSTOM4, + MORPH_TANGENTS_1 = CUSTOM5, + MORPH_TANGENTS_2 = CUSTOM6, + MORPH_TANGENTS_3 = CUSTOM7, + + // this is limited by driver::MAX_VERTEX_ATTRIBUTE_COUNT +}; + +static constexpr size_t MAX_MORPH_TARGETS = 4; +static constexpr size_t MAX_CUSTOM_ATTRIBUTES = 8; + +/** + * Material domains + */ +enum class MaterialDomain : uint8_t { + SURFACE = 0, //!< shaders applied to renderables + POST_PROCESS = 1, //!< shaders applied to rendered buffers +}; + +/** + * Specular occlusion + */ +enum class SpecularAmbientOcclusion : uint8_t { + NONE = 0, //!< no specular occlusion + SIMPLE = 1, //!< simple specular occlusion + BENT_NORMALS = 2, //!< more accurate specular occlusion, requires bent normals +}; + +/** + * Refraction + */ +enum class RefractionMode : uint8_t { + NONE = 0, //!< no refraction + CUBEMAP = 1, //!< refracted rays go to the ibl cubemap + SCREEN_SPACE = 2, //!< refracted rays go to screen space +}; + +/** + * Refraction type + */ +enum class RefractionType : uint8_t { + SOLID = 0, //!< refraction through solid objects (e.g. a sphere) + THIN = 1, //!< refraction through thin objects (e.g. window) +}; + +// can't really use std::underlying_type::type because the driver takes a uint32_t +using AttributeBitset = utils::bitset32; + +static constexpr size_t MATERIAL_PROPERTIES_COUNT = 26; +enum class Property : uint8_t { + BASE_COLOR, //!< float4, all shading models + ROUGHNESS, //!< float, lit shading models only + METALLIC, //!< float, all shading models, except unlit and cloth + REFLECTANCE, //!< float, all shading models, except unlit and cloth + AMBIENT_OCCLUSION, //!< float, lit shading models only, except subsurface and cloth + CLEAR_COAT, //!< float, lit shading models only, except subsurface and cloth + CLEAR_COAT_ROUGHNESS, //!< float, lit shading models only, except subsurface and cloth + CLEAR_COAT_NORMAL, //!< float, lit shading models only, except subsurface and cloth + ANISOTROPY, //!< float, lit shading models only, except subsurface and cloth + ANISOTROPY_DIRECTION, //!< float3, lit shading models only, except subsurface and cloth + THICKNESS, //!< float, subsurface shading model only + SUBSURFACE_POWER, //!< float, subsurface shading model only + SUBSURFACE_COLOR, //!< float3, subsurface and cloth shading models only + SHEEN_COLOR, //!< float3, lit shading models only, except subsurface + SHEEN_ROUGHNESS, //!< float3, lit shading models only, except subsurface and cloth + SPECULAR_COLOR, //!< float3, specular-glossiness shading model only + GLOSSINESS, //!< float, specular-glossiness shading model only + EMISSIVE, //!< float4, all shading models + NORMAL, //!< float3, all shading models only, except unlit + POST_LIGHTING_COLOR, //!< float4, all shading models + CLIP_SPACE_TRANSFORM, //!< mat4, vertex shader only + ABSORPTION, //!< float3, how much light is absorbed by the material + TRANSMISSION, //!< float, how much light is refracted through the material + IOR, //!< float, material's index of refraction + MICRO_THICKNESS, //!< float, thickness of the thin layer + BENT_NORMAL, //!< float3, all shading models only, except unlit + + // when adding new Properties, make sure to update MATERIAL_PROPERTIES_COUNT +}; + +} // namespace filament + +#endif diff --git a/ios/include/filament/MaterialInstance.h b/ios/include/filament/MaterialInstance.h new file mode 100644 index 00000000..98b52681 --- /dev/null +++ b/ios/include/filament/MaterialInstance.h @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_MATERIALINSTANCE_H +#define TNT_FILAMENT_MATERIALINSTANCE_H + +#include +#include + +#include + +#include + +#include + +namespace filament { + +class Material; +class Texture; +class TextureSampler; +class UniformBuffer; +class UniformInterfaceBlock; + +class UTILS_PUBLIC MaterialInstance : public FilamentAPI { +public: + using CullingMode = filament::backend::CullingMode; + + template + using is_supported_parameter_t = typename std::enable_if< + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + // these types are slower as they need a layout conversion + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value + >::type; + + /** + * Creates a new MaterialInstance using another MaterialInstance as a template for initialization. + * The new MaterialInstance is an instance of the same Material of the template instance and + * must be destroyed just like any other MaterialInstance. + * + * @param other A MaterialInstance to use as a template for initializing a new instance + * @param name A name for the new MaterialInstance or nullptr to use the template's name + * @return A new MaterialInstance + */ + static MaterialInstance* duplicate(MaterialInstance const* other, const char* name = nullptr) noexcept; + + /** + * @return the Material associated with this instance + */ + Material const* getMaterial() const noexcept; + + /** + * @return the name associated with this instance + */ + const char* getName() const noexcept; + + /** + * Set a uniform by name + * + * @param name Name of the parameter as defined by Material. Cannot be nullptr. + * @param value Value of the parameter to set. + * @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled. + */ + template> + void setParameter(const char* name, T const& value) noexcept; + + /** + * Set a uniform array by name + * + * @param name Name of the parameter array as defined by Material. Cannot be nullptr. + * @param values Array of values to set to the named parameter array. + * @param count Size of the array to set. + * @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled. + */ + template> + void setParameter(const char* name, const T* values, size_t count) noexcept; + + /** + * Set a texture as the named parameter + * + * @param name Name of the parameter as defined by Material. Cannot be nullptr. + * @param texture Non nullptr Texture object pointer. + * @param sampler Sampler parameters. + * @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled. + */ + void setParameter(const char* name, + Texture const* texture, TextureSampler const& sampler) noexcept; + + /** + * Set an RGB color as the named parameter. + * A conversion might occur depending on the specified type + * + * @param name Name of the parameter as defined by Material. Cannot be nullptr. + * @param type Whether the color value is encoded as Linear or sRGB. + * @param color Array of read, green, blue channels values. + * @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled. + */ + void setParameter(const char* name, RgbType type, math::float3 color) noexcept; + + /** + * Set an RGBA color as the named parameter. + * A conversion might occur depending on the specified type + * + * @param name Name of the parameter as defined by Material. Cannot be nullptr. + * @param type Whether the color value is encoded as Linear or sRGB/A. + * @param color Array of read, green, blue and alpha channels values. + * @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled. + */ + void setParameter(const char* name, RgbaType type, math::float4 color) noexcept; + + /** + * Set up a custom scissor rectangle; by default this encompasses the View. + * + * @param left left coordinate of the scissor box + * @param bottom bottom coordinate of the scissor box + * @param width width of the scissor box + * @param height height of the scissor box + */ + void setScissor(uint32_t left, uint32_t bottom, uint32_t width, uint32_t height) noexcept; + + /** + * Returns the scissor rectangle to its default setting, which encompasses the View. + */ + void unsetScissor() noexcept; + + /** + * Sets a polygon offset that will be applied to all renderables drawn with this material + * instance. + * + * The value of the offset is scale * dz + r * constant, where dz is the change in depth + * relative to the screen area of the triangle, and r is the smallest value that is guaranteed + * to produce a resolvable offset for a given implementation. This offset is added before the + * depth test. + * + * @warning using a polygon offset other than zero has a significant negative performance + * impact, as most implementations have to disable early depth culling. DO NOT USE unless + * absolutely necessary. + * + * @param scale scale factor used to create a variable depth offset for each triangle + * @param constant scale factor used to create a constant depth offset for each triangle + */ + void setPolygonOffset(float scale, float constant) noexcept; + + /** + * Overrides the minimum alpha value a fragment must have to not be discarded when the blend + * mode is MASKED. Defaults to 0.4 if it has not been set in the parent Material. The specified + * value should be between 0 and 1 and will be clamped if necessary. + */ + void setMaskThreshold(float threshold) noexcept; + + /** + * Sets the screen space variance of the filter kernel used when applying specular + * anti-aliasing. The default value is set to 0.15. The specified value should be between + * 0 and 1 and will be clamped if necessary. + */ + void setSpecularAntiAliasingVariance(float variance) noexcept; + + /** + * Sets the clamping threshold used to suppress estimation errors when applying specular + * anti-aliasing. The default value is set to 0.2. The specified value should be between 0 + * and 1 and will be clamped if necessary. + */ + void setSpecularAntiAliasingThreshold(float threshold) noexcept; + + /** + * Enables or disables double-sided lighting if the parent Material has double-sided capability, + * otherwise prints a warning. If double-sided lighting is enabled, backface culling is + * automatically disabled. + */ + void setDoubleSided(bool doubleSided) noexcept; + + /** + * Overrides the default triangle culling state that was set on the material. + */ + void setCullingMode(CullingMode culling) noexcept; + + /** + * Overrides the default color-buffer write state that was set on the material. + */ + void setColorWrite(bool enable) noexcept; + + /** + * Overrides the default depth-buffer write state that was set on the material. + */ + void setDepthWrite(bool enable) noexcept; + + /** + * Overrides the default depth testing state that was set on the material. + */ + void setDepthCulling(bool enable) noexcept; +}; + +} // namespace filament + +#endif // TNT_FILAMENT_MATERIALINSTANCE_H diff --git a/ios/include/filament/RenderTarget.h b/ios/include/filament/RenderTarget.h new file mode 100644 index 00000000..cc4ae38a --- /dev/null +++ b/ios/include/filament/RenderTarget.h @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! \file + +#ifndef TNT_FILAMENT_RENDERTARGET_H +#define TNT_FILAMENT_RENDERTARGET_H + +#include + +#include +#include + +#include + +namespace filament { + +class FRenderTarget; + +class Engine; +class Texture; + +/** + * An offscreen render target that can be associated with a View and contains + * weak references to a set of attached Texture objects. + * + * Clients are responsible for the lifetime of all associated Texture attachments. + * + * @see View + */ +class UTILS_PUBLIC RenderTarget : public FilamentAPI { + struct BuilderDetails; + +public: + using CubemapFace = backend::TextureCubemapFace; + + /** Minimum number of color attachment supported */ + static constexpr uint8_t MIN_SUPPORTED_COLOR_ATTACHMENTS_COUNT = + backend::MRT::MIN_SUPPORTED_RENDER_TARGET_COUNT; + + /** Maximum number of color attachment supported */ + static constexpr uint8_t MAX_SUPPORTED_COLOR_ATTACHMENTS_COUNT = + backend::MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; + + /** + * Attachment identifiers + */ + enum class AttachmentPoint : uint8_t { + COLOR0 = 0, //!< identifies the 1st color attachment + COLOR1 = 1, //!< identifies the 2nd color attachment + COLOR2 = 2, //!< identifies the 3rd color attachment + COLOR3 = 3, //!< identifies the 4th color attachment + COLOR4 = 4, //!< identifies the 5th color attachment + COLOR5 = 5, //!< identifies the 6th color attachment + COLOR6 = 6, //!< identifies the 7th color attachment + COLOR7 = 7, //!< identifies the 8th color attachment + DEPTH = MAX_SUPPORTED_COLOR_ATTACHMENTS_COUNT, //!< identifies the depth attachment + COLOR = COLOR0, //!< identifies the 1st color attachment + }; + + //! Use Builder to construct a RenderTarget object instance + class Builder : public BuilderBase { + friend struct BuilderDetails; + public: + Builder() noexcept; + Builder(Builder const& rhs) noexcept; + Builder(Builder&& rhs) noexcept; + ~Builder() noexcept; + Builder& operator=(Builder const& rhs) noexcept; + Builder& operator=(Builder&& rhs) noexcept; + + /** + * Sets a texture to a given attachment point. + * + * All RenderTargets must have a non-null COLOR attachment. + * + * @param attachment The attachment point of the texture. + * @param texture The associated texture object. + * @return A reference to this Builder for chaining calls. + */ + Builder& texture(AttachmentPoint attachment, Texture* texture) noexcept; + + /** + * Sets the mipmap level for a given attachment point. + * + * @param attachment The attachment point of the texture. + * @param level The associated mipmap level, 0 by default. + * @return A reference to this Builder for chaining calls. + */ + Builder& mipLevel(AttachmentPoint attachment, uint8_t level) noexcept; + + /** + * Sets the cubemap face for a given attachment point. + * + * @param attachment The attachment point. + * @param face The associated cubemap face. + * @return A reference to this Builder for chaining calls. + */ + Builder& face(AttachmentPoint attachment, CubemapFace face) noexcept; + + /** + * Sets the layer for a given attachment point (for 3D textures). + * + * @param attachment The attachment point. + * @param layer The associated cubemap layer. + * @return A reference to this Builder for chaining calls. + */ + Builder& layer(AttachmentPoint attachment, uint32_t layer) noexcept; + + /** + * Creates the RenderTarget object and returns a pointer to it. + * + * @return pointer to the newly created object or nullptr if exceptions are disabled and + * an error occurred. + */ + RenderTarget* build(Engine& engine); + + private: + friend class FRenderTarget; + }; + + /** + * Gets the texture set on the given attachment point + * @param attachment Attachment point + * @return A Texture object or nullptr if no texture is set for this attachment point + */ + Texture* getTexture(AttachmentPoint attachment) const noexcept; + + /** + * Returns the mipmap level set on the given attachment point + * @param attachment Attachment point + * @return the mipmap level set on the given attachment point + */ + uint8_t getMipLevel(AttachmentPoint attachment) const noexcept; + + /** + * Returns the face of a cubemap set on the given attachment point + * @param attachment Attachment point + * @return A cubemap face identifier. This is only relevant if the attachment's texture is + * a cubemap. + */ + CubemapFace getFace(AttachmentPoint attachment) const noexcept; + + /** + * Returns the texture-layer set on the given attachment point + * @param attachment Attachment point + * @return A texture layer. This is only relevant if the attachment's texture is a 3D texture. + */ + uint32_t getLayer(AttachmentPoint attachment) const noexcept; + + /** + * Returns the number of color attachments usable by this instance of Engine. This method is + * guaranteed to return at least MIN_SUPPORTED_COLOR_ATTACHMENTS_COUNT and at most + * MAX_SUPPORTED_COLOR_ATTACHMENTS_COUNT. + * @return Number of color attachments usable in a render target. + */ + uint8_t getSupportedColorAttachmentsCount() const noexcept; +}; + +} // namespace filament + +#endif // TNT_FILAMENT_RENDERTARGET_H diff --git a/ios/include/filament/RenderableManager.h b/ios/include/filament/RenderableManager.h new file mode 100644 index 00000000..b9e05771 --- /dev/null +++ b/ios/include/filament/RenderableManager.h @@ -0,0 +1,585 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_RENDERABLECOMPONENTMANAGER_H +#define TNT_FILAMENT_RENDERABLECOMPONENTMANAGER_H + +#include +#include +#include + +#include + +#include +#include + +#include + +#include + +namespace utils { + class Entity; +} // namespace utils + +namespace filament { + +class BufferObject; +class Engine; +class IndexBuffer; +class Material; +class MaterialInstance; +class Renderer; +class SkinningBuffer; +class VertexBuffer; + +class FEngine; +class FRenderPrimitive; +class FRenderableManager; + +/** + * Factory and manager for \em renderables, which are entities that can be drawn. + * + * Renderables are bundles of \em primitives, each of which has its own geometry and material. All + * primitives in a particular renderable share a set of rendering attributes, such as whether they + * cast shadows or use vertex skinning. + * + * Usage example: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * auto renderable = utils::EntityManager::get().create(); + * + * RenderableManager::Builder(1) + * .boundingBox({{ -1, -1, -1 }, { 1, 1, 1 }}) + * .material(0, matInstance) + * .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, vertBuffer, indBuffer, 0, 3) + * .receiveShadows(false) + * .build(engine, renderable); + * + * scene->addEntity(renderable); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * To modify the state of an existing renderable, clients should first use RenderableManager + * to get a temporary handle called an \em instance. The instance can then be used to get or set + * the renderable's state. Please note that instances are ephemeral; clients should store entities, + * not instances. + * + * - For details about constructing renderables, see RenderableManager::Builder. + * - To associate a 4x4 transform with an entity, see TransformManager. + * - To associate a human-readable label with an entity, see utils::NameComponentManager. + */ +class UTILS_PUBLIC RenderableManager : public FilamentAPI { + struct BuilderDetails; + +public: + using Instance = utils::EntityInstance; + using PrimitiveType = backend::PrimitiveType; + + /** + * Checks if the given entity already has a renderable component. + */ + bool hasComponent(utils::Entity e) const noexcept; + + /** + * Gets a temporary handle that can be used to access the renderable state. + * + * @return Non-zero handle if the entity has a renderable component, 0 otherwise. + */ + Instance getInstance(utils::Entity e) const noexcept; + + /** + * The transformation associated with a skinning joint. + * + * Clients can specify bones either using this quat-vec3 pair, or by using 4x4 matrices. + */ + struct Bone { + math::quatf unitQuaternion = { 1, 0, 0, 0 }; + math::float3 translation = { 0, 0, 0 }; + float reserved = 0; + }; + + /** + * Adds renderable components to entities using a builder pattern. + */ + class Builder : public BuilderBase { + friend struct BuilderDetails; + public: + enum Result { Error = -1, Success = 0 }; + + /** + * Creates a builder for renderable components. + * + * @param count the number of primitives that will be supplied to the builder + * + * Note that builders typically do not have a long lifetime since clients should discard + * them after calling build(). For a usage example, see RenderableManager. + */ + explicit Builder(size_t count) noexcept; + + /*! \cond PRIVATE */ + Builder(Builder const& rhs) = delete; + Builder(Builder&& rhs) noexcept; + ~Builder() noexcept; + Builder& operator=(Builder& rhs) = delete; + Builder& operator=(Builder&& rhs) noexcept; + /*! \endcond */ + + /** + * Specifies the geometry data for a primitive. + * + * Filament primitives must have an associated VertexBuffer and IndexBuffer. Typically, each + * primitive is specified with a pair of daisy-chained calls: \c geometry(...) and \c + * material(...). + * + * @param index zero-based index of the primitive, must be less than the count passed to Builder constructor + * @param type specifies the topology of the primitive (e.g., \c RenderableManager::PrimitiveType::TRIANGLES) + * @param vertices specifies the vertex buffer, which in turn specifies a set of attributes + * @param indices specifies the index buffer (either u16 or u32) + * @param offset specifies where in the index buffer to start reading (expressed as a number of indices) + * @param minIndex specifies the minimum index contained in the index buffer + * @param maxIndex specifies the maximum index contained in the index buffer + * @param count number of indices to read (for triangles, this should be a multiple of 3) + */ + Builder& geometry(size_t index, PrimitiveType type, VertexBuffer* vertices, IndexBuffer* indices, size_t offset, size_t minIndex, size_t maxIndex, size_t count) noexcept; + Builder& geometry(size_t index, PrimitiveType type, VertexBuffer* vertices, IndexBuffer* indices, size_t offset, size_t count) noexcept; //!< \overload + Builder& geometry(size_t index, PrimitiveType type, VertexBuffer* vertices, IndexBuffer* indices) noexcept; //!< \overload + + /** + * Binds a material instance to the specified primitive. + * + * If no material is specified for a given primitive, Filament will fall back to a basic default material. + * + * @param index zero-based index of the primitive, must be less than the count passed to Builder constructor + * @param materialInstance the material to bind + */ + Builder& material(size_t index, MaterialInstance const* materialInstance) noexcept; + + /** + * The axis-aligned bounding box of the renderable. + * + * This is an object-space AABB used for frustum culling. For skinning and morphing, this + * should encompass all possible vertex positions. It is mandatory unless culling is + * disabled for the renderable. + * + * \see computeAABB() + */ + Builder& boundingBox(const Box& axisAlignedBoundingBox) noexcept; + + /** + * Sets bits in a visibility mask. By default, this is 0x1. + * + * This feature provides a simple mechanism for hiding and showing groups of renderables + * in a Scene. See View::setVisibleLayers(). + * + * For example, to set bit 1 and reset bits 0 and 2 while leaving all other bits unaffected, + * do: `builder.layerMask(7, 2)`. + * + * To change this at run time, see RenderableManager::setLayerMask. + * + * @param select the set of bits to affect + * @param values the replacement values for the affected bits + */ + Builder& layerMask(uint8_t select, uint8_t values) noexcept; + + /** + * Provides coarse-grained control over draw order. + * + * In general Filament reserves the right to re-order renderables to allow for efficient + * rendering. However clients can control ordering at a coarse level using \em priority. + * + * For example, this could be used to draw a semitransparent HUD, if a client wishes to + * avoid using a separate View for the HUD. Note that priority is completely orthogonal to + * Builder::layerMask, which merely controls visibility. + * + * \see Builder::blendOrder() + * + * The priority is clamped to the range [0..7], defaults to 4; 7 is lowest priority + * (rendered last). + */ + Builder& priority(uint8_t priority) noexcept; + + /** + * Controls frustum culling, true by default. + * + * \note Do not confuse frustum culling with backface culling. The latter is controlled via + * the material. + */ + Builder& culling(bool enable) noexcept; + + /** + * Enables or disables a light channel. Light channel 0 is enabled by default. + * + * @param channel Light channel to enable or disable, between 0 and 7. + * @param enable Whether to enable or disable the light channel. + */ + Builder& lightChannel(unsigned int channel, bool enable = true) noexcept; + + /** + * Controls if this renderable casts shadows, false by default. + * + * If the View's shadow type is set to ShadowType::VSM, castShadows should only be disabled + * if either is true: + * - receiveShadows is also disabled + * - the object is guaranteed to not cast shadows on itself or other objects (for example, + * a ground plane) + */ + Builder& castShadows(bool enable) noexcept; + + /** + * Controls if this renderable receives shadows, true by default. + */ + Builder& receiveShadows(bool enable) noexcept; + + /** + * Controls if this renderable uses screen-space contact shadows. This is more + * expensive but can improve the quality of shadows, especially in large scenes. + * (off by default). + */ + Builder& screenSpaceContactShadows(bool enable) noexcept; + + /** + * Allows bones to be swapped out and shared using SkinningBuffer. + * + * If skinning buffer mode is enabled, clients must call setSkinningBuffer() rather than + * setBones(). This allows sharing of data between renderables. + * + * @param enabled If true, enables buffer object mode. False by default. + */ + Builder& enableSkinningBuffers(bool enabled = true) noexcept; + + /** + * Enables GPU vertex skinning for up to 255 bones, 0 by default. + * + * Skinning Buffer mode must be enabled. + * + * Each vertex can be affected by up to 4 bones simultaneously. The attached + * VertexBuffer must provide data in the \c BONE_INDICES slot (uvec4) and the + * \c BONE_WEIGHTS slot (float4). + * + * See also RenderableManager::setSkinningBuffer() or SkinningBuffer::setBones(), + * which can be called on a per-frame basis to advance the animation. + * + * @param skinningBuffer nullptr to disable, otherwise the SkinningBuffer to use + * @param count 0 to disable, otherwise the number of bone transforms (up to 255) + * @param offset offset in the SkinningBuffer + */ + Builder& skinning(SkinningBuffer* skinningBuffer, size_t count, size_t offset) noexcept; + + + /** + * Enables GPU vertex skinning for up to 255 bones, 0 by default. + * + * Skinning Buffer mode must be disabled. + * + * Each vertex can be affected by up to 4 bones simultaneously. The attached + * VertexBuffer must provide data in the \c BONE_INDICES slot (uvec4) and the + * \c BONE_WEIGHTS slot (float4). + * + * See also RenderableManager::setBones(), which can be called on a per-frame basis + * to advance the animation. + * + * @param boneCount 0 to disable, otherwise the number of bone transforms (up to 255) + * @param transforms the initial set of transforms (one for each bone) + */ + Builder& skinning(size_t boneCount, math::mat4f const* transforms) noexcept; + Builder& skinning(size_t boneCount, Bone const* bones) noexcept; //!< \overload + Builder& skinning(size_t boneCount) noexcept; //!< \overload + + /** + * Controls if the renderable has vertex morphing targets, false by default. + * + * This is required to enable GPU morphing for up to 4 attributes. The attached VertexBuffer + * must provide data in the appropriate VertexAttribute slots (\c MORPH_POSITION_0 etc). + * + * See also RenderableManager::setMorphWeights(), which can be called on a per-frame basis + * to advance the animation. + */ + Builder& morphing(bool enable) noexcept; + + /** + * Sets an ordering index for blended primitives that all live at the same Z value. + * + * @param primitiveIndex the primitive of interest + * @param order draw order number (0 by default). Only the lowest 15 bits are used. + */ + Builder& blendOrder(size_t primitiveIndex, uint16_t order) noexcept; + + /** + * Adds the Renderable component to an entity. + * + * @param engine Reference to the filament::Engine to associate this Renderable with. + * @param entity Entity to add the Renderable component to. + * @return Success if the component was created successfully, Error otherwise. + * + * If exceptions are disabled and an error occurs, this function is a no-op. + * Success can be checked by looking at the return value. + * + * If this component already exists on the given entity and the construction is successful, + * it is first destroyed as if destroy(utils::Entity e) was called. In case of error, + * the existing component is unmodified. + * + * @exception utils::PostConditionPanic if a runtime error occurred, such as running out of + * memory or other resources. + * @exception utils::PreConditionPanic if a parameter to a builder function was invalid. + */ + Result build(Engine& engine, utils::Entity entity); + + private: + friend class FEngine; + friend class FRenderPrimitive; + friend class FRenderableManager; + struct Entry { + VertexBuffer* vertices = nullptr; + IndexBuffer* indices = nullptr; + size_t offset = 0; + size_t minIndex = 0; + size_t maxIndex = 0; + size_t count = 0; + MaterialInstance const* materialInstance = nullptr; + PrimitiveType type = PrimitiveType::TRIANGLES; + uint16_t blendOrder = 0; + }; + }; + + /** + * Destroys the renderable component in the given entity. + */ + void destroy(utils::Entity e) noexcept; + + /** + * Changes the bounding box used for frustum culling. + * + * \see Builder::boundingBox() + * \see RenderableManager::getAxisAlignedBoundingBox() + */ + void setAxisAlignedBoundingBox(Instance instance, const Box& aabb) noexcept; + + /** + * Changes the visibility bits. + * + * \see Builder::layerMask() + * \see View::setVisibleLayers(). + * \see RenderableManager::getLayerMask() + */ + void setLayerMask(Instance instance, uint8_t select, uint8_t values) noexcept; + + /** + * Changes the coarse-level draw ordering. + * + * \see Builder::priority(). + */ + void setPriority(Instance instance, uint8_t priority) noexcept; + + /** + * Changes whether or not frustum culling is on. + * + * \see Builder::culling() + */ + void setCulling(Instance instance, bool enable) noexcept; + + /** + * Enables or disables a light channel. + * Light channel 0 is enabled by default. + * + * \see Builder::lightChannel() + */ + void setLightChannel(Instance instance, unsigned int channel, bool enable) noexcept; + + /** + * Returns whether a light channel is enabled on a specified renderable. + * @param instance Instance of the component obtained from getInstance(). + * @param channel Light channel to query + * @return true if the light channel is enabled, false otherwise + */ + bool getLightChannel(Instance instance, unsigned int channel) const noexcept; + + /** + * Changes whether or not the renderable casts shadows. + * + * \see Builder::castShadows() + */ + void setCastShadows(Instance instance, bool enable) noexcept; + + /** + * Changes whether or not the renderable can receive shadows. + * + * \see Builder::receiveShadows() + */ + void setReceiveShadows(Instance instance, bool enable) noexcept; + + /** + * Changes whether or not the renderable can use screen-space contact shadows. + * + * \see Builder::screenSpaceContactShadows() + */ + void setScreenSpaceContactShadows(Instance instance, bool enable) noexcept; + + /** + * Checks if the renderable can cast shadows. + * + * \see Builder::castShadows(). + */ + bool isShadowCaster(Instance instance) const noexcept; + + /** + * Checks if the renderable can receive shadows. + * + * \see Builder::receiveShadows(). + */ + bool isShadowReceiver(Instance instance) const noexcept; + + /** + * Updates the bone transforms in the range [offset, offset + boneCount). + * The bones must be pre-allocated using Builder::skinning(). + */ + void setBones(Instance instance, Bone const* transforms, size_t boneCount = 1, size_t offset = 0) noexcept; + void setBones(Instance instance, math::mat4f const* transforms, size_t boneCount = 1, size_t offset = 0) noexcept; //!< \overload + + /** + * Associates a SkinningBuffer to a renderable instance + */ + void setSkinningBuffer(Instance instance, SkinningBuffer* skinningBuffer, + size_t count, size_t offset) noexcept; + + /** + * Updates the vertex morphing weights on a renderable, all zeroes by default. + * + * This is specified using a 4-tuple, one float per morph target. If the renderable has fewer + * than 4 morph targets, then clients should fill the unused components with zeroes. + * + * The renderable must be built with morphing enabled, see Builder::morphing(). + */ + void setMorphWeights(Instance instance, math::float4 const& weights) noexcept; + + /** + * Gets the bounding box used for frustum culling. + * + * \see Builder::boundingBox() + * \see RenderableManager::setAxisAlignedBoundingBox() + */ + const Box& getAxisAlignedBoundingBox(Instance instance) const noexcept; + + /** + * Get the visibility bits. + * + * \see Builder::layerMask() + * \see View::setVisibleLayers(). + * \see RenderableManager::getLayerMask() + */ + uint8_t getLayerMask(Instance instance) const noexcept; + + /** + * Gets the immutable number of primitives in the given renderable. + */ + size_t getPrimitiveCount(Instance instance) const noexcept; + + /** + * Changes the material instance binding for the given primitive. + * + * \see Builder::material() + */ + void setMaterialInstanceAt(Instance instance, + size_t primitiveIndex, MaterialInstance const* materialInstance) noexcept; + + /** + * Retrieves the material instance that is bound to the given primitive. + */ + MaterialInstance* getMaterialInstanceAt(Instance instance, size_t primitiveIndex) const noexcept; + + /** + * Changes the geometry for the given primitive. + * + * \see Builder::geometry() + */ + void setGeometryAt(Instance instance, size_t primitiveIndex, + PrimitiveType type, VertexBuffer* vertices, IndexBuffer* indices, + size_t offset, size_t count) noexcept; + + /** + * Changes the active range of indices or topology for the given primitive. + * + * \see Builder::geometry() + */ + void setGeometryAt(Instance instance, size_t primitiveIndex, + PrimitiveType type, size_t offset, size_t count) noexcept; + + /** + * Changes the ordering index for blended primitives that all live at the same Z value. + * + * \see Builder::blendOrder() + * + * @param instance the renderable of interest + * @param primitiveIndex the primitive of interest + * @param order draw order number (0 by default). Only the lowest 15 bits are used. + */ + void setBlendOrderAt(Instance instance, size_t primitiveIndex, uint16_t order) noexcept; + + /** + * Retrieves the set of enabled attribute slots in the given primitive's VertexBuffer. + */ + AttributeBitset getEnabledAttributesAt(Instance instance, size_t primitiveIndex) const noexcept; + + /*! \cond PRIVATE */ + template + struct is_supported_vector_type { + using type = typename std::enable_if< + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value + >::type; + }; + + template + struct is_supported_index_type { + using type = typename std::enable_if< + std::is_same::value || + std::is_same::value + >::type; + }; + /*! \endcond */ + + /** + * Utility method that computes the axis-aligned bounding box from a set of vertices. + * + * - The index type must be \c uint16_t or \c uint32_t. + * - The vertex type must be \c float4, \c half4, \c float3, or \c half3. + * - For 4-component vertices, the w component is ignored (implicitly replaced with 1.0). + */ + template::type, + typename = typename is_supported_index_type::type> + static Box computeAABB(VECTOR const* vertices, INDEX const* indices, size_t count, + size_t stride = sizeof(VECTOR)) noexcept; +}; + +template +Box RenderableManager::computeAABB(VECTOR const* vertices, INDEX const* indices, size_t count, + size_t stride) noexcept { + math::float3 bmin(std::numeric_limits::max()); + math::float3 bmax(std::numeric_limits::lowest()); + for (size_t i = 0; i < count; ++i) { + VECTOR const* p = reinterpret_cast( + (char const*)vertices + indices[i] * stride); + const math::float3 v(p->x, p->y, p->z); + bmin = min(bmin, v); + bmax = max(bmax, v); + } + return Box().set(bmin, bmax); +} + +} // namespace filament + +#endif // TNT_FILAMENT_RENDERABLECOMPONENTMANAGER_H diff --git a/ios/include/filament/Renderer.h b/ios/include/filament/Renderer.h new file mode 100644 index 00000000..bd9c5c23 --- /dev/null +++ b/ios/include/filament/Renderer.h @@ -0,0 +1,540 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! \file + +#ifndef TNT_FILAMENT_RENDERER_H +#define TNT_FILAMENT_RENDERER_H + +#include + +#include + +#include +#include + +#include + +#include + +namespace filament { + +class Engine; +class RenderTarget; +class SwapChain; +class View; +class Viewport; + +namespace backend { +class PixelBufferDescriptor; +} // namespace backend + +/** + * A Renderer instance represents an operating system's window. + * + * Typically, applications create a Renderer per window. The Renderer generates drawing commands + * for the render thread and manages frame latency. + * + * A Renderer generates drawing commands from a View, itself containing a Scene description. + * + * Creation and Destruction + * ======================== + * + * A Renderer is created using Engine.createRenderer() and destroyed using + * Engine.destroy(const Renderer*). + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * #include + * #include + * using namespace filament; + * + * Engine* engine = Engine::create(); + * + * Renderer* renderer = engine->createRenderer(); + * engine->destroy(&renderer); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * @see Engine, View + */ +class UTILS_PUBLIC Renderer : public FilamentAPI { +public: + + /** + * Use DisplayInfo to set important Display properties. This is used to achieve correct + * frame pacing and dynamic resolution scaling. + */ + struct DisplayInfo { + // refresh-rate of the display in Hz. set to 0 for offscreen or turn off frame-pacing. + float refreshRate = 60.0f; + + // how far in advance a buffer must be queued for presentation at a given time in ns + uint64_t presentationDeadlineNanos = 0; + + // offset by which vsyncSteadyClockTimeNano provided in beginFrame() is offset in ns + uint64_t vsyncOffsetNanos = 0; + }; + + /** + * Use FrameRateOptions to set the desired frame rate and control how quickly the system + * reacts to GPU load changes. + * + * interval: desired frame interval in multiple of the refresh period, set in DisplayInfo + * (as 1 / DisplayInfo::refreshRate) + * + * The parameters below are relevant when some Views are using dynamic resolution scaling: + * + * headRoomRatio: additional headroom for the GPU as a ratio of the targetFrameTime. + * Useful for taking into account constant costs like post-processing or + * GPU drivers on different platforms. + * history: History size. higher values, tend to filter more (clamped to 30) + * scaleRate: rate at which the gpu load is adjusted to reach the target frame rate + * This value can be computed as 1 / N, where N is the number of frames + * needed to reach 64% of the target scale factor. + * Higher values make the dynamic resolution react faster. + * + * @see View::DynamicResolutionOptions + * @see Renderer::DisplayInfo + * + */ + struct FrameRateOptions { + float headRoomRatio = 0.0f; //!< additional headroom for the GPU + float scaleRate = 0.125f; //!< rate at which the system reacts to load changes + uint8_t history = 3; //!< history size + uint8_t interval = 1; //!< desired frame interval in unit of 1.0 / DisplayInfo::refreshRate + }; + + /** + * ClearOptions are used at the beginning of a frame to clear or retain the SwapChain content. + */ + struct ClearOptions { + /** Color to use to clear the SwapChain */ + math::float4 clearColor = {}; + /** + * Whether the SwapChain should be cleared using the clearColor. Use this if translucent + * View will be drawn, for instance. + */ + bool clear = false; + /** + * Whether the SwapChain content should be discarded. clear implies discard. Set this + * to false (along with clear to false as well) if the SwapChain already has content that + * needs to be preserved + */ + bool discard = true; + }; + + /** + * Information about the display this Renderer is associated to. This information is needed + * to accurately compute dynamic-resolution scaling and for frame-pacing. + */ + void setDisplayInfo(const DisplayInfo& info) noexcept; + + /** + * Set options controlling the desired frame-rate. + */ + void setFrameRateOptions(FrameRateOptions const& options) noexcept; + + /** + * Set ClearOptions which are used at the beginning of a frame to clear or retain the + * SwapChain content. + */ + void setClearOptions(const ClearOptions& options); + + /** + * Get the Engine that created this Renderer. + * + * @return A pointer to the Engine instance this Renderer is associated to. + */ + Engine* getEngine() noexcept; + + /** + * Get the Engine that created this Renderer. + * + * @return A constant pointer to the Engine instance this Renderer is associated to. + */ + inline Engine const* getEngine() const noexcept { + return const_cast(this)->getEngine(); + } + + /** + * Flags used to configure the behavior of copyFrame(). + * + * @see + * copyFrame() + */ + using CopyFrameFlag = uint32_t; + + /** + * Indicates that the dstSwapChain passed into copyFrame() should be + * committed after the frame has been copied. + * + * @see + * copyFrame() + */ + static constexpr CopyFrameFlag COMMIT = 0x1; + /** + * Indicates that the presentation time should be set on the dstSwapChain + * passed into copyFrame to the monotonic clock time when the frame is + * copied. + * + * @see + * copyFrame() + */ + static constexpr CopyFrameFlag SET_PRESENTATION_TIME = 0x2; + /** + * Indicates that the dstSwapChain passed into copyFrame() should be + * cleared to black before the frame is copied into the specified viewport. + * + * @see + * copyFrame() + */ + static constexpr CopyFrameFlag CLEAR = 0x4; + + + /** + * Set-up a frame for this Renderer. + * + * beginFrame() manages frame pacing, and returns whether or not a frame should be drawn. The + * goal of this is to skip frames when the GPU falls behind in order to keep the frame + * latency low. + * + * If a given frame takes too much time in the GPU, the CPU will get ahead of the GPU. The + * display will draw the same frame twice producing a stutter. At this point, the CPU is + * ahead of the GPU and depending on how many frames are buffered, latency increases. + * + * beginFrame() attempts to detect this situation and returns false in that case, indicating + * to the caller to skip the current frame. + * + * When beginFrame() returns true, it is mandatory to render the frame and call endFrame(). + * However, when beginFrame() returns false, the caller has the choice to either skip the + * frame and not call endFrame(), or proceed as though true was returned. + * + * @param vsyncSteadyClockTimeNano The time in nanosecond of when the current frame started, + * or 0 if unknown. This value should be the timestamp of + * the last h/w vsync. It is expressed in the + * std::chrono::steady_clock time base. + * @param swapChain A pointer to the SwapChain instance to use. + * + * @return + * *false* the current frame should be skipped, + * *true* the current frame must be drawn and endFrame() must be called. + * + * @remark + * When skipping a frame, the whole frame is canceled, and endFrame() must not be called. + * + * @note + * All calls to render() must happen *after* beginFrame(). + * + * @see + * endFrame() + */ + bool beginFrame(SwapChain* swapChain, + uint64_t vsyncSteadyClockTimeNano = 0u); + + /** + * Render a View into this renderer's window. + * + * This is filament main rendering method, most of the CPU-side heavy lifting is performed + * here. render() main function is to generate render commands which are asynchronously + * executed by the Engine's render thread. + * + * render() generates commands for each of the following stages: + * + * 1. Shadow map pass, if needed (currently only a single shadow map is supported). + * 2. Depth pre-pass. + * 3. Color pass. + * 4. Post-processing pass. + * + * A typical render loop looks like this: + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * #include + * #include + * using namespace filament; + * + * void renderLoop(Renderer* renderer, SwapChain* swapChain) { + * do { + * // typically we wait for VSYNC and user input events + * if (renderer->beginFrame(swapChain)) { + * renderer->render(mView); + * renderer->endFrame(); + * } + * } while (!quit()); + * } + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * + * @param view A pointer to the view to render. + * + * @attention + * render() must be called *after* beginFrame() and *before* endFrame(). + * + * @note + * render() must be called from the Engine's main thread (or external synchronization + * must be provided). In particular, calls to render() on different Renderer instances + * **must** be synchronized. + * + * @remark + * render() perform potentially heavy computations and cannot be multi-threaded. However, + * internally, render() is highly multi-threaded to both improve performance in mitigate + * the call's latency. + * + * @remark + * render() is typically called once per frame (but not necessarily). + * + * @see + * beginFrame(), endFrame(), View + * + */ + void render(View const* view); + + /** + * Copy the currently rendered view to the indicated swap chain, using the + * indicated source and destination rectangle. + * + * @param dstSwapChain The swap chain into which the frame should be copied. + * @param dstViewport The destination rectangle in which to draw the view. + * @param srcViewport The source rectangle to be copied. + * @param flags One or more CopyFrameFlag behavior configuration flags. + * + * @remark + * copyFrame() should be called after a frame is rendered using render() + * but before endFrame() is called. + */ + void copyFrame(SwapChain* dstSwapChain, Viewport const& dstViewport, + Viewport const& srcViewport, uint32_t flags = 0); + + /** + * Reads back the content of the SwapChain associated with this Renderer. + * + * @param xoffset Left offset of the sub-region to read back. + * @param yoffset Bottom offset of the sub-region to read back. + * @param width Width of the sub-region to read back. + * @param height Height of the sub-region to read back. + * @param buffer Client-side buffer where the read-back will be written. + * + * The following formats are always supported: + * - PixelBufferDescriptor::PixelDataFormat::RGBA + * - PixelBufferDescriptor::PixelDataFormat::RGBA_INTEGER + * + * The following types are always supported: + * - PixelBufferDescriptor::PixelDataType::UBYTE + * - PixelBufferDescriptor::PixelDataType::UINT + * - PixelBufferDescriptor::PixelDataType::INT + * - PixelBufferDescriptor::PixelDataType::FLOAT + * + * Other combinations of format/type may be supported. If a combination is + * not supported, this operation may fail silently. Use a DEBUG build + * to get some logs about the failure. + * + * + * Framebuffer as seen on User buffer (PixelBufferDescriptor&) + * screen + * + * +--------------------+ + * | | .stride .alignment + * | | ----------------------->--> + * | | O----------------------+--+ low addresses + * | | | | | | + * | w | | | .top | | + * | <---------> | | V | | + * | +---------+ | | +---------+ | | + * | | ^ | | ======> | | | | | + * | x | h| | | |.left| | | | + * +------>| v | | +---->| | | | + * | +.........+ | | +.........+ | | + * | ^ | | | | + * | y | | +----------------------+--+ high addresses + * O------------+-------+ + * + * + * Typically readPixels() will be called after render() and before endFrame(). + * + * After issuing this method, the callback associated with `buffer` will be invoked on the + * main thread, indicating that the read-back has completed. Typically, this will happen + * after multiple calls to beginFrame(), render(), endFrame(). + * + * It is also possible to use a Fence to wait for the read-back. + * + * @remark + * readPixels() is intended for debugging and testing. It will impact performance significantly. + * + */ + void readPixels(uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height, + backend::PixelBufferDescriptor&& buffer); + + /** + * Finishes the current frame and schedules it for display. + * + * endFrame() schedules the current frame to be displayed on the Renderer's window. + * + * @note + * All calls to render() must happen *before* endFrame(). endFrame() must be called if + * beginFrame() returned true, otherwise, endFrame() must not be called unless the caller + * ignored beginFrame()'s return value. + * + * @see + * beginFrame() + */ + void endFrame(); + + + /** + * Reads back the content of the provided RenderTarget. + * + * @param renderTarget RenderTarget to read back from. + * @param xoffset Left offset of the sub-region to read back. + * @param yoffset Bottom offset of the sub-region to read back. + * @param width Width of the sub-region to read back. + * @param height Height of the sub-region to read back. + * @param buffer Client-side buffer where the read-back will be written. + * + * The following formats are always supported: + * - PixelBufferDescriptor::PixelDataFormat::RGBA + * - PixelBufferDescriptor::PixelDataFormat::RGBA_INTEGER + * + * The following types are always supported: + * - PixelBufferDescriptor::PixelDataType::UBYTE + * - PixelBufferDescriptor::PixelDataType::UINT + * - PixelBufferDescriptor::PixelDataType::INT + * - PixelBufferDescriptor::PixelDataType::FLOAT + * + * Other combinations of format/type may be supported. If a combination is + * not supported, this operation may fail silently. Use a DEBUG build + * to get some logs about the failure. + * + * + * Framebuffer as seen on User buffer (PixelBufferDescriptor&) + * screen + * + * +--------------------+ + * | | .stride .alignment + * | | ----------------------->--> + * | | O----------------------+--+ low addresses + * | | | | | | + * | w | | | .top | | + * | <---------> | | V | | + * | +---------+ | | +---------+ | | + * | | ^ | | ======> | | | | | + * | x | h| | | |.left| | | | + * +------>| v | | +---->| | | | + * | +.........+ | | +.........+ | | + * | ^ | | | | + * | y | | +----------------------+--+ high addresses + * O------------+-------+ + * + * + * Typically readPixels() will be called after render() and before endFrame(). + * + * After issuing this method, the callback associated with `buffer` will be invoked on the + * main thread, indicating that the read-back has completed. Typically, this will happen + * after multiple calls to beginFrame(), render(), endFrame(). + * + * It is also possible to use a Fence to wait for the read-back. + * + * @remark + * readPixels() is intended for debugging and testing. It will impact performance significantly. + * + */ + void readPixels(RenderTarget* renderTarget, + uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height, + backend::PixelBufferDescriptor&& buffer); + + /** + * Render a standalone View into its associated RenderTarget + * + * This call is mostly equivalent to calling render(View*) inside a + * beginFrame / endFrame block, but incurs less overhead. It can be used + * as a poor man's compute API. + * + * @param view A pointer to the view to render. This View must have a RenderTarget associated + * to it. + * + * @attention + * renderStandaloneView() must be called outside of beginFrame() / endFrame(). + * + * @note + * renderStandaloneView() must be called from the Engine's main thread + * (or external synchronization must be provided). In particular, calls to + * renderStandaloneView() on different Renderer instances **must** be synchronized. + * + * @remark + * renderStandaloneView() perform potentially heavy computations and cannot be multi-threaded. + * However, internally, renderStandaloneView() is highly multi-threaded to both improve + * performance in mitigate the call's latency. + */ + void renderStandaloneView(View const* view); + + + /** + * Returns the time in second of the last call to beginFrame(). This value is constant for all + * views rendered during a frame. The epoch is set with resetUserTime(). + * + * In materials, this value can be queried using `vec4 getUserTime()`. The value returned + * is a highp vec4 encoded as follows: + * + * time.x = (float)Renderer.getUserTime(); + * time.y = Renderer.getUserTime() - time.x; + * + * It follows that the following invariants are true: + * + * (double)time.x + (double)time.y == Renderer.getUserTime() + * time.x == (float)Renderer.getUserTime() + * + * This encoding allows the shader code to perform high precision (i.e. double) time + * calculations when needed despite the lack of double precision in the shader, for e.g.: + * + * To compute (double)time * vertex in the material, use the following construct: + * + * vec3 result = time.x * vertex + time.y * vertex; + * + * + * Most of the time, high precision computations are not required, but be aware that the + * precision of time.x rapidly diminishes as time passes: + * + * time | precision + * --------+---------- + * 16.7s | us + * 4h39 | ms + * 77h | 1/60s + * + * + * In other words, it only possible to get microsecond accuracy for about 16s or millisecond + * accuracy for just under 5h. + * + * This problem can be mitigated by calling resetUserTime(), or using high precision time as + * described above. + * + * @return The time is seconds since resetUserTime() was last called. + * + * @see + * resetUserTime() + */ + double getUserTime() const; + + /** + * Sets the user time epoch to now, i.e. resets the user time to zero. + * + * Use this method used to keep the precision of time high in materials, in practice it should + * be called at least when the application is paused, e.g. Activity.onPause() in Android. + * + * @see + * getUserTime() + */ + void resetUserTime(); +}; + +} // namespace filament + +#endif // TNT_FILAMENT_RENDERER_H diff --git a/ios/include/filament/Scene.h b/ios/include/filament/Scene.h new file mode 100644 index 00000000..7ef5c22f --- /dev/null +++ b/ios/include/filament/Scene.h @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! \file + +#ifndef TNT_FILAMENT_SCENE_H +#define TNT_FILAMENT_SCENE_H + +#include + +#include + +namespace utils { + class Entity; +} // namespace utils + +namespace filament { + +class IndirectLight; +class Skybox; + +/** + * A Scene is a flat container of Renderable and Light instances. + * + * A Scene doesn't provide a hierarchy of Renderable objects, i.e.: it's not a scene-graph. + * However, it manages the list of objects to render and the list of lights. Renderable + * and Light objects can be added or removed from a Scene at any time. + * + * A Renderable *must* be added to a Scene in order to be rendered, and the Scene must be + * provided to a View. + * + * + * Creation and Destruction + * ======================== + * + * A Scene is created using Engine.createScene() and destroyed using + * Engine.destroy(const Scene*). + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * #include + * #include + * using namespace filament; + * + * Engine* engine = Engine::create(); + * + * Scene* scene = engine->createScene(); + * engine->destroy(&scene); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * @see View, Renderable, Light + */ +class UTILS_PUBLIC Scene : public FilamentAPI { +public: + + /** + * Sets the Skybox. + * + * The Skybox is drawn last and covers all pixels not touched by geometry. + * + * @param skybox The Skybox to use to fill untouched pixels, or nullptr to unset the Skybox. + */ + void setSkybox(Skybox* skybox) noexcept; + + /** + * Returns the Skybox associated with the Scene. + * + * @return The associated Skybox, or nullptr if there is none. + */ + Skybox* getSkybox() noexcept; + + /** + * Returns an immutable Skybox associated with the Scene. + * + * @return The associated Skybox, or nullptr if there is none. + */ + Skybox const* getSkybox() const noexcept; + + /** + * Set the IndirectLight to use when rendering the Scene. + * + * Currently, a Scene may only have a single IndirectLight. This call replaces the current + * IndirectLight. + * + * @param ibl The IndirectLight to use when rendering the Scene or nullptr to unset. + */ + void setIndirectLight(IndirectLight const* ibl) noexcept; + + /** + * Adds an Entity to the Scene. + * + * @param entity The entity is ignored if it doesn't have a Renderable or Light component. + * + * \attention + * A given Entity object can only be added once to a Scene. + * + */ + void addEntity(utils::Entity entity); + + /** + * Adds a list of entities to the Scene. + * + * @param entities Array containing entities to add to the scene. + * @param count Size of the entity array. + */ + void addEntities(const utils::Entity* entities, size_t count); + + /** + * Removes the Renderable from the Scene. + * + * @param entity The Entity to remove from the Scene. If the specified + * \p entity doesn't exist, this call is ignored. + */ + void remove(utils::Entity entity); + + /** + * Removes a list of entities to the Scene. + * + * This is equivalent to calling remove in a loop. + * If any of the specified entities do not exist in the scene, they are skipped. + * + * @param entities Array containing entities to remove from the scene. + * @param count Size of the entity array. + */ + void removeEntities(const utils::Entity* entities, size_t count); + + /** + * Returns the number of Renderable objects in the Scene. + * + * @return number of Renderable objects in the Scene. + */ + size_t getRenderableCount() const noexcept; + + /** + * Returns the total number of Light objects in the Scene. + * + * @return The total number of Light objects in the Scene. + */ + size_t getLightCount() const noexcept; + + /** + * Returns true if the given entity is in the Scene. + * + * @return Whether the given entity is in the Scene. + */ + bool hasEntity(utils::Entity entity) const noexcept; +}; + +} // namespace filament + +#endif // TNT_FILAMENT_SCENE_H diff --git a/ios/include/filament/SkinningBuffer.h b/ios/include/filament/SkinningBuffer.h new file mode 100644 index 00000000..ca313d74 --- /dev/null +++ b/ios/include/filament/SkinningBuffer.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_SKINNINGBUFFER_H +#define TNT_FILAMENT_SKINNINGBUFFER_H + +#include + +#include + +#include + +#include + +#include + + +namespace filament { + +/** + * SkinningBuffer is used to hold skinning data (bones). It is a simple wraper around + * a structured UBO. + */ +class UTILS_PUBLIC SkinningBuffer : public FilamentAPI { + struct BuilderDetails; + +public: + class Builder : public BuilderBase { + friend struct BuilderDetails; + public: + Builder() noexcept; + Builder(Builder const& rhs) noexcept; + Builder(Builder&& rhs) noexcept; + ~Builder() noexcept; + Builder& operator=(Builder const& rhs) noexcept; + Builder& operator=(Builder&& rhs) noexcept; + + /** + * Size of the skinning buffer in bones. + * + * Due to limitation in the GLSL, the SkinningBuffer must always by a multiple of + * 256, this adjustment is done automatically, but can cause + * some memory overhead. This memory overhead can be mitigated by using the same + * SkinningBuffer to store the bone information for multiple RenderPrimitives. + * + * @param boneCount Number of bones the skinning buffer can hold. + * @return A reference to this Builder for chaining calls. + */ + Builder& boneCount(uint32_t boneCount) noexcept; + + /** + * The new buffer is created with identity bones + * @param initialize true to initializing the buffer, false to not. + * @return A reference to this Builder for chaining calls. + */ + Builder& initialize(bool initialize = true) noexcept; + + /** + * Creates the SkinningBuffer object and returns a pointer to it. + * + * @param engine Reference to the filament::Engine to associate this SkinningBuffer with. + * + * @return pointer to the newly created object or nullptr if exceptions are disabled and + * an error occurred. + * + * @exception utils::PostConditionPanic if a runtime error occurred, such as running out of + * memory or other resources. + * @exception utils::PreConditionPanic if a parameter to a builder function was invalid. + * + * @see SkinningBuffer::setBones + */ + SkinningBuffer* build(Engine& engine); + private: + friend class FSkinningBuffer; + }; + + /** + * Updates the bone transforms in the range [offset, offset + count). + * @param engine Reference to the filament::Engine to associate this SkinningBuffer with. + * @param transforms pointer to at least count Bone + * @param count number of Bone elements in transforms + * @param offset offset in elements (not bytes) in the SkinningBuffer (not in transforms) + */ + void setBones(Engine& engine, RenderableManager::Bone const* transforms, + size_t count, size_t offset = 0); + + /** + * Updates the bone transforms in the range [offset, offset + count). + * @param engine Reference to the filament::Engine to associate this SkinningBuffer with. + * @param transforms pointer to at least count mat4f + * @param count number of mat4f elements in transforms + * @param offset offset in elements (not bytes) in the SkinningBuffer (not in transforms) + */ + void setBones(Engine& engine, math::mat4f const* transforms, + size_t count, size_t offset = 0); + + /** + * Returns the size of this SkinningBuffer in elements. + * @return The number of bones the SkinningBuffer holds. + */ + size_t getBoneCount() const noexcept; +}; + +} // namespace filament + +#endif //TNT_FILAMENT_SKINNINGBUFFER_H diff --git a/ios/include/filament/Skybox.h b/ios/include/filament/Skybox.h new file mode 100644 index 00000000..bf4342a4 --- /dev/null +++ b/ios/include/filament/Skybox.h @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! \file + +#ifndef TNT_FILAMENT_SKYBOX_H +#define TNT_FILAMENT_SKYBOX_H + +#include + +#include + +#include +#include + +namespace filament { + +class FSkybox; + +class Engine; +class Texture; + +/** + * Skybox + * + * When added to a Scene, the Skybox fills all untouched pixels. + * + * Creation and destruction + * ======================== + * + * A Skybox object is created using the Skybox::Builder and destroyed by calling + * Engine::destroy(const Skybox*). + * + * ~~~~~~~~~~~{.cpp} + * filament::Engine* engine = filament::Engine::create(); + * + * filament::IndirectLight* skybox = filament::Skybox::Builder() + * .environment(cubemap) + * .build(*engine); + * + * engine->destroy(skybox); + * ~~~~~~~~~~~ + * + * + * @note + * Currently only Texture based sky boxes are supported. + * + * @see Scene, IndirectLight + */ +class UTILS_PUBLIC Skybox : public FilamentAPI { + struct BuilderDetails; + +public: + //! Use Builder to construct an Skybox object instance + class Builder : public BuilderBase { + friend struct BuilderDetails; + public: + Builder() noexcept; + Builder(Builder const& rhs) noexcept; + Builder(Builder&& rhs) noexcept; + ~Builder() noexcept; + Builder& operator=(Builder const& rhs) noexcept; + Builder& operator=(Builder&& rhs) noexcept; + + /** + * Set the environment map (i.e. the skybox content). + * + * The Skybox is rendered as though it were an infinitely large cube with the camera + * inside it. This means that the cubemap which is mapped onto the cube's exterior + * will appear mirrored. This follows the OpenGL conventions. + * + * The cmgen tool generates reflection maps by default which are therefore ideal to use + * as skyboxes. + * + * @param cubemap This Texture must be a cube map. + * + * @return This Builder, for chaining calls. + * + * @see Texture + */ + Builder& environment(Texture* cubemap) noexcept; + + /** + * Indicates whether the sun should be rendered. The sun can only be + * rendered if there is at least one light of type SUN in the scene. + * The default value is false. + * + * @param show True if the sun should be rendered, false otherwise + * + * @return This Builder, for chaining calls. + */ + Builder& showSun(bool show) noexcept; + + /** + * Skybox intensity when no IndirectLight is set on the Scene. + * + * This call is ignored when an IndirectLight is set on the Scene, and the intensity + * of the IndirectLight is used instead. + * + * @param envIntensity Scale factor applied to the skybox texel values such that + * the result is in lux, or lumen/m^2 (default = 30000) + * + * @return This Builder, for chaining calls. + * + * @see IndirectLight::Builder::intensity + */ + Builder& intensity(float envIntensity) noexcept; + + /** + * Sets the skybox to a constant color. Default is opaque black. + * + * Ignored if an environment is set. + * + * @param color + * + * @return This Builder, for chaining calls. + */ + Builder& color(math::float4 color) noexcept; + + /** + * Creates the Skybox object and returns a pointer to it. + * + * @param engine Reference to the filament::Engine to associate this Skybox with. + * + * @return pointer to the newly created object, or nullptr if the light couldn't be created. + */ + Skybox* build(Engine& engine); + + private: + friend class FSkybox; + }; + + void setColor(math::float4 color) noexcept; + + /** + * Sets bits in a visibility mask. By default, this is 0x1. + * + * This provides a simple mechanism for hiding or showing this Skybox in a Scene. + * + * @see View::setVisibleLayers(). + * + * For example, to set bit 1 and reset bits 0 and 2 while leaving all other bits unaffected, + * call: `setLayerMask(7, 2)`. + * + * @param select the set of bits to affect + * @param values the replacement values for the affected bits + */ + void setLayerMask(uint8_t select, uint8_t values) noexcept; + + /** + * @return the visibility mask bits + */ + uint8_t getLayerMask() const noexcept; + + /** + * Returns the skybox's intensity in lux, or lumen/m^2. + */ + float getIntensity() const noexcept; + + /** + * @return the associated texture, or null if it does not exist + */ + Texture const* getTexture() const noexcept; +}; + +} // namespace filament + +#endif // TNT_FILAMENT_SKYBOX_H diff --git a/ios/include/filament/Stream.h b/ios/include/filament/Stream.h new file mode 100644 index 00000000..af675ee7 --- /dev/null +++ b/ios/include/filament/Stream.h @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_STREAM_H +#define TNT_FILAMENT_STREAM_H + +#include + +#include + +#include + +#include + +namespace filament { + +class FStream; + +class Engine; + +/** + * Stream is used to attach a video stream to a Filament `Texture`. + * + * Note that the `Stream` class is fairly Android centric. It supports three different + * configurations: + * + * - TEXTURE_ID...takes an OpenGL texture ID and incurs a copy + * - ACQUIRED.....connects to an Android AHardwareBuffer + * - NATIVE.......connects to an Android SurfaceTexture + * + * Before explaining these different configurations, let's review the high-level structure of an AR + * or video application that uses Filament: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * while (true) { + * + * // Misc application work occurs here, such as: + * // - Writing the image data for a video frame into a Stream + * // - Moving the Filament Camera + * + * if (renderer->beginFrame(swapChain)) { + * renderer->render(view); + * renderer->endFrame(); + * } + * } + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Let's say that the video image data at the time of a particular invocation of `beginFrame` + * becomes visible to users at time A. The 3D scene state (including the camera) at the time of + * that same invocation becomes apparent to users at time B. + * + * - If time A matches time B, we say that the stream is \em{synchronized}. + * - Filament invokes low-level graphics commands on the \em{driver thread}. + * - The thread that calls `beginFrame` is called the \em{main thread}. + * + * The TEXTURE_ID configuration achieves synchronization automatically. In this mode, Filament + * performs a copy on the main thread during `beginFrame` by blitting the external image into + * an internal round-robin queue of images. This copy has a run-time cost. + * + * For ACQUIRED streams, there is no need to perform the copy because Filament explictly acquires + * the stream, then releases it later via a callback function. This configuration is especially + * useful when the Vulkan backend is enabled. + * + * For NATIVE streams, Filament does not make any synchronization guarantee. However they are simple + * to use and do not incur a copy. These are often appropriate in video applications. + * + * Please see `sample-stream-test` and `sample-hello-camera` for usage examples. + * + * @see backend::StreamType + * @see Texture#setExternalStream + * @see Engine#destroyStream + */ +class UTILS_PUBLIC Stream : public FilamentAPI { + struct BuilderDetails; + +public: + using Callback = backend::StreamCallback; + using StreamType = backend::StreamType; + + /** + * Constructs a Stream object instance. + * + * By default, Stream objects are ACQUIRED and must have external images pushed to them via + *
Stream::setAcquiredImage
. + * + * To create a NATIVE or TEXTURE_ID stream, call one of the
stream
methods + * on the builder. + */ + class Builder : public BuilderBase { + friend struct BuilderDetails; + public: + Builder() noexcept; + Builder(Builder const& rhs) noexcept; + Builder(Builder&& rhs) noexcept; + ~Builder() noexcept; + Builder& operator=(Builder const& rhs) noexcept; + Builder& operator=(Builder&& rhs) noexcept; + + /** + * Creates a NATIVE stream. Native streams can sample data directly from an + * opaque platform object such as a SurfaceTexture on Android. + * + * @param stream An opaque native stream handle. e.g.: on Android this is an + * `android/graphics/SurfaceTexture` JNI jobject. The wrap mode must + * be CLAMP_TO_EDGE. + * + * @return This Builder, for chaining calls. + */ + Builder& stream(void* stream) noexcept; + + /** + * Creates a TEXTURE_ID stream. This will sample data from the supplied + * external texture and copy it into an internal private texture. + * + * @param externalTextureId An opaque texture id (typically a GLuint created with glGenTextures) + * In a context shared with filament. In that case this texture's + * target must be GL_TEXTURE_EXTERNAL_OES and the wrap mode must + * be CLAMP_TO_EDGE. + * + * @return This Builder, for chaining calls. + * + * @see Texture::setExternalStream() + * @deprecated this method existed only for ARCore which doesn't need this anymore, use Texture::import() instead. + */ + UTILS_DEPRECATED + Builder& stream(intptr_t externalTextureId) noexcept; + + /** + * + * @param width initial width of the incoming stream. Whether this value is used is + * stream dependent. On Android, it must be set when using + * Builder::stream(long externalTextureId). + * + * @return This Builder, for chaining calls. + */ + Builder& width(uint32_t width) noexcept; + + /** + * + * @param height initial height of the incoming stream. Whether this value is used is + * stream dependent. On Android, it must be set when using + * Builder::stream(long externalTextureId). + * + * @return This Builder, for chaining calls. + */ + Builder& height(uint32_t height) noexcept; + + /** + * Creates the Stream object and returns a pointer to it. + * + * @param engine Reference to the filament::Engine to associate this Stream with. + * + * @return pointer to the newly created object, or nullptr if the stream couldn't be created. + */ + Stream* build(Engine& engine); + + private: + friend class FStream; + }; + + /** + * Indicates whether this stream is a NATIVE stream, TEXTURE_ID stream, or ACQUIRED stream. + */ + StreamType getStreamType() const noexcept; + + /** + * Updates an ACQUIRED stream with an image that is guaranteed to be used in the next frame. + * + * This method tells Filament to immediately "acquire" the image and trigger a callback + * when it is done with it. This should be called by the user outside of beginFrame / endFrame, + * and should be called only once per frame. If the user pushes images to the same stream + * multiple times in a single frame, only the final image is honored, but all callbacks are + * invoked. + * + * This method should be called on the same thread that calls Renderer::beginFrame, which is + * also where the callback is invoked. This method can only be used for streams that were + * constructed without calling the `stream` method on the builder. + * + * @see Stream for more information about NATIVE, TEXTURE_ID, and ACQUIRED configurations. + * + * @param image Pointer to AHardwareBuffer, casted to void* since this is a public header. + * @param callback This is triggered by Filament when it wishes to release the image. + * It callback tales two arguments: the AHardwareBuffer and the userdata. + * @param userdata Optional closure data. Filament will pass this into the callback when it + * releases the image. + */ + void setAcquiredImage(void* image, Callback callback, void* userdata) noexcept; + + /** + * Updates the size of the incoming stream. Whether this value is used is + * stream dependent. On Android, it must be set when using + * Builder::stream(long externalTextureId). + * + * @param width new width of the incoming stream + * @param height new height of the incoming stream + */ + void setDimensions(uint32_t width, uint32_t height) noexcept; + + /** + * Read-back the content of the last frame of a Stream since the last call to + * Renderer.beginFrame(). + * + * The Stream must be of type externalTextureId. This function is a no-op otherwise. + * + * @param xoffset Left offset of the sub-region to read back. + * @param yoffset Bottom offset of the sub-region to read back. + * @param width Width of the sub-region to read back. + * @param height Height of the sub-region to read back. + * @param buffer Client-side buffer where the read-back will be written. + * + * The following format are always supported: + * - PixelBufferDescriptor::PixelDataFormat::RGBA + * - PixelBufferDescriptor::PixelDataFormat::RGBA_INTEGER + * + * The following types are always supported: + * - PixelBufferDescriptor::PixelDataType::UBYTE + * - PixelBufferDescriptor::PixelDataType::UINT + * - PixelBufferDescriptor::PixelDataType::INT + * - PixelBufferDescriptor::PixelDataType::FLOAT + * + * Other combination of format/type may be supported. If a combination is + * not supported, this operation may fail silently. Use a DEBUG build + * to get some logs about the failure. + * + * Stream buffer User buffer (PixelBufferDescriptor&) + * +--------------------+ + * | | .stride .alignment + * | | ----------------------->--> + * | | O----------------------+--+ low addresses + * | | | | | | + * | w | | | .top | | + * | <---------> | | V | | + * | +---------+ | | +---------+ | | + * | | ^ | | ======> | | | | | + * | x | h| | | |.left| | | | + * +------>| v | | +---->| | | | + * | +.........+ | | +.........+ | | + * | ^ | | | | + * | y | | +----------------------+--+ high addresses + * O------------+-------+ + * + * Typically readPixels() will be called after Renderer.beginFrame(). + * + * After issuing this method, the callback associated with `buffer` will be invoked on the + * main thread, indicating that the read-back has completed. Typically, this will happen + * after multiple calls to beginFrame(), render(), endFrame(). + * + * It is also possible to use a Fence to wait for the read-back. + * + * @remark + * readPixels() is intended for debugging and testing. It will impact performance significantly. + */ + void readPixels(uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height, + backend::PixelBufferDescriptor&& buffer) noexcept; + + /** + * Returns the presentation time of the currently displayed frame in nanosecond. + * + * This value can change at any time. + * + * @return timestamp in nanosecond. + */ + int64_t getTimestamp() const noexcept; +}; + +} // namespace filament + +#endif // TNT_FILAMENT_STREAM_H diff --git a/ios/include/filament/SwapChain.h b/ios/include/filament/SwapChain.h new file mode 100644 index 00000000..24847607 --- /dev/null +++ b/ios/include/filament/SwapChain.h @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_SWAPCHAIN_H +#define TNT_FILAMENT_SWAPCHAIN_H + +#include +#include +#include + +#include + +namespace filament { + +/** + * A swap chain represents an Operating System's *native* renderable surface. + * + * Typically it's a native window or a view. Because a SwapChain is initialized from a + * native object, it is given to filament as a `void *`, which must be of the proper type + * for each platform filament is running on. + * + * \code + * SwapChain* swapChain = engine->createSwapChain(nativeWindow); + * \endcode + * + * When Engine::create() is used without specifying a Platform, the `nativeWindow` + * parameter above must be of type: + * + * Platform | nativeWindow type + * :---------------|:----------------------------: + * Android | ANativeWindow* + * macOS - OpenGL | NSView* + * macOS - Metal | CAMetalLayer* + * iOS - OpenGL | CAEAGLLayer* + * iOS - Metal | CAMetalLayer* + * X11 | Window + * Windows | HWND + * + * Otherwise, the `nativeWindow` is defined by the concrete implementation of Platform. + * + * + * Examples: + * + * Android + * ------- + * + * On Android, an `ANativeWindow*` can be obtained from a Java `Surface` object using: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * #include + * // parameters + * // env: JNIEnv* + * // surface: jobject + * ANativeWindow* win = ANativeWindow_fromSurface(env, surface); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * \warning + * Don't use reflection to access the `mNativeObject` field, it won't work. + * + * A `Surface` can be retrieved from a `SurfaceView` or `SurfaceHolder` easily using + * `SurfaceHolder.getSurface()` and/or `SurfaceView.getHolder()`. + * + * \note + * To use a `TextureView` as a SwapChain, it is necessary to first get its `SurfaceTexture`, + * for instance using `TextureView.SurfaceTextureListener` and then create a `Surface`: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.java} + * // using a TextureView.SurfaceTextureListener: + * public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) { + * mSurface = new Surface(surfaceTexture); + * // mSurface can now be used in JNI to create an ANativeWindow. + * } + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Linux + * ----- + * + * Example using SDL: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * SDL_SysWMinfo wmi; + * SDL_VERSION(&wmi.version); + * SDL_GetWindowWMInfo(sdlWindow, &wmi); + * Window nativeWindow = (Window) wmi.info.x11.window; + * + * using namespace filament; + * Engine* engine = Engine::create(); + * SwapChain* swapChain = engine->createSwapChain((void*) nativeWindow); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Windows + * ------- + * + * Example using SDL: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * SDL_SysWMinfo wmi; + * SDL_VERSION(&wmi.version); + * ASSERT_POSTCONDITION(SDL_GetWindowWMInfo(sdlWindow, &wmi), "SDL version unsupported!"); + * HDC nativeWindow = (HDC) wmi.info.win.hdc; + * + * using namespace filament; + * Engine* engine = Engine::create(); + * SwapChain* swapChain = engine->createSwapChain((void*) nativeWindow); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * OSX + * --- + * + * On OSX, any `NSView` can be used *directly* as a `nativeWindow` with createSwapChain(). + * + * Example using SDL/Objective-C: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.mm} + * #include + * + * #include + * #include + * + * SDL_SysWMinfo wmi; + * SDL_VERSION(&wmi.version); + * NSWindow* win = (NSWindow*) wmi.info.cocoa.window; + * NSView* view = [win contentView]; + * void* nativeWindow = view; + * + * using namespace filament; + * Engine* engine = Engine::create(); + * SwapChain* swapChain = engine->createSwapChain(nativeWindow); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * @see Engine + */ +class UTILS_PUBLIC SwapChain : public FilamentAPI { +public: + using FrameScheduledCallback = backend::FrameScheduledCallback; + using FrameCompletedCallback = backend::FrameCompletedCallback; + + static const uint64_t CONFIG_TRANSPARENT = backend::SWAP_CHAIN_CONFIG_TRANSPARENT; + /** + * This flag indicates that the swap chain may be used as a source surface + * for reading back render results. This config must be set when creating + * any swap chain that will be used as the source for a blit operation. + * + * @see + * Renderer.copyFrame() + */ + static const uint64_t CONFIG_READABLE = backend::SWAP_CHAIN_CONFIG_READABLE; + + /** + * Indicates that the native X11 window is an XCB window rather than an XLIB window. + * This is ignored on non-Linux platforms and in builds that support only one X11 API. + */ + static const uint64_t CONFIG_ENABLE_XCB = backend::SWAP_CHAIN_CONFIG_ENABLE_XCB; + + /** + * Indicates that the native window is a CVPixelBufferRef. + * + * This is only supported by the Metal backend. The CVPixelBuffer must be in the + * kCVPixelFormatType_32BGRA format. + * + * It is not necessary to add an additional retain call before passing the pixel buffer to + * Filament. Filament will call CVPixelBufferRetain during Engine::createSwapChain, and + * CVPixelBufferRelease when the swap chain is destroyed. + */ + static const uint64_t CONFIG_APPLE_CVPIXELBUFFER = + backend::SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER; + + void* getNativeWindow() const noexcept; + + /** + * FrameScheduledCallback is a callback function that notifies an application when Filament has + * completed processing a frame and that frame is ready to be scheduled for presentation. + * + * Typically, Filament is responsible for scheduling the frame's presentation to the SwapChain. + * If a SwapChain::FrameScheduledCallback is set, however, the application bares the + * responsibility of scheduling a frame for presentation by calling the backend::PresentCallable + * passed to the callback function. Currently this functionality is only supported by the Metal + * backend. + * + * A FrameScheduledCallback can be set on an individual SwapChain through + * SwapChain::setFrameScheduledCallback. If the callback is set, then the SwapChain will *not* + * automatically schedule itself for presentation. Instead, the application must call the + * PresentCallable passed to the FrameScheduledCallback. + * + * @param callback A callback, or nullptr to unset. + * @param user An optional pointer to user data passed to the callback function. + * + * @remark Only Filament's Metal backend supports PresentCallables and frame callbacks. Other + * backends ignore the callback (which will never be called) and proceed normally. + * + * @remark The SwapChain::FrameScheduledCallback is called on an arbitrary thread. + * + * @see PresentCallable + */ + void setFrameScheduledCallback(FrameScheduledCallback callback, void* user = nullptr); + + /** + * FrameCompletedCallback is a callback function that notifies an application when a frame's + * contents have completed rendering on the GPU. + * + * Use SwapChain::setFrameCompletedCallback to set a callback on an individual SwapChain. Each + * time a frame completes GPU rendering, the callback will be called with optional user data. + * + * The FrameCompletedCallback is guaranteed to be called on the main Filament thread. + * + * @param callback A callback, or nullptr to unset. + * @param user An optional pointer to user data passed to the callback function. + * + * @remark Only Filament's Metal backend supports frame callbacks. Other backends ignore the + * callback (which will never be called) and proceed normally. + */ + void setFrameCompletedCallback(FrameCompletedCallback callback, void* user = nullptr); +}; + +} // namespace filament + +#endif // TNT_FILAMENT_SWAPCHAIN_H diff --git a/ios/include/filament/Texture.h b/ios/include/filament/Texture.h new file mode 100644 index 00000000..71550ba6 --- /dev/null +++ b/ios/include/filament/Texture.h @@ -0,0 +1,517 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! \file + +#ifndef TNT_FILAMENT_TEXTURE_H +#define TNT_FILAMENT_TEXTURE_H + +#include +#include +#include + +#include + +#include + +namespace filament { + +class FTexture; + +class Engine; +class Stream; + +/** + * Texture + * + * The Texture class supports: + * - 2D textures + * - 3D textures + * - Cube maps + * - mip mapping + * + * + * Creation and destruction + * ======================== + * + * A Texture object is created using the Texture::Builder and destroyed by calling + * Engine::destroy(const Texture*). + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} + * filament::Engine* engine = filament::Engine::create(); + * + * filament::Texture* texture = filament::Texture::Builder() + * .width(64) + * .height(64) + * .build(*engine); + * + * engine->destroy(texture); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ +class UTILS_PUBLIC Texture : public FilamentAPI { + struct BuilderDetails; + +public: + static constexpr const size_t BASE_LEVEL = 0; + + using PixelBufferDescriptor = backend::PixelBufferDescriptor; //!< Geometry of a pixel buffer + using Sampler = backend::SamplerType; //!< Type of sampler + using InternalFormat = backend::TextureFormat; //!< Internal texel format + using CubemapFace = backend::TextureCubemapFace; //!< Cube map faces + using Format = backend::PixelDataFormat; //!< Pixel color format + using Type = backend::PixelDataType; //!< Pixel data format + using CompressedType = backend::CompressedPixelDataType; //!< Compressed pixel data format + using FaceOffsets = backend::FaceOffsets; //!< Cube map faces offsets + using Usage = backend::TextureUsage; //!< Usage affects texel layout + using Swizzle = backend::TextureSwizzle; //!< Texture swizzle + + /** @return whether a backend supports a particular format. */ + static bool isTextureFormatSupported(Engine& engine, InternalFormat format) noexcept; + + /** @return whether a backend supports texture swizzling. */ + static bool isTextureSwizzleSupported(Engine& engine) noexcept; + + static size_t computeTextureDataSize(Texture::Format format, Texture::Type type, + size_t stride, size_t height, size_t alignment) noexcept; + + + /** + * Options for environment prefiltering into reflection map + * + * @see generatePrefilterMipmap() + */ + struct PrefilterOptions { + uint16_t sampleCount = 8; //!< sample count used for filtering + bool mirror = true; //!< whether the environment must be mirrored + private: + UTILS_UNUSED uintptr_t reserved[3] = {}; + }; + + + //! Use Builder to construct a Texture object instance + class Builder : public BuilderBase { + friend struct BuilderDetails; + public: + Builder() noexcept; + Builder(Builder const& rhs) noexcept; + Builder(Builder&& rhs) noexcept; + ~Builder() noexcept; + Builder& operator=(Builder const& rhs) noexcept; + Builder& operator=(Builder&& rhs) noexcept; + + /** + * Specifies the width in texels of the texture. Doesn't need to be a power-of-two. + * @param width Width of the texture in texels (default: 1). + * @return This Builder, for chaining calls. + */ + Builder& width(uint32_t width) noexcept; + + /** + * Specifies the height in texels of the texture. Doesn't need to be a power-of-two. + * @param height Height of the texture in texels (default: 1). + * @return This Builder, for chaining calls. + */ + Builder& height(uint32_t height) noexcept; + + /** + * Specifies the depth in texels of the texture. Doesn't need to be a power-of-two. + * The depth controls the number of layers in a 2D array texture. Values greater than 1 + * effectively create a 3D texture. + * @param depth Depth of the texture in texels (default: 1). + * @return This Builder, for chaining calls. + * @attention This Texture instance must use Sampler::SAMPLER_3D or Sampler::SAMPLER_2D_ARRAY or it has no effect. + */ + Builder& depth(uint32_t depth) noexcept; + + /** + * Specifies the numbers of mip map levels. + * This creates a mip-map pyramid. The maximum number of levels a texture can have is + * such that max(width, height, level) / 2^MAX_LEVELS = 1 + * @param levels Number of mipmap levels for this texture. + * @return This Builder, for chaining calls. + */ + Builder& levels(uint8_t levels) noexcept; + + /** + * Specifies the type of sampler to use. + * @param target Sampler type + * @return This Builder, for chaining calls. + * @see Sampler + */ + Builder& sampler(Sampler target) noexcept; + + /** + * Specifies the *internal* format of this texture. + * + * The internal format specifies how texels are stored (which may be different from how + * they're specified in setImage()). InternalFormat specifies both the color components + * and the data type used. + * + * @param format Format of the texture's texel. + * @return This Builder, for chaining calls. + * @see InternalFormat, setImage + */ + Builder& format(InternalFormat format) noexcept; + + /** + * Specifies if the texture will be used as a render target attachment. + * + * If the texture is potentially rendered into, it may require a different memory layout, + * which needs to be known during construction. + * + * @param usage Defaults to Texture::Usage::DEFAULT; c.f. Texture::Usage::COLOR_ATTACHMENT. + * @return This Builder, for chaining calls. + */ + Builder& usage(Usage usage) noexcept; + + /** + * Specifies how a texture's channels map to color components + * + * Texture Swizzle is only supported is isTextureSwizzleSupported() returns true. + * + * @param r texture channel for red component + * @param g texture channel for green component + * @param b texture channel for blue component + * @param a texture channel for alpha component + * @return This Builder, for chaining calls. + * @see Texture::isTextureSwizzleSupported() + */ + Builder& swizzle(Swizzle r, Swizzle g, Swizzle b, Swizzle a) noexcept; + + /** + * Creates the Texture object and returns a pointer to it. + * + * @param engine Reference to the filament::Engine to associate this Texture with. + * + * @return pointer to the newly created object or nullptr if exceptions are disabled and + * an error occurred. + * + * @exception utils::PostConditionPanic if a runtime error occurred, such as running out of + * memory or other resources. + * @exception utils::PreConditionPanic if a parameter to a builder function was invalid. + */ + Texture* build(Engine& engine); + + /* no user serviceable parts below */ + + /** + * Specify a native texture to import as a Filament texture. + * + * The texture id is backend-specific: + * - OpenGL: GLuint texture ID + * - Metal: id + * + * With Metal, the id object should be cast to an intptr_t using + * CFBridgingRetain to transfer ownership to Filament. Filament will release ownership of + * the texture object when the Filament texture is destroyed. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} + * id metalTexture = ... + * filamentTexture->import((intptr_t) CFBridgingRetain(metalTexture)); + * // free to release metalTexture + * + * // after using texture: + * engine->destroy(filamentTexture); // metalTexture is released + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * @warning This method should be used as a last resort. This API is subject to change or + * removal. + * + * @param id a backend specific texture identifier + * + * @return This Builder, for chaining calls. + */ + Builder& import(intptr_t id) noexcept; + + private: + friend class FTexture; + }; + + /** + * Returns the width of a 2D or 3D texture level + * @param level texture level. + * @return Width in texel of the specified \p level, clamped to 1. + * @attention If this texture is using Sampler::SAMPLER_EXTERNAL, the dimension + * of the texture are unknown and this method always returns whatever was set on the Builder. + */ + size_t getWidth(size_t level = BASE_LEVEL) const noexcept; + + /** + * Returns the height of a 2D or 3D texture level + * @param level texture level. + * @return Height in texel of the specified \p level, clamped to 1. + * @attention If this texture is using Sampler::SAMPLER_EXTERNAL, the dimension + * of the texture are unknown and this method always returns whatever was set on the Builder. + */ + size_t getHeight(size_t level = BASE_LEVEL) const noexcept; + + /** + * Returns the depth of a 3D texture level + * @param level texture level. + * @return Depth in texel of the specified \p level, clamped to 1. + * @attention If this texture is using Sampler::SAMPLER_EXTERNAL, the dimension + * of the texture are unknown and this method always returns whatever was set on the Builder. + */ + size_t getDepth(size_t level = BASE_LEVEL) const noexcept; + + /** + * Returns the maximum number of levels this texture can have. + * @return maximum number of levels this texture can have. + * @attention If this texture is using Sampler::SAMPLER_EXTERNAL, the dimension + * of the texture are unknown and this method always returns whatever was set on the Builder. + */ + size_t getLevels() const noexcept; + + /** + * Return this texture Sampler as set by Builder::sampler(). + * @return this texture Sampler as set by Builder::sampler() + */ + Sampler getTarget() const noexcept; + + /** + * Return this texture InternalFormat as set by Builder::format(). + * @return this texture InternalFormat as set by Builder::format(). + */ + InternalFormat getFormat() const noexcept; + + /** + * Specify the image of a 2D texture for a level. + * + * @param engine Engine this texture is associated to. + * @param level Level to set the image for. + * @param buffer Client-side buffer containing the image to set. + * + * @attention \p engine must be the instance passed to Builder::build() + * @attention \p level must be less than getLevels(). + * @attention \p buffer's Texture::Format must match that of getFormat(). + * @attention This Texture instance must use Sampler::SAMPLER_2D or + * Sampler::SAMPLER_EXTERNAL. IF the later is specified + * and external textures are supported by the driver implementation, + * this method will have no effect, otherwise it will behave as if the + * texture was specified with driver::SamplerType::SAMPLER_2D. + * + * @note + * This is equivalent to calling: + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * setImage(engine, level, 0, 0, getWidth(level), getHeight(level), buffer); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * @see Builder::sampler() + */ + void setImage(Engine& engine, size_t level, PixelBufferDescriptor&& buffer) const; + + /** + * Updates a sub-image of a 2D texture for a level. + * + * @param engine Engine this texture is associated to. + * @param level Level to set the image for. + * @param xoffset Left offset of the sub-region to update. + * @param yoffset Bottom offset of the sub-region to update. + * @param width Width of the sub-region to update. + * @param height Height of the sub-region to update. + * @param buffer Client-side buffer containing the image to set. + * + * @attention \p engine must be the instance passed to Builder::build() + * @attention \p level must be less than getLevels(). + * @attention \p buffer's Texture::Format must match that of getFormat(). + * @attention This Texture instance must use Sampler::SAMPLER_2D or + * Sampler::SAMPLER_EXTERNAL. IF the later is specified + * and external textures are supported by the driver implementation, + * this method will have no effect, otherwise it will behave as if the + * texture was specified with Sampler::SAMPLER_2D. + * + * @see Builder::sampler() + */ + void setImage(Engine& engine, size_t level, + uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height, + PixelBufferDescriptor&& buffer) const; + + /** + * Updates a sub-image of a 3D texture or 2D texture array for a level. + * + * @param engine Engine this texture is associated to. + * @param level Level to set the image for. + * @param xoffset Left offset of the sub-region to update. + * @param yoffset Bottom offset of the sub-region to update. + * @param zoffset Depth offset of the sub-region to update. + * @param width Width of the sub-region to update. + * @param height Height of the sub-region to update. + * @param depth Depth of the sub-region to update. + * @param buffer Client-side buffer containing the image to set. + * + * @attention \p engine must be the instance passed to Builder::build() + * @attention \p level must be less than getLevels(). + * @attention \p buffer's Texture::Format must match that of getFormat(). + * @attention This Texture instance must use Sampler::SAMPLER_3D or Sampler::SAMPLER_2D_array. + * + * @see Builder::sampler() + */ + void setImage(Engine& engine, size_t level, + uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, + uint32_t width, uint32_t height, uint32_t depth, + PixelBufferDescriptor&& buffer) const; + + /** + * Specify all six images of a cube map level. + * + * This method follows exactly the OpenGL conventions. + * + * @param engine Engine this texture is associated to. + * @param level Level to set the image for. + * @param buffer Client-side buffer containing the images to set. + * @param faceOffsets Offsets in bytes into \p buffer for all six images. The offsets + * are specified in the following order: +x, -x, +y, -y, +z, -z + * + * @attention \p engine must be the instance passed to Builder::build() + * @attention \p level must be less than getLevels(). + * @attention \p buffer's Texture::Format must match that of getFormat(). + * @attention This Texture instance must use Sampler::SAMPLER_CUBEMAP or it has no effect + * + * @see Texture::CubemapFace, Builder::sampler() + */ + void setImage(Engine& engine, size_t level, + PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets) const; + + + /** + * Specify the external image to associate with this Texture. Typically the external + * image is OS specific, and can be a video or camera frame. + * There are many restrictions when using an external image as a texture, such as: + * - only the level of detail (lod) 0 can be specified + * - only nearest or linear filtering is supported + * - the size and format of the texture is defined by the external image + * - only the CLAMP_TO_EDGE wrap mode is supported + * + * @param engine Engine this texture is associated to. + * @param image An opaque handle to a platform specific image. Supported types are + * eglImageOES on Android and CVPixelBufferRef on iOS. + * + * On iOS the following pixel formats are supported: + * - kCVPixelFormatType_32BGRA + * - kCVPixelFormatType_420YpCbCr8BiPlanarFullRange + * + * @attention \p engine must be the instance passed to Builder::build() + * @attention This Texture instance must use Sampler::SAMPLER_EXTERNAL or it has no effect + * + * @see Builder::sampler() + * + */ + void setExternalImage(Engine& engine, void* image) noexcept; + + /** + * Specify the external image and plane to associate with this Texture. Typically the external + * image is OS specific, and can be a video or camera frame. When using this method, the + * external image must be a planar type (such as a YUV camera frame). The plane parameter + * selects which image plane is bound to this texture. + * + * A single external image can be bound to different Filament textures, with each texture + * associated with a separate plane: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * textureA->setExternalImage(engine, image, 0); + * textureB->setExternalImage(engine, image, 1); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * There are many restrictions when using an external image as a texture, such as: + * - only the level of detail (lod) 0 can be specified + * - only nearest or linear filtering is supported + * - the size and format of the texture is defined by the external image + * - only the CLAMP_TO_EDGE wrap mode is supported + * + * @param engine Engine this texture is associated to. + * @param image An opaque handle to a platform specific image. Supported types are + * eglImageOES on Android and CVPixelBufferRef on iOS. + * @param plane The plane index of the external image to associate with this texture. + * + * This method is only meaningful on iOS with + * kCVPixelFormatType_420YpCbCr8BiPlanarFullRange images. On platforms + * other than iOS, this method is a no-op. + */ + void setExternalImage(Engine& engine, void* image, size_t plane) noexcept; + + /** + * Specify the external stream to associate with this Texture. Typically the external + * stream is OS specific, and can be a video or camera stream. + * There are many restrictions when using an external stream as a texture, such as: + * - only the level of detail (lod) 0 can be specified + * - only nearest or linear filtering is supported + * - the size and format of the texture is defined by the external stream + * + * @param engine Engine this texture is associated to. + * @param stream A Stream object + * + * @attention \p engine must be the instance passed to Builder::build() + * @attention This Texture instance must use Sampler::SAMPLER_EXTERNAL or it has no effect + * + * @see Builder::sampler(), Stream + * + */ + void setExternalStream(Engine& engine, Stream* stream) noexcept; + + /** + * Generates all the mipmap levels automatically. This requires the texture to have a + * color-renderable format. + * + * @param engine Engine this texture is associated to. + * + * @attention \p engine must be the instance passed to Builder::build() + * @attention This Texture instance must NOT use Sampler::SAMPLER_CUBEMAP or it has no effect + */ + void generateMipmaps(Engine& engine) const noexcept; + + /** + * Creates a reflection map from an environment map. + * + * This is a utility function that replaces calls to Texture::setImage(). + * The provided environment map is processed and all mipmap levels are populated. The + * processing is similar to the offline tool `cmgen` as a lower quality setting. + * + * This function is intended to be used when the environment cannot be processed offline, + * for instance if it's generated at runtime. + * + * The source data must obey to some constraints: + * - the data type must be PixelDataFormat::RGB + * - the data format must be one of + * - PixelDataType::FLOAT + * - PixelDataType::HALF + * + * The current texture must be a cubemap + * + * The reflections cubemap's internal format cannot be a compressed format. + * + * The reflections cubemap's dimension must be a power-of-two. + * + * @warning This operation is computationally intensive, especially with large environments and + * is currently synchronous. Expect about 1ms for a 16x16 cubemap. + * + * @param engine Reference to the filament::Engine to associate this IndirectLight with. + * @param buffer Client-side buffer containing the images to set. + * @param faceOffsets Offsets in bytes into \p buffer for all six images. The offsets + * are specified in the following order: +x, -x, +y, -y, +z, -z + * @param options Optional parameter to controlling user-specified quality and options. + * + * @exception utils::PreConditionPanic if the source data constraints are not respected. + * + */ + void generatePrefilterMipmap(Engine& engine, + PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets, + PrefilterOptions const* options = nullptr); +}; + +} // namespace filament + +#endif // TNT_FILAMENT_TEXTURE_H diff --git a/ios/include/filament/TextureSampler.h b/ios/include/filament/TextureSampler.h new file mode 100644 index 00000000..4a4cb9cb --- /dev/null +++ b/ios/include/filament/TextureSampler.h @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! \file + +#ifndef TNT_FILAMENT_TEXTURESAMPLER_H +#define TNT_FILAMENT_TEXTURESAMPLER_H + +#include + +#include + +#include + +namespace filament { + +/** + * TextureSampler defines how a texture is accessed. + */ +class UTILS_PUBLIC TextureSampler { +public: + using WrapMode = backend::SamplerWrapMode; + using MinFilter = backend::SamplerMinFilter; + using MagFilter = backend::SamplerMagFilter; + using CompareMode = backend::SamplerCompareMode; + using CompareFunc = backend::SamplerCompareFunc; + + /** + * Creates a default sampler. + * The default parameters are: + * - filterMag : NEAREST + * - filterMin : NEAREST + * - wrapS : CLAMP_TO_EDGE + * - wrapT : CLAMP_TO_EDGE + * - wrapR : CLAMP_TO_EDGE + * - compareMode : NONE + * - compareFunc : Less or equal + * - no anisotropic filtering + */ + TextureSampler() noexcept = default; + + TextureSampler(const TextureSampler& rhs) noexcept = default; + TextureSampler& operator=(const TextureSampler& rhs) noexcept = default; + + /** + * Creates a TextureSampler with the default parameters but setting the filtering and wrap modes. + * @param minMag filtering for both minification and magnification + * @param str wrapping mode for all texture coordinate axes + */ + explicit TextureSampler(MagFilter minMag, WrapMode str = WrapMode::CLAMP_TO_EDGE) noexcept { + mSamplerParams.filterMin = MinFilter(minMag); + mSamplerParams.filterMag = minMag; + mSamplerParams.wrapS = str; + mSamplerParams.wrapT = str; + mSamplerParams.wrapR = str; + } + + /** + * Creates a TextureSampler with the default parameters but setting the filtering and wrap modes. + * @param min filtering for minification + * @param mag filtering for magnification + * @param str wrapping mode for all texture coordinate axes + */ + TextureSampler(MinFilter min, MagFilter mag, WrapMode str = WrapMode::CLAMP_TO_EDGE) noexcept { + mSamplerParams.filterMin = min; + mSamplerParams.filterMag = mag; + mSamplerParams.wrapS = str; + mSamplerParams.wrapT = str; + mSamplerParams.wrapR = str; + } + + /** + * Creates a TextureSampler with the default parameters but setting the filtering and wrap modes. + * @param min filtering for minification + * @param mag filtering for magnification + * @param s wrap mode for the s (horizontal)texture coordinate + * @param t wrap mode for the t (vertical) texture coordinate + * @param r wrap mode for the r (depth) texture coordinate + */ + TextureSampler(MinFilter min, MagFilter mag, WrapMode s, WrapMode t, WrapMode r) noexcept { + mSamplerParams.filterMin = min; + mSamplerParams.filterMag = mag; + mSamplerParams.wrapS = s; + mSamplerParams.wrapT = t; + mSamplerParams.wrapR = r; + } + + /** + * Creates a TextureSampler with the default parameters but setting the compare mode and function + * @param mode Compare mode + * @param func Compare function + */ + explicit TextureSampler(CompareMode mode, CompareFunc func = CompareFunc::LE) noexcept { + mSamplerParams.compareMode = mode; + mSamplerParams.compareFunc = func; + } + + /** + * Sets the minification filter + * @param v Minification filter + */ + void setMinFilter(MinFilter v) noexcept { + mSamplerParams.filterMin = v; + } + + /** + * Sets the magnification filter + * @param v Magnification filter + */ + void setMagFilter(MagFilter v) noexcept { + mSamplerParams.filterMag = v; + } + + /** + * Sets the wrap mode for the s (horizontal) texture coordinate + * @param v wrap mode + */ + void setWrapModeS(WrapMode v) noexcept { + mSamplerParams.wrapS = v; + } + + /** + * Sets the wrap mode for the t (vertical) texture coordinate + * @param v wrap mode + */ + void setWrapModeT(WrapMode v) noexcept { + mSamplerParams.wrapT = v; + } + + /** + * Sets the wrap mode for the r (depth, for 3D textures) texture coordinate + * @param v wrap mode + */ + void setWrapModeR(WrapMode v) noexcept { + mSamplerParams.wrapR = v; + } + + /** + * This controls anisotropic filtering. + * @param anisotropy Amount of anisotropy, should be a power-of-two. The default is 0. + * The maximum permissible value is 7. + */ + void setAnisotropy(float anisotropy) noexcept { + const int log2 = ilogbf(anisotropy > 0 ? anisotropy : -anisotropy); + mSamplerParams.anisotropyLog2 = uint8_t(log2 < 7 ? log2 : 7); + } + + /** + * Sets the compare mode and function. + * @param mode Compare mode + * @param func Compare function + */ + void setCompareMode(CompareMode mode, CompareFunc func = CompareFunc::LE) noexcept { + mSamplerParams.compareMode = mode; + mSamplerParams.compareFunc = func; + } + + //! returns the minification filter value + MinFilter getMinFilter() const noexcept { return mSamplerParams.filterMin; } + + //! returns the magnification filter value + MagFilter getMagFilter() const noexcept { return mSamplerParams.filterMag; } + + //! returns the s-coordinate wrap mode (horizontal) + WrapMode getWrapModeS() const noexcept { return mSamplerParams.wrapS; } + + //! returns the t-coordinate wrap mode (vertical) + WrapMode getWrapModeT() const noexcept { return mSamplerParams.wrapT; } + + //! returns the r-coordinate wrap mode (depth) + WrapMode getWrapModeR() const noexcept { return mSamplerParams.wrapR; } + + //! returns the anisotropy value + float getAnisotropy() const noexcept { return float(1u << mSamplerParams.anisotropyLog2); } + + //! returns the compare mode + CompareMode getCompareMode() const noexcept { return mSamplerParams.compareMode; } + + //! returns the compare function + CompareFunc getCompareFunc() const noexcept { return mSamplerParams.compareFunc; } + + + // no user-serviceable parts below... + backend::SamplerParams getSamplerParams() const noexcept { return mSamplerParams; } + +private: + backend::SamplerParams mSamplerParams{}; +}; + +} // namespace filament + +#endif // TNT_FILAMENT_TEXTURESAMPLER_H diff --git a/ios/include/filament/ToneMapper.h b/ios/include/filament/ToneMapper.h new file mode 100644 index 00000000..03a8f6b6 --- /dev/null +++ b/ios/include/filament/ToneMapper.h @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_TONE_MAPPER_H +#define TNT_FILAMENT_TONE_MAPPER_H + +#include + +#include + +namespace filament { + +/** + * Interface for tone mapping operators. A tone mapping operator, or tone mapper, + * is responsible for compressing the dynamic range of the rendered scene to a + * dynamic range suitable for display. + * + * In Filament, tone mapping is a color grading step. ToneMapper instances are + * created and passed to the ColorGrading::Builder to produce a 3D LUT that will + * be used during post-processing to prepare the final color buffer for display. + * + * Filament provides several default tone mapping operators that fall into three + * categories: + * + * - Configurable tone mapping operators + * - GenericToneMapper + * - Fixed-aesthetic tone mapping operators + * - ACESToneMapper + * - ACESLegacyToneMapper + * - FilmicToneMapper + * - Debug/validation tone mapping operators + * - LinearToneMapper + * - DisplayRangeToneMapper + * + * You can create custom tone mapping operators by subclassing ToneMapper. + */ +struct UTILS_PUBLIC ToneMapper { + ToneMapper() noexcept; + virtual ~ToneMapper() noexcept; + + /** + * Maps an open domain (or "scene referred" values) color value to display + * domain (or "display referred") color value. Both the input and output + * color values are defined in the Rec.2020 color space, with no transfer + * function applied ("linear Rec.2020"). + * + * @param c Input color to tone map, in the Rec.2020 color space with no + * transfer function applied ("linear") + * + * @return A tone mapped color in the Rec.2020 color space, with no transfer + * function applied ("linear") + */ + virtual math::float3 operator()(math::float3 c) const noexcept = 0; +}; + +/** + * Linear tone mapping operator that returns the input color but clamped to + * the 0..1 range. This operator is mostly useful for debugging. + */ +struct UTILS_PUBLIC LinearToneMapper final : public ToneMapper { + LinearToneMapper() noexcept; + ~LinearToneMapper() noexcept final; + + math::float3 operator()(math::float3 c) const noexcept; +}; + +/** + * ACES tone mapping operator. This operator is an implementation of the + * ACES Reference Rendering Transform (RRT) combined with the Output Device + * Transform (ODT) for sRGB monitors (dim surround, 100 nits). + */ +struct UTILS_PUBLIC ACESToneMapper final : public ToneMapper { + ACESToneMapper() noexcept; + ~ACESToneMapper() noexcept final; + + math::float3 operator()(math::float3 c) const noexcept; +}; + +/** + * ACES tone mapping operator, modified to match the perceived brightness + * of FilmicToneMapper. This operator is the same as ACESToneMapper but + * applies a brightness multiplier of ~1.6 to the input color value to + * target brighter viewing environments. + */ +struct UTILS_PUBLIC ACESLegacyToneMapper final : public ToneMapper { + ACESLegacyToneMapper() noexcept; + ~ACESLegacyToneMapper() noexcept final; + + math::float3 operator()(math::float3 c) const noexcept; +}; + +/** + * "Filmic" tone mapping operator. This tone mapper was designed to + * approximate the aesthetics of the ACES RRT + ODT for Rec.709 + * and historically Filament's default tone mapping operator. It exists + * only for backward compatibility purposes and is not otherwise recommended. + */ +struct UTILS_PUBLIC FilmicToneMapper final : public ToneMapper { + FilmicToneMapper() noexcept; + ~FilmicToneMapper() noexcept final; + + math::float3 operator()(math::float3 x) const noexcept; +}; + +/** + * Generic tone mapping operator that gives control over the tone mapping + * curve. This operator can be used to control the aesthetics of the final + * image. This operator also allows to control the dynamic range of the + * scene referred values. + * + * The tone mapping curve is defined by 5 parameters: + * - contrast: controls the contrast of the curve + * - shoulder: controls the shoulder of the curve, i.e. how quickly scene + * referred values map to output white + * - midGrayIn: sets the input middle gray + * - midGrayOut: sets the output middle gray + * - hdrMax: defines the maximum input value that will be mapped to + * output white + */ +struct UTILS_PUBLIC GenericToneMapper final : public ToneMapper { + /** + * Builds a new generic tone mapper. The default values of the + * constructor parameters approximate an ACES tone mapping curve + * and the maximum input value is set to 10.0. + * + * @param contrast: controls the contrast of the curve, must be > 0.0, values + * in the range 0.5..2.0 are recommended. + * @param shoulder: controls the shoulder of the curve, i.e. how quickly scene + * referred values map to output white, between 0.0 and 1.0. + * @param midGrayIn: sets the input middle gray, between 0.0 and 1.0. + * @param midGrayOut: sets the output middle gray, between 0.0 and 1.0. + * @param hdrMax: defines the maximum input value that will be mapped to + * output white. Must be >= 1.0. + */ + GenericToneMapper( + float contrast = 1.585f, + float shoulder = 0.5f, + float midGrayIn = 0.18f, + float midGrayOut = 0.268f, + float hdrMax = 10.0f + ) noexcept; + ~GenericToneMapper() noexcept final; + + GenericToneMapper(GenericToneMapper const&) = delete; + GenericToneMapper& operator=(GenericToneMapper const&) = delete; + GenericToneMapper(GenericToneMapper&& rhs) noexcept; + GenericToneMapper& operator=(GenericToneMapper& rhs) noexcept; + + math::float3 operator()(math::float3 x) const noexcept; + + /** Returns the contrast of the curve as a strictly positive value. */ + float getContrast() const noexcept; + + /** Returns how fast scene referred values map to output white as a value between 0.0 and 1.0. */ + float getShoulder() const noexcept; + + /** Returns the middle gray point for input values as a value between 0.0 and 1.0. */ + float getMidGrayIn() const noexcept; + + /** Returns the middle gray point for output values as a value between 0.0 and 1.0. */ + float getMidGrayOut() const noexcept; + + /** Returns the maximum input value that will map to output white, as a value >= 1.0. */ + float getHdrMax() const noexcept; + + /** Sets the contrast of the curve, must be > 0.0, values in the range 0.5..2.0 are recommended. */ + void setContrast(float contrast) noexcept; + + /** Sets how quickly scene referred values map to output white, between 0.0 and 1.0. */ + void setShoulder(float shoulder) noexcept; + + /** Sets the input middle gray, between 0.0 and 1.0. */ + void setMidGrayIn(float midGrayIn) noexcept; + + /** Sets the output middle gray, between 0.0 and 1.0. */ + void setMidGrayOut(float midGrayOut) noexcept; + + /** Defines the maximum input value that will be mapped to output white. Must be >= 1.0. */ + void setHdrMax(float hdrMax) noexcept; + +private: + struct Options; + Options* mOptions; +}; + +/** + * A tone mapper that converts the input HDR RGB color into one of 16 debug colors + * that represent the pixel's exposure. When the output is cyan, the input color + * represents middle gray (18% exposure). Every exposure stop above or below middle + * gray causes a color shift. + * + * The relationship between exposures and colors is: + * + * - -5EV black + * - -4EV darkest blue + * - -3EV darker blue + * - -2EV dark blue + * - -1EV blue + * - OEV cyan + * - +1EV dark green + * - +2EV green + * - +3EV yellow + * - +4EV yellow-orange + * - +5EV orange + * - +6EV bright red + * - +7EV red + * - +8EV magenta + * - +9EV purple + * - +10EV white + * + * This tone mapper is useful to validate and tweak scene lighting. + */ +struct UTILS_PUBLIC DisplayRangeToneMapper final : public ToneMapper { + DisplayRangeToneMapper() noexcept; + ~DisplayRangeToneMapper() noexcept; + + math::float3 operator()(math::float3 c) const noexcept; +}; + +} // namespace filament + +#endif // TNT_FILAMENT_TONE_MAPPER_H diff --git a/ios/include/filament/TransformManager.h b/ios/include/filament/TransformManager.h new file mode 100644 index 00000000..f690e766 --- /dev/null +++ b/ios/include/filament/TransformManager.h @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_TRANSFORMMANAGER_H +#define TNT_FILAMENT_TRANSFORMMANAGER_H + +#include + +#include +#include + +#include + +#include + + +namespace utils { +class Entity; +} // namespace utils + +namespace filament { + +class FTransformManager; + +/** + * TransformManager is used to add transform components to entities. + * + * A Transform component gives an entity a position and orientation in space in the coordinate + * space of its parent transform. The TransformManager takes care of computing the world-space + * transform of each component (i.e. its transform relative to the root). + * + * Creation and destruction + * ======================== + * + * A transform component is created using TransformManager::create() and destroyed by calling + * TransformManager::destroy(). + * + * ~~~~~~~~~~~{.cpp} + * filament::Engine* engine = filament::Engine::create(); + * utils::Entity object = utils::EntityManager.get().create(); + * + * auto& tcm = engine->getTransformManager(); + * + * // create the transform component + * tcm.create(object); + * + * // set its transform + * auto i = tcm.getInstance(object); + * tcm.setTransform(i, mat4f::translation({ 0, 0, -1 })); + * + * // destroy the transform component + * tcm.destroy(object); + * ~~~~~~~~~~~ + * + */ +class UTILS_PUBLIC TransformManager : public FilamentAPI { +public: + using Instance = utils::EntityInstance; + + class children_iterator : std::iterator { + friend class FTransformManager; + TransformManager const& mManager; + Instance mInstance; + children_iterator(TransformManager const& mgr, Instance instance) noexcept + : mManager(mgr), mInstance(instance) { } + public: + children_iterator& operator++(); + + children_iterator operator++(int) { // NOLINT + children_iterator ret(*this); + ++(*this); + return ret; + } + + bool operator == (const children_iterator& other) const noexcept { + return mInstance == other.mInstance; + } + + bool operator != (const children_iterator& other) const noexcept { + return mInstance != other.mInstance; + } + + value_type operator*() const { return mInstance; } + }; + + /** + * Returns whether a particular Entity is associated with a component of this TransformManager + * @param e An Entity. + * @return true if this Entity has a component associated with this manager. + */ + bool hasComponent(utils::Entity e) const noexcept; + + /** + * Gets an Instance representing the transform component associated with the given Entity. + * @param e An Entity. + * @return An Instance object, which represents the transform component associated with the Entity e. + * @note Use Instance::isValid() to make sure the component exists. + * @see hasComponent() + */ + Instance getInstance(utils::Entity e) const noexcept; + + /** + * Enables or disable the accurate translation mode. Disabled by default. + * + * When accurate translation mode is active, the translation component of all transforms is + * maintained at double precision. This is only useful if the mat4 version of setTransform() + * is used, as well as getTransformAccurate(). + * + * @param enable true to enable the accurate translation mode, false to disable. + * + * @see isAccurateTranslationsEnabled + * @see create(utils::Entity, Instance, const math::mat4&); + * @see setTransform(Instance, const math::mat4&) + * @see getTransformAccurate + * @see getWorldTransformAccurate + */ + void setAccurateTranslationsEnabled(bool enable) noexcept; + + /** + * Returns whether the high precision translation mode is active. + * @return true if accurate translations mode is active, false otherwise + * @see setAccurateTranslationsEnabled + */ + bool isAccurateTranslationsEnabled() const noexcept; + + /** + * Creates a transform component and associate it with the given entity. + * @param entity An Entity to associate a transform component to. + * @param parent The Instance of the parent transform, or Instance{} if no parent. + * @param localTransform The transform to initialize the transform component with. + * This is always relative to the parent. + * + * If this component already exists on the given entity, it is first destroyed as if + * destroy(utils::Entity e) was called. + * + * @see destroy() + */ + void create(utils::Entity entity, Instance parent, const math::mat4f& localTransform); + void create(utils::Entity entity, Instance parent, const math::mat4& localTransform); //!< \overload + void create(utils::Entity entity, Instance parent = {}); //!< \overload + + /** + * Destroys this component from the given entity, children are orphaned. + * @param e An entity. + * + * @note If this transform had children, these are orphaned, which means their local + * transform becomes a world transform. Usually it's nonsensical. It's recommended to make + * sure that a destroyed transform doesn't have children. + * + * @see create() + */ + void destroy(utils::Entity e) noexcept; + + /** + * Re-parents an entity to a new one. + * @param i The instance of the transform component to re-parent + * @param newParent The instance of the new parent transform + * @attention It is an error to re-parent an entity to a descendant and will cause undefined behaviour. + * @see getInstance() + */ + void setParent(Instance i, Instance newParent) noexcept; + + /** + * Returns the parent of a transform component, or the null entity if it is a root. + * @param i The instance of the transform component to query. + */ + utils::Entity getParent(Instance i) const noexcept; + + /** + * Returns the number of children of a transform component. + * @param i The instance of the transform component to query. + * @return The number of children of the queried component. + */ + size_t getChildCount(Instance i) const noexcept; + + /** + * Gets a list of children for a transform component. + * + * @param i The instance of the transform component to query. + * @param children Pointer to array-of-Entity. The array must have at least "count" elements. + * @param count The maximum number of children to retrieve. + * @return The number of children written to the pointer. + */ + size_t getChildren(Instance i, utils::Entity* children, size_t count) const noexcept; + + /** + * Returns an iterator to the Instance of the first child of the given parent. + * + * @param parent Instance of the parent + * @return A forward iterator pointing to the first child of the given parent. + * + * A child_iterator can only safely be dereferenced if it's different from getChildrenEnd(parent) + */ + children_iterator getChildrenBegin(Instance parent) const noexcept; + + /** + * Returns an undreferencable iterator representing the end of the children list + * + * @param parent Instance of the parent + * @return A forward iterator. + * + * This iterator cannot be dereferenced + */ + children_iterator getChildrenEnd(Instance parent) const noexcept; + + /** + * Sets a local transform of a transform component. + * @param ci The instance of the transform component to set the local transform to. + * @param localTransform The local transform (i.e. relative to the parent). + * @see getTransform() + * @attention This operation can be slow if the hierarchy of transform is too deep, and this + * will be particularly bad when updating a lot of transforms. In that case, + * consider using openLocalTransformTransaction() / commitLocalTransformTransaction(). + */ + void setTransform(Instance ci, const math::mat4f& localTransform) noexcept; + + /** + * Sets a local transform of a transform component and keeps double precision translation. + * All other values of the transform are stored at single precision. + * @param ci The instance of the transform component to set the local transform to. + * @param localTransform The local transform (i.e. relative to the parent). + * @see getTransform() + * @attention This operation can be slow if the hierarchy of transform is too deep, and this + * will be particularly bad when updating a lot of transforms. In that case, + * consider using openLocalTransformTransaction() / commitLocalTransformTransaction(). + */ + void setTransform(Instance ci, const math::mat4& localTransform) noexcept; + + /** + * Returns the local transform of a transform component. + * @param ci The instance of the transform component to query the local transform from. + * @return The local transform of the component (i.e. relative to the parent). This always + * returns the value set by setTransform(). + * @see setTransform() + */ + const math::mat4f& getTransform(Instance ci) const noexcept; + + /** + * Returns the local transform of a transform component. + * @param ci The instance of the transform component to query the local transform from. + * @return The local transform of the component (i.e. relative to the parent). This always + * returns the value set by setTransform(). + * @see setTransform() + */ + const math::mat4 getTransformAccurate(Instance ci) const noexcept; + + /** + * Return the world transform of a transform component. + * @param ci The instance of the transform component to query the world transform from. + * @return The world transform of the component (i.e. relative to the root). This is the + * composition of this component's local transform with its parent's world transform. + * @see setTransform() + */ + const math::mat4f& getWorldTransform(Instance ci) const noexcept; + + /** + * Return the world transform of a transform component. + * @param ci The instance of the transform component to query the world transform from. + * @return The world transform of the component (i.e. relative to the root). This is the + * composition of this component's local transform with its parent's world transform. + * @see setTransform() + */ + const math::mat4 getWorldTransformAccurate(Instance ci) const noexcept; + + /** + * Opens a local transform transaction. During a transaction, getWorldTransform() can + * return an invalid transform until commitLocalTransformTransaction() is called. However, + * setTransform() will perform significantly better and in constant time. + * + * This is useful when updating many transforms and the transform hierarchy is deep (say more + * than 4 or 5 levels). + * + * @note If the local transform transaction is already open, this is a no-op. + * + * @see commitLocalTransformTransaction(), setTransform() + */ + void openLocalTransformTransaction() noexcept; + + /** + * Commits the currently open local transform transaction. When this returns, calls + * to getWorldTransform() will return the proper value. + * + * @attention failing to call this method when done updating the local transform will cause + * a lot of rendering problems. The system never closes the transaction + * automatically. + * + * @note If the local transform transaction is not open, this is a no-op. + * + * @see openLocalTransformTransaction(), setTransform() + */ + void commitLocalTransformTransaction() noexcept; +}; + +} // namespace filament + + +#endif // TNT_TRANSFORMMANAGER_H diff --git a/ios/include/filament/VertexBuffer.h b/ios/include/filament/VertexBuffer.h new file mode 100644 index 00000000..9557a541 --- /dev/null +++ b/ios/include/filament/VertexBuffer.h @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! \file + +#ifndef TNT_FILAMENT_VERTEXBUFFER_H +#define TNT_FILAMENT_VERTEXBUFFER_H + +#include +#include + +#include +#include + +#include + +namespace filament { + +class FVertexBuffer; + +class BufferObject; +class Engine; + +/** + * Holds a set of buffers that define the geometry of a Renderable. + * + * The geometry of the Renderable itself is defined by a set of vertex attributes such as + * position, color, normals, tangents, etc... + * + * There is no need to have a 1-to-1 mapping between attributes and buffer. A buffer can hold the + * data of several attributes -- attributes are then referred as being "interleaved". + * + * The buffers themselves are GPU resources, therefore mutating their data can be relatively slow. + * For this reason, it is best to separate the constant data from the dynamic data into multiple + * buffers. + * + * It is possible, and even encouraged, to use a single vertex buffer for several Renderables. + * + * @see IndexBuffer, RenderableManager + */ +class UTILS_PUBLIC VertexBuffer : public FilamentAPI { + struct BuilderDetails; + +public: + using AttributeType = backend::ElementType; + using BufferDescriptor = backend::BufferDescriptor; + + class Builder : public BuilderBase { + friend struct BuilderDetails; + public: + Builder() noexcept; + Builder(Builder const& rhs) noexcept; + Builder(Builder&& rhs) noexcept; + ~Builder() noexcept; + Builder& operator=(Builder const& rhs) noexcept; + Builder& operator=(Builder&& rhs) noexcept; + + /** + * Defines how many buffers will be created in this vertex buffer set. These buffers are + * later referenced by index from 0 to \p bufferCount - 1. + * + * This call is mandatory. The default is 0. + * + * @param bufferCount Number of buffers in this vertex buffer set. The maximum value is 8. + * @return A reference to this Builder for chaining calls. + */ + Builder& bufferCount(uint8_t bufferCount) noexcept; + + /** + * Size of each buffer in the set in vertex. + * + * @param vertexCount Number of vertices in each buffer in this set. + * @return A reference to this Builder for chaining calls. + */ + Builder& vertexCount(uint32_t vertexCount) noexcept; + + /** + * Allows buffers to be swapped out and shared using BufferObject. + * + * If buffer objects mode is enabled, clients must call setBufferObjectAt rather than + * setBufferAt. This allows sharing of data between VertexBuffer objects, but it may + * slightly increase the memory footprint of Filament's internal bookkeeping. + * + * @param enabled If true, enables buffer object mode. False by default. + */ + Builder& enableBufferObjects(bool enabled = true) noexcept; + + /** + * Sets up an attribute for this vertex buffer set. + * + * Using \p byteOffset and \p byteStride, attributes can be interleaved in the same buffer. + * + * @param attribute The attribute to set up. + * @param bufferIndex The index of the buffer containing the data for this attribute. Must + * be between 0 and bufferCount() - 1. + * @param attributeType The type of the attribute data (e.g. byte, float3, etc...) + * @param byteOffset Offset in *bytes* into the buffer \p bufferIndex + * @param byteStride Stride in *bytes* to the next element of this attribute. When set to + * zero the attribute size, as defined by \p attributeType is used. + * + * @return A reference to this Builder for chaining calls. + * + * @warning VertexAttribute::TANGENTS must be specified as a quaternion and is how normals + * are specified. + * + * @warning Not all backends support 3-component attributes that are not floats. For help + * with conversion, see geometry::Transcoder. + * + * @see VertexAttribute + * + * This is a no-op if the \p attribute is an invalid enum. + * This is a no-op if the \p bufferIndex is out of bounds. + * + */ + Builder& attribute(VertexAttribute attribute, uint8_t bufferIndex, + AttributeType attributeType, + uint32_t byteOffset = 0, uint8_t byteStride = 0) noexcept; + + /** + * Sets whether a given attribute should be normalized. By default attributes are not + * normalized. A normalized attribute is mapped between 0 and 1 in the shader. This applies + * only to integer types. + * + * @param attribute Enum of the attribute to set the normalization flag to. + * @param normalize true to automatically normalize the given attribute. + * @return A reference to this Builder for chaining calls. + * + * This is a no-op if the \p attribute is an invalid enum. + */ + Builder& normalized(VertexAttribute attribute, bool normalize = true) noexcept; + + /** + * Creates the VertexBuffer object and returns a pointer to it. + * + * @param engine Reference to the filament::Engine to associate this VertexBuffer with. + * + * @return pointer to the newly created object or nullptr if exceptions are disabled and + * an error occurred. + * + * @exception utils::PostConditionPanic if a runtime error occurred, such as running out of + * memory or other resources. + * @exception utils::PreConditionPanic if a parameter to a builder function was invalid. + */ + VertexBuffer* build(Engine& engine); + + private: + friend class FVertexBuffer; + }; + + /** + * Returns the vertex count. + * @return Number of vertices in this vertex buffer set. + */ + size_t getVertexCount() const noexcept; + + /** + * Asynchronously copy-initializes the specified buffer from the given buffer data. + * + * Do not use this if you called enableBufferObjects() on the Builder. + * + * @param engine Reference to the filament::Engine to associate this VertexBuffer with. + * @param bufferIndex Index of the buffer to initialize. Must be between 0 + * and Builder::bufferCount() - 1. + * @param buffer A BufferDescriptor representing the data used to initialize the buffer at + * index \p bufferIndex. BufferDescriptor points to raw, untyped data that will + * be copied as-is into the buffer. + * @param byteOffset Offset in *bytes* into the buffer at index \p bufferIndex of this vertex + * buffer set. + */ + void setBufferAt(Engine& engine, uint8_t bufferIndex, BufferDescriptor&& buffer, + uint32_t byteOffset = 0); + + /** + * Swaps in the given buffer object. + * + * To use this, you must first call enableBufferObjects() on the Builder. + * + * @param engine Reference to the filament::Engine to associate this VertexBuffer with. + * @param bufferIndex Index of the buffer to initialize. Must be between 0 + * and Builder::bufferCount() - 1. + * @param bufferObject The handle to the GPU data that will be used in this buffer slot. + */ + void setBufferObjectAt(Engine& engine, uint8_t bufferIndex, BufferObject const* bufferObject); +}; + +} // namespace filament + +#endif // TNT_FILAMENT_VERTEXBUFFER_H diff --git a/ios/include/filament/View.h b/ios/include/filament/View.h new file mode 100644 index 00000000..7b0c1f60 --- /dev/null +++ b/ios/include/filament/View.h @@ -0,0 +1,911 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! \file + +#ifndef TNT_FILAMENT_VIEW_H +#define TNT_FILAMENT_VIEW_H + +#include +#include + +#include + +#include + +#include + +namespace filament { + +class Camera; +class ColorGrading; +class MaterialInstance; +class RenderTarget; +class Scene; +class Texture; +class Viewport; + +/** + * A View encompasses all the state needed for rendering a Scene. + * + * Renderer::render() operates on View objects. These View objects specify important parameters + * such as: + * - The Scene + * - The Camera + * - The Viewport + * - Some rendering parameters + * + * \note + * View instances are heavy objects that internally cache a lot of data needed for rendering. + * It is not advised for an application to use many View objects. + * + * For example, in a game, a View could be used for the main scene and another one for the + * game's user interface. More View instances could be used for creating special effects (e.g. + * a View is akin to a rendering pass). + * + * + * @see Renderer, Scene, Camera, RenderTarget + */ +class UTILS_PUBLIC View : public FilamentAPI { +public: + enum class QualityLevel : uint8_t { + LOW, + MEDIUM, + HIGH, + ULTRA + }; + + enum class BlendMode : uint8_t { + OPAQUE, + TRANSLUCENT + }; + + /** + * Dynamic resolution can be used to either reach a desired target frame rate + * by lowering the resolution of a View, or to increase the quality when the + * rendering is faster than the target frame rate. + * + * This structure can be used to specify the minimum scale factor used when + * lowering the resolution of a View, and the maximum scale factor used when + * increasing the resolution for higher quality rendering. The scale factors + * can be controlled on each X and Y axis independently. By default, all scale + * factors are set to 1.0. + * + * enabled: enable or disables dynamic resolution on a View + * + * homogeneousScaling: by default the system scales the major axis first. Set this to true + * to force homogeneous scaling. + * + * minScale: the minimum scale in X and Y this View should use + * + * maxScale: the maximum scale in X and Y this View should use + * + * quality: upscaling quality. + * LOW: 1 bilinear tap, Medium: 4 bilinear taps, High: 9 bilinear taps (tent) + * + * \note + * Dynamic resolution is only supported on platforms where the time to render + * a frame can be measured accurately. Dynamic resolution is currently only + * supported on Android. + * + * @see Renderer::FrameRateOptions + * + */ + struct DynamicResolutionOptions { + math::float2 minScale = math::float2(0.5f); //!< minimum scale factors in x and y + math::float2 maxScale = math::float2(1.0f); //!< maximum scale factors in x and y + bool enabled = false; //!< enable or disable dynamic resolution + bool homogeneousScaling = false; //!< set to true to force homogeneous scaling + QualityLevel quality = QualityLevel::LOW; //!< Upscaling quality + }; + + /** + * Options to control the bloom effect + * + * enabled: Enable or disable the bloom post-processing effect. Disabled by default. + * + * levels: Number of successive blurs to achieve the blur effect, the minimum is 3 and the + * maximum is 12. This value together with resolution influences the spread of the + * blur effect. This value can be silently reduced to accommodate the original + * image size. + * + * resolution: Resolution of bloom's minor axis. The minimum value is 2^levels and the + * the maximum is lower of the original resolution and 4096. This parameter is + * silently clamped to the minimum and maximum. + * It is highly recommended that this value be smaller than the target resolution + * after dynamic resolution is applied (horizontally and vertically). + * + * strength: how much of the bloom is added to the original image. Between 0 and 1. + * + * blendMode: Whether the bloom effect is purely additive (false) or mixed with the original + * image (true). + * + * anamorphism: Bloom's aspect ratio (x/y), for artistic purposes. + * + * threshold: When enabled, a threshold at 1.0 is applied on the source image, this is + * useful for artistic reasons and is usually needed when a dirt texture is used. + * + * dirt: A dirt/scratch/smudges texture (that can be RGB), which gets added to the + * bloom effect. Smudges are visible where bloom occurs. Threshold must be + * enabled for the dirt effect to work properly. + * + * dirtStrength: Strength of the dirt texture. + */ + struct BloomOptions { + enum class BlendMode : uint8_t { + ADD, //!< Bloom is modulated by the strength parameter and added to the scene + INTERPOLATE //!< Bloom is interpolated with the scene using the strength parameter + }; + Texture* dirt = nullptr; //!< user provided dirt texture + float dirtStrength = 0.2f; //!< strength of the dirt texture + float strength = 0.10f; //!< bloom's strength between 0.0 and 1.0 + uint32_t resolution = 360; //!< resolution of vertical axis (2^levels to 2048) + float anamorphism = 1.0f; //!< bloom x/y aspect-ratio (1/32 to 32) + uint8_t levels = 6; //!< number of blur levels (3 to 11) + BlendMode blendMode = BlendMode::ADD; //!< how the bloom effect is applied + bool threshold = true; //!< whether to threshold the source + bool enabled = false; //!< enable or disable bloom + float highlight = 1000.0f; //!< limit highlights to this value before bloom [10, +inf] + + bool lensFlare = false; //!< enable screen-space lens flare + bool starburst = true; //!< enable starburst effect on lens flare + float chromaticAberration = 0.005f; //!< amount of chromatic aberration + uint8_t ghostCount = 4; //!< number of flare "ghosts" + float ghostSpacing = 0.6f; //!< spacing of the ghost in screen units [0, 1[ + float ghostThreshold = 10.0f; //!< hdr threshold for the ghosts + float haloThickness = 0.1f; //!< thickness of halo in vertical screen units, 0 to disable + float haloRadius = 0.4f; //!< radius of halo in vertical screen units [0, 0.5] + float haloThreshold = 10.0f; //!< hdr threshold for the halo + }; + + /** + * Options to control fog in the scene + */ + struct FogOptions { + float distance = 0.0f; //!< distance in world units from the camera where the fog starts ( >= 0.0 ) + float maximumOpacity = 1.0f; //!< fog's maximum opacity between 0 and 1 + float height = 0.0f; //!< fog's floor in world units + float heightFalloff = 1.0f; //!< how fast fog dissipates with altitude + LinearColor color{0.5f}; //!< fog's color (linear), see fogColorFromIbl + float density = 0.1f; //!< fog's density at altitude given by 'height' + float inScatteringStart = 0.0f; //!< distance in world units from the camera where in-scattering starts + float inScatteringSize = -1.0f; //!< size of in-scattering (>0 to activate). Good values are >> 1 (e.g. ~10 - 100). + bool fogColorFromIbl = false; //!< Fog color will be modulated by the IBL color in the view direction. + bool enabled = false; //!< enable or disable fog + }; + + /** + * Options to control Depth of Field (DoF) effect in the scene. + * + * cocScale can be used to set the depth of field blur independently from the camera + * aperture, e.g. for artistic reasons. This can be achieved by setting: + * cocScale = cameraAperture / desiredDoFAperture + * + * @see Camera + */ + struct DepthOfFieldOptions { + enum class Filter : uint8_t { + NONE = 0, + MEDIAN = 2 + }; + float cocScale = 1.0f; //!< circle of confusion scale factor (amount of blur) + float maxApertureDiameter = 0.01f; //!< maximum aperture diameter in meters (zero to disable rotation) + bool enabled = false; //!< enable or disable depth of field effect + Filter filter = Filter::MEDIAN; //!< filter to use for filling gaps in the kernel + bool nativeResolution = false; //!< perform DoF processing at native resolution + /** + * Number of of rings used by the gather kernels. The number of rings affects quality + * and performance. The actual number of sample per pixel is defined + * as (ringCount * 2 - 1)^2. Here are a few commonly used values: + * 3 rings : 25 ( 5x 5 grid) + * 4 rings : 49 ( 7x 7 grid) + * 5 rings : 81 ( 9x 9 grid) + * 17 rings : 1089 (33x33 grid) + * + * With a maximum circle-of-confusion of 32, it is never necessary to use more than 17 rings. + * + * Usually all three settings below are set to the same value, however, it is often + * acceptable to use a lower ring count for the "fast tiles", which improves performance. + * Fast tiles are regions of the screen where every pixels have a similar + * circle-of-confusion radius. + * + * A value of 0 means default, which is 5 on desktop and 3 on mobile. + * + * @{ + */ + uint8_t foregroundRingCount = 0; //!< number of kernel rings for foreground tiles + uint8_t backgroundRingCount = 0; //!< number of kernel rings for background tiles + uint8_t fastGatherRingCount = 0; //!< number of kernel rings for fast tiles + /** @}*/ + + /** + * maximum circle-of-confusion in pixels for the foreground, must be in [0, 32] range. + * A value of 0 means default, which is 32 on desktop and 24 on mobile. + */ + uint16_t maxForegroundCOC = 0; + + /** + * maximum circle-of-confusion in pixels for the background, must be in [0, 32] range. + * A value of 0 means default, which is 32 on desktop and 24 on mobile. + */ + uint16_t maxBackgroundCOC = 0; + }; + + /** + * Options to control the vignetting effect. + */ + struct VignetteOptions { + float midPoint = 0.5f; //!< high values restrict the vignette closer to the corners, between 0 and 1 + float roundness = 0.5f; //!< controls the shape of the vignette, from a rounded rectangle (0.0), to an oval (0.5), to a circle (1.0) + float feather = 0.5f; //!< softening amount of the vignette effect, between 0 and 1 + LinearColorA color{0.0f, 0.0f, 0.0f, 1.0f}; //!< color of the vignette effect, alpha is currently ignored + bool enabled = false; //!< enables or disables the vignette effect + }; + + /** + * Structure used to set the precision of the color buffer and related quality settings. + * + * @see setRenderQuality, getRenderQuality + */ + struct RenderQuality { + /** + * Sets the quality of the HDR color buffer. + * + * A quality of HIGH or ULTRA means using an RGB16F or RGBA16F color buffer. This means + * colors in the LDR range (0..1) have a 10 bit precision. A quality of LOW or MEDIUM means + * using an R11G11B10F opaque color buffer or an RGBA16F transparent color buffer. With + * R11G11B10F colors in the LDR range have a precision of either 6 bits (red and green + * channels) or 5 bits (blue channel). + */ + QualityLevel hdrColorBuffer = QualityLevel::HIGH; + }; + + /** + * Options for screen space Ambient Occlusion (SSAO) and Screen Space Cone Tracing (SSCT) + * @see setAmbientOcclusionOptions() + */ + struct AmbientOcclusionOptions { + float radius = 0.3f; //!< Ambient Occlusion radius in meters, between 0 and ~10. + float power = 1.0f; //!< Controls ambient occlusion's contrast. Must be positive. + float bias = 0.0005f; //!< Self-occlusion bias in meters. Use to avoid self-occlusion. Between 0 and a few mm. + float resolution = 0.5f;//!< How each dimension of the AO buffer is scaled. Must be either 0.5 or 1.0. + float intensity = 1.0f; //!< Strength of the Ambient Occlusion effect. + float bilateralThreshold = 0.05f; //!< depth distance that constitute an edge for filtering + QualityLevel quality = QualityLevel::LOW; //!< affects # of samples used for AO. + QualityLevel lowPassFilter = QualityLevel::MEDIUM; //!< affects AO smoothness + QualityLevel upsampling = QualityLevel::LOW; //!< affects AO buffer upsampling quality + bool enabled = false; //!< enables or disables screen-space ambient occlusion + bool bentNormals = false; //!< enables bent normals computation from AO, and specular AO + float minHorizonAngleRad = 0.0f; //!< min angle in radian to consider + /** + * Screen Space Cone Tracing (SSCT) options + * Ambient shadows from dominant light + */ + struct Ssct { + float lightConeRad = 1.0f; //!< full cone angle in radian, between 0 and pi/2 + float shadowDistance = 0.3f; //!< how far shadows can be cast + float contactDistanceMax = 1.0f; //!< max distance for contact + float intensity = 0.8f; //!< intensity + math::float3 lightDirection{ 0, -1, 0 }; //!< light direction + float depthBias = 0.01f; //!< depth bias in world units (mitigate self shadowing) + float depthSlopeBias = 0.01f; //!< depth slope bias (mitigate self shadowing) + uint8_t sampleCount = 4; //!< tracing sample count, between 1 and 255 + uint8_t rayCount = 1; //!< # of rays to trace, between 1 and 255 + bool enabled = false; //!< enables or disables SSCT + } ssct; + }; + + /** + * Options for Temporal Anti-aliasing (TAA) + * @see setTemporalAntiAliasingOptions() + */ + struct TemporalAntiAliasingOptions { + float filterWidth = 1.0f; //!< reconstruction filter width typically between 0 (sharper, aliased) and 1 (smoother) + float feedback = 0.04f; //!< history feedback, between 0 (maximum temporal AA) and 1 (no temporal AA). + bool enabled = false; //!< enables or disables temporal anti-aliasing + }; + + /** + * List of available post-processing anti-aliasing techniques. + * @see setAntiAliasing, getAntiAliasing, setSampleCount + */ + enum class AntiAliasing : uint8_t { + NONE = 0, //!< no anti aliasing performed as part of post-processing + FXAA = 1 //!< FXAA is a low-quality but very efficient type of anti-aliasing. (default). + }; + + /** + * List of available post-processing dithering techniques. + */ + enum class Dithering : uint8_t { + NONE = 0, //!< No dithering + TEMPORAL = 1 //!< Temporal dithering (default) + }; + + /** + * List of available shadow mapping techniques. + * @see setShadowType + */ + enum class ShadowType : uint8_t { + PCF, //!< percentage-closer filtered shadows (default) + VSM //!< variance shadows + }; + + /** + * View-level options for VSM Shadowing. + * @see setVsmShadowOptions() + * @warning This API is still experimental and subject to change. + */ + struct VsmShadowOptions { + /** + * Sets the number of anisotropic samples to use when sampling a VSM shadow map. If greater + * than 0, mipmaps will automatically be generated each frame for all lights. + * + * The number of anisotropic samples = 2 ^ vsmAnisotropy. + */ + uint8_t anisotropy = 0; + + /** + * Whether to generate mipmaps for all VSM shadow maps. + */ + bool mipmapping = false; + + /** + * EVSM exponent. + * The maximum value permissible is 5.54 for a shadow map in fp16, or 42.0 for a + * shadow map in fp32. Currently the shadow map bit depth is always fp16. + */ + float exponent = 5.54f; + + /** + * VSM minimum variance scale, must be positive. + */ + float minVarianceScale = 0.5f; + + /** + * VSM light bleeding reduction amount, between 0 and 1. + */ + float lightBleedReduction = 0.15f; + }; + + /** + * Sets the View's name. Only useful for debugging. + * @param name Pointer to the View's name. The string is copied. + */ + void setName(const char* name) noexcept; + + /** + * Returns the View's name + * + * @return a pointer owned by the View instance to the View's name. + * + * @attention Do *not* free the pointer or modify its content. + */ + const char* getName() const noexcept; + + /** + * Set this View instance's Scene. + * + * @param scene Associate the specified Scene to this View. A Scene can be associated to + * several View instances.\n + * \p scene can be nullptr to dissociate the currently set Scene + * from this View.\n + * The View doesn't take ownership of the Scene pointer (which + * acts as a reference). + * + * @note + * There is no reference-counting. + * Make sure to dissociate a Scene from all Views before destroying it. + */ + void setScene(Scene* scene); + + /** + * Returns the Scene currently associated with this View. + * @return A pointer to the Scene associated to this View. nullptr if no Scene is set. + */ + Scene* getScene() noexcept; + + /** + * Returns the Scene currently associated with this View. + * @return A pointer to the Scene associated to this View. nullptr if no Scene is set. + */ + Scene const* getScene() const noexcept { + return const_cast(this)->getScene(); + } + + /** + * Specifies an offscreen render target to render into. + * + * By default, the view's associated render target is nullptr, which corresponds to the + * SwapChain associated with the engine. + * + * A view with a custom render target cannot rely on Renderer::ClearOptions, which only apply + * to the SwapChain. Such view can use a Skybox instead. + * + * @param renderTarget Render target associated with view, or nullptr for the swap chain. + */ + void setRenderTarget(RenderTarget* renderTarget) noexcept; + + /** + * Gets the offscreen render target associated with this view. + * + * Returns nullptr if the render target is the swap chain (which is default). + * + * @see setRenderTarget + */ + RenderTarget* getRenderTarget() const noexcept; + + /** + * Sets the rectangular region to render to. + * + * The viewport specifies where the content of the View (i.e. the Scene) is rendered in + * the render target. The Render target is automatically clipped to the Viewport. + * + * @param viewport The Viewport to render the Scene into. The Viewport is a value-type, it is + * therefore copied. The parameter can be discarded after this call returns. + */ + void setViewport(Viewport const& viewport) noexcept; + + /** + * Returns the rectangular region that gets rendered to. + * @return A constant reference to View's viewport. + */ + Viewport const& getViewport() const noexcept; + + /** + * Sets this View's Camera. + * + * @param camera Associate the specified Camera to this View. A Camera can be associated to + * several View instances.\n + * \p camera can be nullptr to dissociate the currently set Camera from this + * View.\n + * The View doesn't take ownership of the Camera pointer (which + * acts as a reference). + * + * @note + * There is no reference-counting. + * Make sure to dissociate a Camera from all Views before destroying it. + */ + void setCamera(Camera* camera) noexcept; + + /** + * Returns the Camera currently associated with this View. + * @return A reference to the Camera associated to this View. + */ + Camera& getCamera() noexcept; + + /** + * Returns the Camera currently associated with this View. + * @return A reference to the Camera associated to this View. + */ + Camera const& getCamera() const noexcept { + return const_cast(this)->getCamera(); + } + + /** + * Sets the blending mode used to draw the view into the SwapChain. + * + * @param blendMode either BlendMode::OPAQUE or BlendMode::TRANSLUCENT + * @see getBlendMode + */ + void setBlendMode(BlendMode blendMode) noexcept; + + /** + * + * @return blending mode set by setBlendMode + * @see setBlendMode + */ + BlendMode getBlendMode() const noexcept; + + /** + * Sets which layers are visible. + * + * Renderable objects can have one or several layers associated to them. Layers are + * represented with an 8-bits bitmask, where each bit corresponds to a layer. + * @see RenderableManager::setLayerMask(). + * + * This call sets which of those layers are visible. Renderables in invisible layers won't be + * rendered. + * + * @param select a bitmask specifying which layer to set or clear using \p values. + * @param values a bitmask where each bit sets the visibility of the corresponding layer + * (1: visible, 0: invisible), only layers in \p select are affected. + * + * @note By default all layers are visible. + * @note This is a convenient way to quickly show or hide sets of Renderable objects. + */ + void setVisibleLayers(uint8_t select, uint8_t values) noexcept; + + /** + * Get the visible layers. + * + * @see View::setVisibleLayers() + */ + uint8_t getVisibleLayers() const noexcept; + + /** + * Enables or disables shadow mapping. Enabled by default. + * + * @param enabled true enables shadow mapping, false disables it. + * + * @see LightManager::Builder::castShadows(), + * RenderableManager::Builder::receiveShadows(), + * RenderableManager::Builder::castShadows(), + */ + void setShadowingEnabled(bool enabled) noexcept; + + /** + * @return whether shadowing is enabled + */ + bool isShadowingEnabled() const noexcept; + + /** + * Enables or disables screen space refraction. Enabled by default. + * + * @param enabled true enables screen space refraction, false disables it. + */ + void setScreenSpaceRefractionEnabled(bool enabled) noexcept; + + /** + * @return whether screen space refraction is enabled + */ + bool isScreenSpaceRefractionEnabled() const noexcept; + + /** + * Sets how many samples are to be used for MSAA in the post-process stage. + * Default is 1 and disables MSAA. + * + * @param count number of samples to use for multi-sampled anti-aliasing.\n + * 0: treated as 1 + * 1: no anti-aliasing + * n: sample count. Effective sample could be different depending on the + * GPU capabilities. + * + * @note Anti-aliasing can also be performed in the post-processing stage, generally at lower + * cost. See setAntialiasing. + * + * @see setAntialiasing + */ + void setSampleCount(uint8_t count = 1) noexcept; + + /** + * Returns the sample count set by setSampleCount(). Effective sample count could be different. + * A value of 0 or 1 means MSAA is disabled. + * + * @return value set by setSampleCount(). + */ + uint8_t getSampleCount() const noexcept; + + /** + * Enables or disables anti-aliasing in the post-processing stage. Enabled by default. + * MSAA can be enabled in addition, see setSampleCount(). + * + * @param type FXAA for enabling, NONE for disabling anti-aliasing. + * + * @note For MSAA anti-aliasing, see setSamplerCount(). + * + * @see setSampleCount + */ + void setAntiAliasing(AntiAliasing type) noexcept; + + /** + * Queries whether anti-aliasing is enabled during the post-processing stage. To query + * whether MSAA is enabled, see getSampleCount(). + * + * @return The post-processing anti-aliasing method. + */ + AntiAliasing getAntiAliasing() const noexcept; + + /** + * Enables or disable temporal anti-aliasing (TAA). Disabled by default. + * + * @param options temporal anti-aliasing options + */ + void setTemporalAntiAliasingOptions(TemporalAntiAliasingOptions options) noexcept; + + /** + * Returns temporal anti-aliasing options. + * + * @return temporal anti-aliasing options + */ + TemporalAntiAliasingOptions const& getTemporalAntiAliasingOptions() const noexcept; + + /** + * Sets this View's color grading transforms. + * + * @param colorGrading Associate the specified ColorGrading to this View. A ColorGrading can be + * associated to several View instances.\n + * \p colorGrading can be nullptr to dissociate the currently set + * ColorGrading from this View. Doing so will revert to the use of the + * default color grading transforms.\n + * The View doesn't take ownership of the ColorGrading pointer (which + * acts as a reference). + * + * @note + * There is no reference-counting. + * Make sure to dissociate a ColorGrading from all Views before destroying it. + */ + void setColorGrading(ColorGrading* colorGrading) noexcept; + + /** + * Returns the color grading transforms currently associated to this view. + * @return A pointer to the ColorGrading associated to this View. + */ + const ColorGrading* getColorGrading() const noexcept; + + /** + * Sets ambient occlusion options. + * + * @param options Options for ambient occlusion. + */ + void setAmbientOcclusionOptions(AmbientOcclusionOptions const& options) noexcept; + + /** + * Gets the ambient occlusion options. + * + * @return ambient occlusion options currently set. + */ + AmbientOcclusionOptions const& getAmbientOcclusionOptions() const noexcept; + + /** + * Enables or disables bloom in the post-processing stage. Disabled by default. + * + * @param options options + */ + void setBloomOptions(BloomOptions options) noexcept; + + /** + * Queries the bloom options. + * + * @return the current bloom options for this view. + */ + BloomOptions getBloomOptions() const noexcept; + + /** + * Enables or disables fog. Disabled by default. + * + * @param options options + */ + void setFogOptions(FogOptions options) noexcept; + + /** + * Queries the fog options. + * + * @return the current fog options for this view. + */ + FogOptions getFogOptions() const noexcept; + + /** + * Enables or disables Depth of Field. Disabled by default. + * + * @param options options + */ + void setDepthOfFieldOptions(DepthOfFieldOptions options) noexcept; + + /** + * Queries the depth of field options. + * + * @return the current depth of field options for this view. + */ + DepthOfFieldOptions getDepthOfFieldOptions() const noexcept; + + /** + * Enables or disables the vignetted effect in the post-processing stage. Disabled by default. + * + * @param options options + */ + void setVignetteOptions(VignetteOptions options) noexcept; + + /** + * Queries the vignette options. + * + * @return the current vignette options for this view. + */ + VignetteOptions getVignetteOptions() const noexcept; + + /** + * Enables or disables dithering in the post-processing stage. Enabled by default. + * + * @param dithering dithering type + */ + void setDithering(Dithering dithering) noexcept; + + /** + * Queries whether dithering is enabled during the post-processing stage. + * + * @return the current dithering type for this view. + */ + Dithering getDithering() const noexcept; + + /** + * Sets the dynamic resolution options for this view. Dynamic resolution options + * controls whether dynamic resolution is enabled, and if it is, how it behaves. + * + * @param options The dynamic resolution options to use on this view + */ + void setDynamicResolutionOptions(DynamicResolutionOptions const& options) noexcept; + + /** + * Returns the dynamic resolution options associated with this view. + * @return value set by setDynamicResolutionOptions(). + */ + DynamicResolutionOptions getDynamicResolutionOptions() const noexcept; + + /** + * Sets the rendering quality for this view. Refer to RenderQuality for more + * information about the different settings available. + * + * @param renderQuality The render quality to use on this view + */ + void setRenderQuality(RenderQuality const& renderQuality) noexcept; + + /** + * Returns the render quality used by this view. + * @return value set by setRenderQuality(). + */ + RenderQuality getRenderQuality() const noexcept; + + /** + * Sets options relative to dynamic lighting for this view. + * + * @param zLightNear Distance from the camera where the lights are expected to shine. + * This parameter can affect performance and is useful because depending + * on the scene, lights that shine close to the camera may not be + * visible -- in this case, using a larger value can improve performance. + * e.g. when standing and looking straight, several meters of the ground + * isn't visible and if lights are expected to shine there, there is no + * point using a short zLightNear. (Default 5m). + * + * @param zLightFar Distance from the camera after which lights are not expected to be visible. + * Similarly to zLightNear, setting this value properly can improve + * performance. (Default 100m). + * + * + * Together zLightNear and zLightFar must be chosen so that the visible influence of lights + * is spread between these two values. + * + */ + void setDynamicLightingOptions(float zLightNear, float zLightFar) noexcept; + + /* + * Set the shadow mapping technique this View uses. + * + * The ShadowType affects all the shadows seen within the View. + * + * ShadowType::VSM imposes a restriction on marking renderables as only shadow receivers (but + * not casters). To ensure correct shadowing with VSM, all shadow participant renderables should + * be marked as both receivers and casters. Objects that are guaranteed to not cast shadows on + * themselves or other objects (such as flat ground planes) can be set to not cast shadows, + * which might improve shadow quality. + * + * @warning This API is still experimental and subject to change. + */ + void setShadowType(ShadowType shadow) noexcept; + + /** + * Sets VSM shadowing options that apply across the entire View. + * + * Additional light-specific VSM options can be set with LightManager::setShadowOptions. + * + * Only applicable when shadow type is set to ShadowType::VSM. + * + * @param options Options for shadowing. + * + * @see setShadowType + * + * @warning This API is still experimental and subject to change. + */ + void setVsmShadowOptions(VsmShadowOptions const& options) noexcept; + + /** + * Returns the VSM shadowing options associated with this View. + * + * @return value set by setVsmShadowOptions(). + */ + VsmShadowOptions getVsmShadowOptions() const noexcept; + + /** + * Enables or disables post processing. Enabled by default. + * + * Post-processing includes: + * - Bloom + * - Tone-mapping & gamma encoding + * - Dithering + * - MSAA + * - FXAA + * - Dynamic scaling + * + * Disabling post-processing forgoes color correctness as well as anti-aliasing and + * should only be used experimentally (e.g., for UI overlays). + * + * @param enabled true enables post processing, false disables it. + * + * @see setBloomOptions, setColorGrading, setAntiAliasing, setDithering, setSampleCount + */ + void setPostProcessingEnabled(bool enabled) noexcept; + + //! Returns true if post-processing is enabled. See setPostProcessingEnabled() for more info. + bool isPostProcessingEnabled() const noexcept; + + /** + * Inverts the winding order of front faces. By default front faces use a counter-clockwise + * winding order. When the winding order is inverted, front faces are faces with a clockwise + * winding order. + * + * Changing the winding order will directly affect the culling mode in materials + * (see Material::getCullingMode()). + * + * Inverting the winding order of front faces is useful when rendering mirrored reflections + * (water, mirror surfaces, front camera in AR, etc.). + * + * @param inverted True to invert front faces, false otherwise. + */ + void setFrontFaceWindingInverted(bool inverted) noexcept; + + /** + * Returns true if the winding order of front faces is inverted. + * See setFrontFaceWindingInverted() for more information. + */ + bool isFrontFaceWindingInverted() const noexcept; + + // for debugging... + + //! debugging: allows to entirely disable frustum culling. (culling enabled by default). + void setFrustumCullingEnabled(bool culling) noexcept; + + //! debugging: returns whether frustum culling is enabled. + bool isFrustumCullingEnabled() const noexcept; + + //! debugging: sets the Camera used for rendering. It may be different from the culling camera. + void setDebugCamera(Camera* camera) noexcept; + + //! debugging: returns a Camera from the point of view of *the* dominant directional light used for shadowing. + Camera const* getDirectionalLightCamera() const noexcept; + + /** + * List of available ambient occlusion techniques + * @deprecated use AmbientOcclusionOptions::enabled instead + */ + enum class UTILS_DEPRECATED AmbientOcclusion : uint8_t { + NONE = 0, //!< No Ambient Occlusion + SSAO = 1 //!< Basic, sampling SSAO + }; + + /** + * Activates or deactivates ambient occlusion. + * @deprecated use setAmbientOcclusionOptions() instead + * @see setAmbientOcclusionOptions + * + * @param ambientOcclusion Type of ambient occlusion to use. + */ + UTILS_DEPRECATED + void setAmbientOcclusion(AmbientOcclusion ambientOcclusion) noexcept; + + /** + * Queries the type of ambient occlusion active for this View. + * @deprecated use getAmbientOcclusionOptions() instead + * @see getAmbientOcclusionOptions + * + * @return ambient occlusion type. + */ + UTILS_DEPRECATED + AmbientOcclusion getAmbientOcclusion() const noexcept; +}; + + +} // namespace filament + +#endif // TNT_FILAMENT_VIEW_H diff --git a/ios/include/filament/Viewport.h b/ios/include/filament/Viewport.h new file mode 100644 index 00000000..7755cd17 --- /dev/null +++ b/ios/include/filament/Viewport.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//! \file + +#ifndef TNT_FILAMENT_VIEWPORT_H +#define TNT_FILAMENT_VIEWPORT_H + +#include + +#include + +#include +#include + +#include +#include + +namespace filament { + +/** + * Viewport describes a view port in pixel coordinates + * + * A view port is represented by its left-bottom coordinate, width and height in pixels. + */ +class UTILS_PUBLIC Viewport : public backend::Viewport { +public: + /** + * Creates a Viewport of zero width and height at the origin. + */ + Viewport() noexcept : backend::Viewport{} {} + + Viewport(const Viewport& viewport) noexcept = default; + Viewport(Viewport&& viewport) noexcept = default; + Viewport& operator=(const Viewport& viewport) noexcept = default; + Viewport& operator=(Viewport&& viewport) noexcept = default; + + /** + * Creates a Viewport from its left-bottom coordinates, width and height in pixels + * + * @param left left coordinate in pixel + * @param bottom bottom coordinate in pixel + * @param width width in pixel + * @param height height in pixel + */ + Viewport(int32_t left, int32_t bottom, uint32_t width, uint32_t height) noexcept + : backend::Viewport{ left, bottom, width, height } { + } + + /** + * Returns whether the area of the view port is null. + * + * @return true if either width or height is 0 pixel. + */ + bool empty() const noexcept { return !width || !height; } + + /** + * Computes a new scaled Viewport + * @param s scaling factor on the x and y axes. + * @return A new scaled Viewport. The coordinates and dimensions of the new Viewport are + * rounded to the nearest integer value. + */ + Viewport scale(math::float2 s) const noexcept; + +private: + + /** + * Compares two Viewports for equality + * @param lhs reference to the left hand side Viewport + * @param rhs reference to the right hand side Viewport + * @return true if \p rhs and \p lhs are identical. + */ + friend bool operator==(Viewport const& lhs, Viewport const& rhs) noexcept { + return (&rhs == &lhs) || + (rhs.left == lhs.left && rhs.bottom == lhs.bottom && + rhs.width == lhs.width && rhs.height == lhs.height); + } + + /** + * Compares two Viewports for inequality + * @param lhs reference to the left hand side Viewport + * @param rhs reference to the right hand side Viewport + * @return true if \p rhs and \p lhs are different. + */ + friend bool operator!=(Viewport const& lhs, Viewport const& rhs) noexcept { + return !(rhs == lhs); + } +}; + +} // namespace filament + +#endif // TNT_FILAMENT_VIEWPORT_H diff --git a/ios/include/filameshio/MeshReader.h b/ios/include/filameshio/MeshReader.h new file mode 100644 index 00000000..f9da44bd --- /dev/null +++ b/ios/include/filameshio/MeshReader.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_FILAMESHIO_MESHREADER_H +#define TNT_FILAMENT_FILAMESHIO_MESHREADER_H + +#include +#include +#include + +namespace filament { + class Engine; + class VertexBuffer; + class IndexBuffer; + class MaterialInstance; +} + +namespace utils { + class Path; +} + +namespace filamesh { + + +/** + * This API can be used to read meshes stored in the "filamesh" format produced + * by the command line tool of the same name. This file format is documented in + * "docs/filamesh.md" in the Filament distribution. + */ +class UTILS_PUBLIC MeshReader { +public: + using Callback = void(*)(void* buffer, size_t size, void* user); + + // Class to track material instances + class MaterialRegistry { + public: + MaterialRegistry(); + MaterialRegistry(const MaterialRegistry& rhs); + MaterialRegistry& operator=(const MaterialRegistry& rhs); + ~MaterialRegistry(); + MaterialRegistry(MaterialRegistry&&); + MaterialRegistry& operator=(MaterialRegistry&&); + + filament::MaterialInstance* getMaterialInstance(const utils::CString& name); + + void registerMaterialInstance(const utils::CString& name, + filament::MaterialInstance* materialInstance); + + void unregisterMaterialInstance(const utils::CString& name); + + void unregisterAll(); + + std::size_t numRegistered() const noexcept; + + void getRegisteredMaterials(filament::MaterialInstance** materialList, + utils::CString* materialNameList) const; + + void getRegisteredMaterials(filament::MaterialInstance** materialList) const; + + void getRegisteredMaterialNames(utils::CString* materialNameList) const; + + private: + struct MaterialRegistryImpl; + MaterialRegistryImpl* mImpl; + }; + + struct Mesh { + utils::Entity renderable; + filament::VertexBuffer* vertexBuffer = nullptr; + filament::IndexBuffer* indexBuffer = nullptr; + }; + + /** + * Loads a filamesh renderable from the specified file. The material registry + * can be used to provide named materials. If a material found in the filamesh + * file cannot be matched to a material in the registry, a default material is + * used instead. The default material can be overridden by adding a material + * named "DefaultMaterial" to the registry. + */ + static Mesh loadMeshFromFile(filament::Engine* engine, + const utils::Path& path, + MaterialRegistry& materials); + + /** + * Loads a filamesh renderable from an in-memory buffer. The material registry + * can be used to provide named materials. If a material found in the filamesh + * file cannot be matched to a material in the registry, a default material is + * used instead. The default material can be overridden by adding a material + * named "DefaultMaterial" to the registry. + */ + static Mesh loadMeshFromBuffer(filament::Engine* engine, + void const* data, Callback destructor, void* user, + MaterialRegistry& materials); + + /** + * Loads a filamesh renderable from an in-memory buffer. The material registry + * can be used to provide named materials. All the primitives of the decoded + * renderable are assigned the specified default material. + */ + static Mesh loadMeshFromBuffer(filament::Engine* engine, + void const* data, Callback destructor, void* user, + filament::MaterialInstance* defaultMaterial); +}; + +} // namespace filamesh + +#endif // TNT_FILAMENT_FILAMESHIO_MESHREADER_H diff --git a/ios/include/geometry/SurfaceOrientation.h b/ios/include/geometry/SurfaceOrientation.h new file mode 100644 index 00000000..e9ad75bb --- /dev/null +++ b/ios/include/geometry/SurfaceOrientation.h @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_GEOMETRY_SURFACEORIENTATION_H +#define TNT_GEOMETRY_SURFACEORIENTATION_H + +#include +#include +#include + +#include + +namespace filament { + +/** + * Mesh-related utilities. + */ +namespace geometry { + +struct OrientationBuilderImpl; +struct OrientationImpl; + +/** + * The surface orientation helper can be used to populate Filament-style TANGENTS buffers. + */ +class UTILS_PUBLIC SurfaceOrientation { +public: + + /** + * The Builder is used to construct an immutable surface orientation helper. + * + * Clients provide pointers into their own data, which is synchronously consumed during build(). + * At a minimum, clients must supply a vertex count. They can supply data in any of the + * following combinations: + * + * 1. normals only ........................... not recommended, selects arbitrary orientation + * 2. normals + tangents ..................... sign of W determines bitangent orientation + * 3. normals + uvs + positions + indices .... selects Lengyel’s Method + * 4. positions + indices .................... generates normals for flat shading only + * + * Additionally, the client-side data has the following type constraints: + * + * - Normals must be float3 + * - Tangents must be float4 + * - UVs must be float2 + * - Positions must be float3 + * - Triangles must be uint3 or ushort3 + * + * Currently, mikktspace is not supported because it requires re-indexing the mesh. Instead + * we use the method described by Eric Lengyel in "Foundations of Game Engine Development" + * (Volume 2, Chapter 7). + */ + class Builder { + public: + Builder() noexcept; + ~Builder() noexcept; + Builder(Builder&& that) noexcept; + Builder& operator=(Builder&& that) noexcept; + + /** + * This attribute is required. + */ + Builder& vertexCount(size_t vertexCount) noexcept; + + Builder& normals(const filament::math::float3*, size_t stride = 0) noexcept; + Builder& tangents(const filament::math::float4*, size_t stride = 0) noexcept; + Builder& uvs(const filament::math::float2*, size_t stride = 0) noexcept; + Builder& positions(const filament::math::float3*, size_t stride = 0) noexcept; + + Builder& triangleCount(size_t triangleCount) noexcept; + Builder& triangles(const filament::math::uint3*) noexcept; + Builder& triangles(const filament::math::ushort3*) noexcept; + + /** + * Generates quats or returns null if the submitted data is an incomplete combination. + */ + SurfaceOrientation* build(); + + private: + OrientationBuilderImpl* mImpl; + Builder(const Builder&) = delete; + Builder& operator=(const Builder&) = delete; + }; + + ~SurfaceOrientation() noexcept; + SurfaceOrientation(SurfaceOrientation&& that) noexcept; + SurfaceOrientation& operator=(SurfaceOrientation&& that) noexcept; + + /** + * Returns the vertex count. + */ + size_t getVertexCount() const noexcept; + + /** + * Converts quaternions into the desired output format and writes up to "quatCount" + * to the given output pointer. Normally quatCount should be equal to the vertex count. + * The optional stride is the desired quat-to-quat stride in bytes. + * @{ + */ + void getQuats(filament::math::quatf* out, size_t quatCount, size_t stride = 0) const noexcept; + void getQuats(filament::math::short4* out, size_t quatCount, size_t stride = 0) const noexcept; + void getQuats(filament::math::quath* out, size_t quatCount, size_t stride = 0) const noexcept; + /** + * @} + */ + +private: + SurfaceOrientation(OrientationImpl*) noexcept; + SurfaceOrientation(const SurfaceOrientation&) = delete; + SurfaceOrientation& operator=(const SurfaceOrientation&) = delete; + OrientationImpl* mImpl; + friend struct OrientationBuilderImpl; +}; + +} // namespace geometry +} // namespace filament + +#endif // TNT_GEOMETRY_SURFACEORIENTATION_H diff --git a/ios/include/geometry/Transcoder.h b/ios/include/geometry/Transcoder.h new file mode 100644 index 00000000..0c77c941 --- /dev/null +++ b/ios/include/geometry/Transcoder.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_GEOMETRY_TRANSCODER_H +#define TNT_GEOMETRY_TRANSCODER_H + +#include + +#include +#include + +namespace filament { +namespace geometry { + +enum class ComponentType { + BYTE, //!< If normalization is enabled, this maps from [-127,127] to [-1,+1] + UBYTE, //!< If normalization is enabled, this maps from [0,255] to [0, +1] + SHORT, //!< If normalization is enabled, this maps from [-32767,32767] to [-1,+1] + USHORT, //!< If normalization is enabled, this maps from [0,65535] to [0, +1] + HALF, //!< 1 sign bit, 5 exponent bits, and 5 mantissa bits. +}; + +/** + * Creates a function object that can convert vertex attribute data into tightly packed floats. + * + * This is especially useful for 3-component formats which are not supported by all backends. + * e.g. The Vulkan minspec includes float3 but not short3. + * + * Usage Example: + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * using filament::geometry::Transcoder; + * using filament::geometry::ComponentType; + * + * Transcoder transcode({ + * .componentType = ComponentType::BYTE, + * .normalized = true, + * .componentCount = 3, + * .inputStrideBytes = 0 + * }); + * + * transcode(outputPtr, inputPtr, count); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * The interpretation of signed normalized data is consistent with Vulkan and OpenGL ES 3.0+. + * Note that this slightly differs from earlier versions of OpenGL ES. For example, a signed byte + * value of -127 maps exactly to -1.0f under ES3 and VK rules, but not ES2. + */ +class UTILS_PUBLIC Transcoder { +public: + /** + * Describes the format of all input data that get passed to this transcoder object. + */ + struct Config { + ComponentType componentType; + bool normalized; + uint32_t componentCount; + uint32_t inputStrideBytes = 0; //!< If stride is 0, the transcoder assumes tight packing. + }; + + /** + * Creates an immutable function object with the specified configuration. + * + * The config is not passed by const reference to allow for type inference at the call site. + */ + Transcoder(Config config) noexcept : mConfig(config) {} + + /** + * Converts arbitrary data into tightly packed 32-bit floating point values. + * + * If target is non-null, writes up to "count" items into target and returns the number of bytes + * actually written. + * + * If target is null, returns the number of bytes required. + * + * @param target Client owned area to write into, or null for a size query + * @param source Pointer to the data to read from (does not get retained) + * @param count The maximum number of items to write (i.e. number of float3 values, not bytes) + * @return Number of bytes required to contain "count" items after conversion to packed floats + * + */ + size_t operator()(float* UTILS_RESTRICT target, void const* UTILS_RESTRICT source, + size_t count) const noexcept; + +private: + const Config mConfig; +}; + +} // namespace geometry +} // namespace filament + +#endif // TNT_GEOMETRY_TRANSCODER_H diff --git a/ios/include/gltfio/Animator.h b/ios/include/gltfio/Animator.h new file mode 100644 index 00000000..53695d5c --- /dev/null +++ b/ios/include/gltfio/Animator.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GLTFIO_ANIMATOR_H +#define GLTFIO_ANIMATOR_H + +#include +#include + +namespace gltfio { + +struct FFilamentAsset; +struct FFilamentInstance; +struct AnimatorImpl; + +/** + * \class Animator Animator.h gltfio/Animator.h + * \brief Updates matrices according to glTF \c animation and \c skin definitions. + * + * Animator can be used for two things: + * - Updating matrices in filament::TransformManager components according to glTF \c animation definitions. + * - Updating bone matrices in filament::RenderableManager components according to glTF \c skin definitions. + * + * For a usage example, see the documentation for AssetLoader. + */ +class UTILS_PUBLIC Animator { +public: + /** + * Applies rotation, translation, and scale to entities that have been targeted by the given + * animation definition. Uses filament::TransformManager. + * + * @param animationIndex Zero-based index for the \c animation of interest. + * @param time Elapsed time of interest in seconds. + */ + void applyAnimation(size_t animationIndex, float time) const; + + /** + * Computes root-to-node transforms for all bone nodes, then passes + * the results into filament::RenderableManager::setBones. + * Uses filament::TransformManager and filament::RenderableManager. + * + * NOTE: this operation is independent of \c animation. + */ + void updateBoneMatrices(); + + /** Returns the number of \c animation definitions in the glTF asset. */ + size_t getAnimationCount() const; + + /** Returns the duration of the specified glTF \c animation in seconds. */ + float getAnimationDuration(size_t animationIndex) const; + + /** + * Returns a weak reference to the string name of the specified \c animation, or an + * empty string if none was specified. + */ + const char* getAnimationName(size_t animationIndex) const; + + // For internal use only. + void addInstance(FFilamentInstance* instance); + +private: + + /*! \cond PRIVATE */ + friend struct FFilamentAsset; + friend struct FFilamentInstance; + /*! \endcond */ + + Animator(FFilamentAsset* asset, FFilamentInstance* instance); + ~Animator(); + AnimatorImpl* mImpl; +}; + +} // namespace gltfio + +#endif // GLTFIO_ANIMATOR_H diff --git a/ios/include/gltfio/AssetLoader.h b/ios/include/gltfio/AssetLoader.h new file mode 100644 index 00000000..4fede91f --- /dev/null +++ b/ios/include/gltfio/AssetLoader.h @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GLTFIO_ASSETLOADER_H +#define GLTFIO_ASSETLOADER_H + +#include +#include + +#include +#include +#include + +#include + +namespace utils { + class EntityManager; + class NameComponentManager; +} + +/** + * Loader and pipeline for glTF 2.0 assets. + */ +namespace gltfio { + +/** + * \struct AssetConfiguration AssetLoader.h gltfio/AssetLoader.h + * \brief Construction parameters for AssetLoader. + */ +struct AssetConfiguration { + //! The engine that the loader should pass to builder objects (e.g. + //! filament::VertexBuffer::Builder). + class filament::Engine* engine; + + //! Controls whether the loader uses filamat to generate materials on the fly, or loads a small + //! set of precompiled ubershader materials. Deleting the MaterialProvider is the client's + //! responsibility. See createMaterialGenerator() and createUbershaderLoader(). + MaterialProvider* materials; + + //! Optional manager for associating string names with entities in the transform hierarchy. + utils::NameComponentManager* names = nullptr; + + //! Overrides the factory used for creating entities in the transform hierarchy. If this is not + //! specified, AssetLoader will use the singleton EntityManager associated with the current + //! process. + utils::EntityManager* entities = nullptr; + + //! Optional default node name for anonymous nodes + char* defaultNodeName = nullptr; +}; + +/** + * \class AssetLoader AssetLoader.h gltfio/AssetLoader.h + * \brief Consumes glTF content and produces FilamentAsset objects. + * + * AssetLoader consumes a blob of glTF 2.0 content (either JSON or GLB) and produces a FilamentAsset + * object, which is a bundle of Filament entities, material instances, textures, vertex buffers, + * and index buffers. + * + * Clients must use AssetLoader to create and destroy FilamentAsset objects. + * + * AssetLoader does not fetch external buffer data or create textures on its own. Clients can use + * ResourceLoader for this, which obtains the URI list from the asset. This is demonstrated in the + * code snippet below. + * + * AssetLoader also owns a cache of filament::Material objects that may be re-used across multiple + * loads. + * + * Example usage: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * auto engine = Engine::create(); + * auto materials = createMaterialGenerator(engine); + * auto loader = AssetLoader::create({engine, materials}); + * + * // Parse the glTF content and create Filament entities. + * std::vector content(...); + * FilamentAsset* asset = loader->createAssetFromJson(content.data(), content.size()); + * content.clear(); + * + * // Load buffers and textures from disk. + * ResourceLoader({engine, ".", true}).loadResources(asset); + * + * // Obtain the simple animation interface. + * Animator* animator = asset->getAnimator(); + * + * // Free the glTF hierarchy as it is no longer needed. + * asset->releaseSourceData(); + * + * // Add renderables to the scene. + * scene->addEntities(asset->getEntities(), asset->getEntityCount()); + * + * // Execute the render loop and play the first animation. + * do { + * animator->applyAnimation(0, time); + * animator->updateBoneMatrices(); + * if (renderer->beginFrame(swapChain)) { + * renderer->render(view); + * renderer->endFrame(); + * } + * } while (!quit); + * + * scene->removeEntities(asset->getEntities(), asset->getEntityCount()); + * loader->destroyAsset(asset); + * materials->destroyMaterials(); + * delete materials; + * AssetLoader::destroy(&loader); + * Engine::destroy(&engine); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +class UTILS_PUBLIC AssetLoader { +public: + + /** + * Creates an asset loader for the given configuration, which specifies the Filament engine. + * + * The engine is held weakly, used only for the creation and destruction of Filament objects. + * The optional name component manager can be used to assign names to renderables. + * The material source specifies whether to use filamat to generate materials on the fly, or to + * load a small set of precompiled ubershader materials. + */ + static AssetLoader* create(const AssetConfiguration& config); + + /** + * Frees the loader. + * + * This does not not automatically free the cache of materials, nor + * does it free the entities for created assets (see destroyAsset). + */ + static void destroy(AssetLoader** loader); + + /** + * Takes a pointer to the contents of a JSON-based glTF 2.0 file and returns a bundle + * of Filament objects. Returns null on failure. + */ + FilamentAsset* createAssetFromJson(const uint8_t* bytes, uint32_t nbytes); + + /** + * Takes a pointer to the contents of a GLB glTF 2.0 file and returns a bundle + * of Filament objects. Returns null on failure. + */ + FilamentAsset* createAssetFromBinary(const uint8_t* bytes, uint32_t nbytes); + + /** + * Consumes the contents of a glTF 2.0 file and produces a primary asset with one or more + * instances. The primary asset has ownership over the instances. + * + * The returned instances share their textures, material instances, and vertex buffers with the + * primary asset. However each instance has its own unique set of entities, transform + * components, and renderable components. Instances are freed when the primary asset is freed. + * + * Light components are not instanced, they belong only to the primary asset. + * + * Clients must use ResourceLoader to load resources on the primary asset. + * + * The entity accessor and renderable stack API in the primary asset can be used to control the + * union of all instances. The individual FilamentInstance objects can be used to access each + * instance's partition of entities. Similarly, the Animator in the primary asset controls all + * instances. To animate instances individually, use FilamentInstance::getAnimator(). + * + * @param bytes the contents of a glTF 2.0 file (JSON or GLB) + * @param numBytes the number of bytes in "bytes" + * @param instances destination pointer, to be populated by the requested number of instances + * @param numInstances requested number of instances + * @return the primary asset that has ownership over all instances + */ + FilamentAsset* createInstancedAsset(const uint8_t* bytes, uint32_t numBytes, + FilamentInstance** instances, size_t numInstances); + + /** + * Adds a new instance to an instanced asset. + * + * Use this with caution. It is more efficient to pre-allocate a max number of instances, and + * gradually add them to the scene as needed. Instances can also be "recycled" by removing and + * re-adding them to the scene. + * + * NOTE: destroyInstance() does not exist because gltfio favors flat arrays for storage of + * entity lists and instance lists, which would be slow to shift. We also wish to discourage + * create/destroy churn, as noted above. + * + * This cannot be called after FilamentAsset::releaseSourceData(). + * This cannot be called on a non-instanced asset. + * See also AssetLoader::createInstancedAsset(). + */ + FilamentInstance* createInstance(FilamentAsset* primary); + + /** + * Allows clients to enable diagnostic shading on newly-loaded assets. + */ + void enableDiagnostics(bool enable = true); + + /** + * Destroys the given asset and all of its associated Filament objects. + * + * This destroys entities, components, material instances, vertex buffers, index buffers, + * and textures. This does not necessarily immediately free all source data, since + * texture decoding or GPU uploading might be underway. + */ + void destroyAsset(const FilamentAsset* asset); + + /** + * Gets a weak reference to an array of cached materials, used internally to create material + * instances for assets. + */ + const filament::Material* const* getMaterials() const noexcept; + + /** + * Gets the number of cached materials. + */ + size_t getMaterialsCount() const noexcept; + + utils::NameComponentManager* getNames() const noexcept; + + MaterialProvider* getMaterialProvider() const noexcept; + + /*! \cond PRIVATE */ +protected: + AssetLoader() noexcept = default; + ~AssetLoader() = default; + +public: + AssetLoader(AssetLoader const&) = delete; + AssetLoader(AssetLoader&&) = delete; + AssetLoader& operator=(AssetLoader const&) = delete; + AssetLoader& operator=(AssetLoader&&) = delete; + /*! \endcond */ +}; + +} // namespace gltfio + +#endif // GLTFIO_ASSETLOADER_H diff --git a/ios/include/gltfio/FilamentAsset.h b/ios/include/gltfio/FilamentAsset.h new file mode 100644 index 00000000..87daa3c3 --- /dev/null +++ b/ios/include/gltfio/FilamentAsset.h @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GLTFIO_FILAMENTASSET_H +#define GLTFIO_FILAMENTASSET_H + +#include +#include + +#include +#include + +namespace filament { + class Camera; + class Engine; + class MaterialInstance; +} + +namespace gltfio { + +class Animator; +class FilamentInstance; + +/** + * \class FilamentAsset FilamentAsset.h gltfio/FilamentAsset.h + * \brief Owns a bundle of Filament objects that have been created by AssetLoader. + * + * For usage instructions, see the documentation for AssetLoader. + * + * This class owns a hierarchy of entities that have been loaded from a glTF asset. Every entity has + * a filament::TransformManager component, and some entities also have \c Name, \c Renderable, + * \c Light, or \c Camera components. + * + * In addition to the aforementioned entities, an asset has strong ownership over a list of + * filament::VertexBuffer, filament::IndexBuffer, filament::MaterialInstance, filament::Texture, + * and, optionally, a simple animation engine (gltfio::Animator). + * + * Clients must use ResourceLoader to create filament::Texture objects, compute tangent quaternions, + * and upload data into vertex buffers and index buffers. + * + * \todo Only the default glTF scene is loaded, other glTF scenes are ignored. + */ +class UTILS_PUBLIC FilamentAsset { +public: + + /** + * Gets the list of entities, one for each glTF node. All of these have a Transform component. + * Some of the returned entities may also have a Renderable component and/or a Light component. + */ + const utils::Entity* getEntities() const noexcept; + + /** + * Gets the number of entities returned by getEntities(). + */ + size_t getEntityCount() const noexcept; + + /** + * Gets the list of entities in the scene representing lights. All of these have a Light component. + */ + const utils::Entity* getLightEntities() const noexcept; + + /** + * Gets the number of entities returned by getLightEntities(). + */ + size_t getLightEntityCount() const noexcept; + + /** + * Gets the list of entities in the scene representing cameras. All of these have a \c Camera + * component. + * + * Note about aspect ratios: + * gltfio always uses an aspect ratio of 1.0 when setting the projection matrix for perspective + * cameras. gltfio then sets the camera's scaling matrix with the aspect ratio specified in the + * glTF file (if present). + * + * The camera's scaling matrix allows clients to adjust the aspect ratio independently from the + * camera's projection. + * + * To change the aspect ratio of the glTF camera: + * + * camera->setScaling(double4 {1.0 / newAspectRatio, 1.0, 1.0, 1.0}); + * + * @see filament::Camera::setScaling + */ + const utils::Entity* getCameraEntities() const noexcept; + + /** + * Gets the number of entities returned by getCameraEntities(). + */ + size_t getCameraEntityCount() const noexcept; + + /** + * Gets the transform root for the asset, which has no matching glTF node. + * + * This node exists for convenience, allowing users to transform the entire asset. For instanced + * assets, this is a "super root" where each of its children is a root in a particular instance. + * This allows users to transform all instances en masse if they wish to do so. + */ + utils::Entity getRoot() const noexcept; + + /** + * Pops a ready renderable off the queue, or returns 0 if no renderables have become ready. + * + * NOTE: To determine the progress percentage or completion status, please use + * ResourceLoader#asyncGetLoadProgress. To get the number of ready renderables, + * please use popRenderables(). + * + * This method allows clients to progressively add the asset's renderables to the scene as + * textures gradually become ready through asynchronous loading. For example, on every frame + * progressive applications can do something like this: + * + * while (utils::Entity e = popRenderable()) { scene.addEntity(e); } + * + * \see ResourceLoader#asyncBeginLoad + * \see popRenderables() + */ + utils::Entity popRenderable() noexcept; + + /** + * Pops up to "count" ready renderables off the queue, or returns the available number. + * + * The given pointer should either be null or point to memory that can hold up to count + * entities. If the pointer is null, returns the number of available renderables. Otherwise + * returns the number of entities that have been written. + * + * \see ResourceLoader#asyncBeginLoad + */ + size_t popRenderables(utils::Entity* entities, size_t count) noexcept; + + /** Gets all material instances. These are already bound to renderables. */ + const filament::MaterialInstance* const* getMaterialInstances() const noexcept; + + /** Gets all material instances (non-const). These are already bound to renderables. */ + filament::MaterialInstance* const* getMaterialInstances() noexcept; + + /** Gets the number of materials returned by getMaterialInstances(). */ + size_t getMaterialInstanceCount() const noexcept; + + /** Gets resource URIs for all externally-referenced buffers. */ + const char* const* getResourceUris() const noexcept; + + /** Gets the number of resource URIs returned by getResourceUris(). */ + size_t getResourceUriCount() const noexcept; + + /** Gets the bounding box computed from the supplied min / max values in glTF accessors. */ + filament::Aabb getBoundingBox() const noexcept; + + /** Gets the NameComponentManager label for the given entity, if it exists. */ + const char* getName(utils::Entity) const noexcept; + + /** Returns the first entity with the given name, or 0 if none exist. */ + utils::Entity getFirstEntityByName(const char* name) noexcept; + + /** + * Gets a list of entities with the given name. + * + * @param name Null-terminated string to match. + * @param entities Pointer to an array to populate. + * @param maxCount Maximum number of entities to retrieve. + * + * @return If entities is non-null, the number of entities written to the entity pointer. + * Otherwise this returns the number of entities with the given name. + */ + size_t getEntitiesByName(const char* name, utils::Entity* entities, + size_t maxCount) const noexcept; + + /** + * Gets a list of entities whose names start with the given prefix. + * + * @param prefix Null-terminated prefix string to match. + * @param entities Pointer to an array to populate. + * @param maxCount Maximum number of entities to retrieve. + * + * @return If entities is non-null, the number of entities written to the entity pointer. + * Otherwise this returns the number of entities with the given prefix. + */ + size_t getEntitiesByPrefix(const char* prefix, utils::Entity* entities, + size_t maxCount) const noexcept; + + /** Gets the glTF extras string for a specific node, or for the asset, if it exists. */ + const char* getExtras(utils::Entity entity = {}) const noexcept; + + /** + * Lazily creates the animation engine or returns it from the cache. + * + * The animator is owned by the asset and should not be manually deleted. + * The first time this is called, it must be called before FilamentAsset::releaseSourceData(). + * If the asset is instanced, this returns a "primary" animator that controls all instances. + * To animate each instance individually, use \see FilamentInstance. + */ + Animator* getAnimator() noexcept; + + /** + * Lazily creates a single LINES renderable that draws the transformed bounding-box hierarchy + * for diagnostic purposes. The wireframe is owned by the asset so clients should not delete it. + */ + utils::Entity getWireframe() noexcept; + + /** + * Returns the Filament engine associated with the AssetLoader that created this asset. + */ + filament::Engine* getEngine() const noexcept; + + /** + * Reclaims CPU-side memory for URI strings, binding lists, and raw animation data. + * + * This should only be called after ResourceLoader::loadResources(). + * If using Animator, this should be called after getAnimator(). + * If this is an instanced asset, this prevents creation of new instances. + */ + void releaseSourceData() noexcept; + + /** + * Returns a weak reference to the underlying cgltf hierarchy. This becomes invalid after + * calling releaseSourceData(). + */ + const void* getSourceAsset() noexcept; + + /*! \cond PRIVATE */ + + FilamentInstance** getAssetInstances() noexcept; + size_t getAssetInstanceCount() const noexcept; + +protected: + FilamentAsset() noexcept = default; + ~FilamentAsset() = default; + +public: + FilamentAsset(FilamentAsset const&) = delete; + FilamentAsset(FilamentAsset&&) = delete; + FilamentAsset& operator=(FilamentAsset const&) = delete; + FilamentAsset& operator=(FilamentAsset&&) = delete; + /*! \endcond */ +}; + +} // namespace gltfio + +#endif // GLTFIO_FILAMENTASSET_H diff --git a/ios/include/gltfio/FilamentInstance.h b/ios/include/gltfio/FilamentInstance.h new file mode 100644 index 00000000..007116a7 --- /dev/null +++ b/ios/include/gltfio/FilamentInstance.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GLTFIO_FILAMENTINSTANCE_H +#define GLTFIO_FILAMENTINSTANCE_H + +#include +#include + +namespace gltfio { + +class Animator; +class FilamentAsset; + +/** + * \class FilamentInstance FilamentInstance.h gltfio/FilamentInstance.h + * \brief Provides access to a hierarchy of entities that have been instanced from a glTF asset. + * + * Every entity has a filament::TransformManager component, and some entities also have \c Name or + * \c Renderable components. + * + * \see AssetLoader::createInstancedAsset() + */ +class UTILS_PUBLIC FilamentInstance { +public: + /** + * Gets the owner of this instance. + */ + FilamentAsset* getAsset() const noexcept; + + /** + * Gets the list of entities in this instance, one for each glTF node. All of these have a + * Transform component. Some of the returned entities may also have a Renderable component or + * Name component. + */ + const utils::Entity* getEntities() const noexcept; + + /** + * Gets the number of entities returned by getEntities(). + */ + size_t getEntityCount() const noexcept; + + /** Gets the transform root for the instance, which has no matching glTF node. */ + utils::Entity getRoot() const noexcept; + + /** + * Lazily creates the animation engine for the instance, or returns it from the cache. + * + * Note that an animator can be obtained either from an individual instance, or from the + * originating FilamentAsset. In the latter case, the animation frame is shared amongst all + * instances. If individual control is desired, users must obtain the animator from the + * individual instances. + * + * The animator is owned by the asset and should not be manually deleted. + * The first time this is called, it must be called before FilamentAsset::releaseSourceData(). + */ + Animator* getAnimator() noexcept; +}; + +} // namespace gltfio + +#endif // GLTFIO_FILAMENTINSTANCE_H diff --git a/ios/include/gltfio/Image.h b/ios/include/gltfio/Image.h new file mode 100644 index 00000000..c2bc2023 --- /dev/null +++ b/ios/include/gltfio/Image.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// gltfio supports PNG and JPEG, disable all other formats. +#define STBI_NO_BMP +#define STBI_NO_PSD +#define STBI_NO_TGA +#define STBI_NO_GIF +#define STBI_NO_HDR +#define STBI_NO_PIC +#define STBI_NO_PNM + +// For emscripten and Android builds, we never load from the file +// system, so we-opt out of the stdio functionality in stb. +#if defined(__EMSCRIPTEN__) || defined(ANDROID) +#define STBI_NO_STDIO +#endif + +#include diff --git a/ios/include/gltfio/MaterialProvider.h b/ios/include/gltfio/MaterialProvider.h new file mode 100644 index 00000000..fd3cc224 --- /dev/null +++ b/ios/include/gltfio/MaterialProvider.h @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GLTFIO_MATERIALPROVIDER_H +#define GLTFIO_MATERIALPROVIDER_H + +#include +#include +#include + +#include + +#include +#include + +namespace gltfio { + +enum class AlphaMode : uint8_t { + OPAQUE, + MASK, + BLEND +}; + +/** + * \struct MaterialKey MaterialProvider.h gltfio/MaterialProvider.h + * \brief Small POD structure that specifies the requirements for a glTF material. + * \note This key is processed by MurmurHashFn so please make padding explicit. + */ +struct alignas(4) MaterialKey { + // -- 32 bit boundary -- + bool doubleSided : 1; + bool unlit : 1; + bool hasVertexColors : 1; + bool hasBaseColorTexture : 1; + bool hasNormalTexture : 1; + bool hasOcclusionTexture : 1; + bool hasEmissiveTexture : 1; + bool useSpecularGlossiness : 1; + AlphaMode alphaMode : 4; + bool enableDiagnostics : 4; + union { + struct { + bool hasMetallicRoughnessTexture : 1; + uint8_t metallicRoughnessUV : 7; + }; + struct { + bool hasSpecularGlossinessTexture : 1; + uint8_t specularGlossinessUV : 7; + }; + }; + uint8_t baseColorUV; + // -- 32 bit boundary -- + bool hasClearCoatTexture : 1; + uint8_t clearCoatUV : 7; + bool hasClearCoatRoughnessTexture : 1; + uint8_t clearCoatRoughnessUV : 7; + bool hasClearCoatNormalTexture : 1; + uint8_t clearCoatNormalUV : 7; + bool hasClearCoat : 1; + bool hasTransmission : 1; + bool hasTextureTransforms : 6; + // -- 32 bit boundary -- + uint8_t emissiveUV; + uint8_t aoUV; + uint8_t normalUV; + bool hasTransmissionTexture : 1; + uint8_t transmissionUV : 7; + // -- 32 bit boundary -- + bool hasSheenColorTexture : 1; + uint8_t sheenColorUV : 7; + bool hasSheenRoughnessTexture : 1; + uint8_t sheenRoughnessUV : 7; + bool hasVolumeThicknessTexture : 1; + uint8_t volumeThicknessUV : 7; + bool hasSheen : 1; + bool hasIOR : 1; + bool hasVolume : 1; +}; + +static_assert(sizeof(MaterialKey) == 16, "MaterialKey has unexpected padding."); + +bool operator==(const MaterialKey& k1, const MaterialKey& k2); + +// Define a mapping from a uv set index in the source asset to one of Filament's uv sets. +enum UvSet : uint8_t { UNUSED, UV0, UV1 }; +constexpr int UvMapSize = 8; +using UvMap = std::array; + +inline uint8_t getNumUvSets(const UvMap& uvmap) { + return std::max({ + uvmap[0], uvmap[1], uvmap[2], uvmap[3], + uvmap[4], uvmap[5], uvmap[6], uvmap[7], + }); +}; + +/** + * \class MaterialProvider MaterialProvider.h gltfio/MaterialProvider.h + * \brief Interface to a provider of glTF materials (has two implementations). + * + * - The \c MaterialGenerator implementation generates materials at run time (which can be slow) and + * requires the filamat library, but produces streamlined shaders. See createMaterialGenerator(). + * + * - The \c UbershaderLoader implementation uses a small number of pre-built materials with complex + * fragment shaders, but does not require any run time work or usage of filamat. See + * createUbershaderLoader(). + * + * Both implementations of MaterialProvider maintain a small cache of materials which must be + * explicitly freed using destroyMaterials(). These materials are not freed automatically when the + * MaterialProvider is destroyed, which allows clients to take ownership if desired. + * + */ +class UTILS_PUBLIC MaterialProvider { +public: + virtual ~MaterialProvider() {} + + /** + * Creates or fetches a compiled Filament material, then creates an instance from it. + * + * @param config Specifies requirements; might be mutated due to resource constraints. + * @param uvmap Output argument that gets populated with a small table that maps from a glTF uv + * index to a Filament uv index. + * @param label Optional tag that is not a part of the cache key. + */ + virtual filament::MaterialInstance* createMaterialInstance(MaterialKey* config, UvMap* uvmap, + const char* label = "material") = 0; + + /** + * Gets a weak reference to the array of cached materials. + */ + virtual const filament::Material* const* getMaterials() const noexcept = 0; + + /** + * Gets the number of cached materials. + */ + virtual size_t getMaterialsCount() const noexcept = 0; + + /** + * Destroys all cached materials. + * + * This is not called automatically when MaterialProvider is destroyed, which allows + * clients to take ownership of the cache if desired. + */ + virtual void destroyMaterials() = 0; + + /** + * Returns true if the presence of the given vertex attribute is required. + * + * Some types of providers (e.g. ubershader) require dummy attribute values + * if the glTF model does not provide them. + */ + virtual bool needsDummyData(filament::VertexAttribute attrib) const noexcept = 0; +}; + +void constrainMaterial(MaterialKey* key, UvMap* uvmap); + +void processShaderString(std::string* shader, const UvMap& uvmap, + const MaterialKey& config); + +/** + * Creates a material provider that builds materials on the fly, composing GLSL at run time. + * + * @param optimizeShaders Optimizes shaders, but at significant cost to construction time. + * @return New material provider that can build materials at run time. + * + * Requires \c libfilamat to be linked in. Not available in \c libgltfio_core. + * + * @see createUbershaderLoader + */ +UTILS_PUBLIC +MaterialProvider* createMaterialGenerator(filament::Engine* engine, bool optimizeShaders = false); + +/** + * Creates a material provider that loads a small set of pre-built materials. + * + * @return New material provider that can quickly load a material from a cache. + * + * Requires \c libgltfio_resources to be linked in. + * + * @see createMaterialGenerator + */ +UTILS_PUBLIC +MaterialProvider* createUbershaderLoader(filament::Engine* engine); + +} // namespace gltfio + +#endif // GLTFIO_MATERIALPROVIDER_H diff --git a/ios/include/gltfio/ResourceLoader.h b/ios/include/gltfio/ResourceLoader.h new file mode 100644 index 00000000..b69a16e5 --- /dev/null +++ b/ios/include/gltfio/ResourceLoader.h @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GLTFIO_RESOURCELOADER_H +#define GLTFIO_RESOURCELOADER_H + +#include + +#include + +#include + +namespace filament { + class Engine; +} + +namespace gltfio { + +struct FFilamentAsset; +class AssetPool; + +/** + * \struct ResourceConfiguration ResourceLoader.h gltfio/ResourceLoader.h + * \brief Construction parameters for ResourceLoader. + */ +struct ResourceConfiguration { + //! The engine that the loader should pass to builder objects (e.g. + //! filament::Texture::Builder). + class filament::Engine* engine; + + //! Optional path or URI that points to the base glTF file. This is used solely + //! to resolve relative paths. The string pointer is not retained. + const char* gltfPath; + + //! If true, adjusts skinning weights to sum to 1. Well formed glTF files do not need this, + //! but it is useful for robustness. + bool normalizeSkinningWeights; + + //! If true, computes the bounding boxes of all \c POSITION attibutes. Well formed glTF files + //! do not need this, but it is useful for robustness. + bool recomputeBoundingBoxes; +}; + +/** + * \class ResourceLoader ResourceLoader.h gltfio/ResourceLoader.h + * \brief Prepares and uploads vertex buffers and textures to the GPU. + * + * For a usage example, see the documentation for AssetLoader. + * + * ResourceLoader must be destroyed on the same thread that calls filament::Renderer::render() + * because it listens to filament::backend::BufferDescriptor callbacks in order to determine when to + * free CPU-side data blobs. + * + * \todo If clients persist their ResourceLoader, Filament textures are currently re-created upon + * subsequent re-loads of the same asset. To fix this, we would need to enable shared ownership + * of Texture objects between ResourceLoader and FilamentAsset. + */ +class UTILS_PUBLIC ResourceLoader { +public: + using BufferDescriptor = filament::backend::BufferDescriptor; + + ResourceLoader(const ResourceConfiguration& config); + ~ResourceLoader(); + + /** + * Feeds the binary content of an external resource into the loader's URI cache. + * + * On some platforms, `ResourceLoader` does not know how to download external resources on its + * own (external resources might come from a filesystem, a database, or the internet) so this + * method allows clients to download external resources and push them to the loader. + * + * Every resource should be passed in before calling #loadResources or #asyncBeginLoad. See + * also FilamentAsset#getResourceUris. + * + * When loading GLB files (as opposed to JSON-based glTF files), clients typically do not + * need to call this method. + */ + void addResourceData(const char* uri, BufferDescriptor&& buffer); + + /** + * Checks if the given resource has already been added to the URI cache. + */ + bool hasResourceData(const char* uri) const; + + /** + * Frees memory by evicting the URI cache that was populated via addResourceData. + * + * This can be called only after a model is fully loaded or after loading has been cancelled. + */ + void evictResourceData(); + + /** + * Loads resources for the given asset from the filesystem or data cache and "finalizes" the + * asset by transforming the vertex data format if necessary, decoding image files, supplying + * tangent data, etc. + * + * Returns false if resources have already been loaded, or if one or more resources could not + * be loaded. + * + * Note: this method is synchronous and blocks until all textures have been decoded. + * For an asynchronous alternative, see #asyncBeginLoad. + */ + bool loadResources(FilamentAsset* asset); + + /** + * Starts an asynchronous resource load. + * + * Returns false if the loading process was unable to start. + * + * This is an alternative to #loadResources and requires periodic calls to #asyncUpdateLoad. + * On multi-threaded systems this creates threads for texture decoding. + */ + bool asyncBeginLoad(FilamentAsset* asset); + + /** + * Gets the status of an asynchronous resource load as a percentage in [0,1]. + */ + float asyncGetLoadProgress() const; + + /** + * Updates an asynchronous load by performing any pending work that must take place + * on the main thread. + * + * Clients must periodically call this until #asyncGetLoadProgress returns 100%. + * After progress reaches 100%, calling this is harmless; it just does nothing. + */ + void asyncUpdateLoad(); + + /** + * Cancels pending decoder jobs, frees all CPU-side texel data, and flushes the Engine. + * + * Calling this is only necessary if the asyncBeginLoad API was used + * and cancellation is required before progress reaches 100%. + */ + void asyncCancelLoad(); + +private: + bool loadResources(FFilamentAsset* asset, bool async); + void applySparseData(FFilamentAsset* asset) const; + void normalizeSkinningWeights(FFilamentAsset* asset) const; + void updateBoundingBoxes(FFilamentAsset* asset) const; + AssetPool* mPool; + struct Impl; + Impl* pImpl; +}; + +} // namespace gltfio + +#endif // GLTFIO_RESOURCELOADER_H + diff --git a/ios/include/gltfio/resources/gltfresources.h b/ios/include/gltfio/resources/gltfresources.h new file mode 100644 index 00000000..a820383e --- /dev/null +++ b/ios/include/gltfio/resources/gltfresources.h @@ -0,0 +1,46 @@ +#ifndef GLTFRESOURCES_H_ +#define GLTFRESOURCES_H_ + +#include + +extern "C" { + extern const uint8_t GLTFRESOURCES_PACKAGE[]; + extern int GLTFRESOURCES_LIT_FADE_OFFSET; + extern int GLTFRESOURCES_LIT_FADE_SIZE; + extern int GLTFRESOURCES_LIT_OPAQUE_OFFSET; + extern int GLTFRESOURCES_LIT_OPAQUE_SIZE; + extern int GLTFRESOURCES_LIT_MASKED_OFFSET; + extern int GLTFRESOURCES_LIT_MASKED_SIZE; + extern int GLTFRESOURCES_SPECULARGLOSSINESS_FADE_OFFSET; + extern int GLTFRESOURCES_SPECULARGLOSSINESS_FADE_SIZE; + extern int GLTFRESOURCES_SPECULARGLOSSINESS_OPAQUE_OFFSET; + extern int GLTFRESOURCES_SPECULARGLOSSINESS_OPAQUE_SIZE; + extern int GLTFRESOURCES_SPECULARGLOSSINESS_MASKED_OFFSET; + extern int GLTFRESOURCES_SPECULARGLOSSINESS_MASKED_SIZE; + extern int GLTFRESOURCES_UNLIT_FADE_OFFSET; + extern int GLTFRESOURCES_UNLIT_FADE_SIZE; + extern int GLTFRESOURCES_UNLIT_OPAQUE_OFFSET; + extern int GLTFRESOURCES_UNLIT_OPAQUE_SIZE; + extern int GLTFRESOURCES_UNLIT_MASKED_OFFSET; + extern int GLTFRESOURCES_UNLIT_MASKED_SIZE; + extern int GLTFRESOURCES_LIT_VOLUME_OFFSET; + extern int GLTFRESOURCES_LIT_VOLUME_SIZE; + extern int GLTFRESOURCES_LIT_TRANSMISSION_OFFSET; + extern int GLTFRESOURCES_LIT_TRANSMISSION_SIZE; + extern int GLTFRESOURCES_LIT_SHEEN_OFFSET; + extern int GLTFRESOURCES_LIT_SHEEN_SIZE; +} +#define GLTFRESOURCES_LIT_FADE_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_LIT_FADE_OFFSET) +#define GLTFRESOURCES_LIT_OPAQUE_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_LIT_OPAQUE_OFFSET) +#define GLTFRESOURCES_LIT_MASKED_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_LIT_MASKED_OFFSET) +#define GLTFRESOURCES_SPECULARGLOSSINESS_FADE_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_SPECULARGLOSSINESS_FADE_OFFSET) +#define GLTFRESOURCES_SPECULARGLOSSINESS_OPAQUE_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_SPECULARGLOSSINESS_OPAQUE_OFFSET) +#define GLTFRESOURCES_SPECULARGLOSSINESS_MASKED_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_SPECULARGLOSSINESS_MASKED_OFFSET) +#define GLTFRESOURCES_UNLIT_FADE_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_UNLIT_FADE_OFFSET) +#define GLTFRESOURCES_UNLIT_OPAQUE_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_UNLIT_OPAQUE_OFFSET) +#define GLTFRESOURCES_UNLIT_MASKED_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_UNLIT_MASKED_OFFSET) +#define GLTFRESOURCES_LIT_VOLUME_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_LIT_VOLUME_OFFSET) +#define GLTFRESOURCES_LIT_TRANSMISSION_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_LIT_TRANSMISSION_OFFSET) +#define GLTFRESOURCES_LIT_SHEEN_DATA (GLTFRESOURCES_PACKAGE + GLTFRESOURCES_LIT_SHEEN_OFFSET) + +#endif diff --git a/ios/include/gltfio/resources/gltfresources_lite.h b/ios/include/gltfio/resources/gltfresources_lite.h new file mode 100644 index 00000000..8bef16f4 --- /dev/null +++ b/ios/include/gltfio/resources/gltfresources_lite.h @@ -0,0 +1,16 @@ +#ifndef GLTFRESOURCES_LITE_H_ +#define GLTFRESOURCES_LITE_H_ + +#include + +extern "C" { + extern const uint8_t GLTFRESOURCES_LITE_PACKAGE[]; + extern int GLTFRESOURCES_LITE_LIT_OPAQUE_OFFSET; + extern int GLTFRESOURCES_LITE_LIT_OPAQUE_SIZE; + extern int GLTFRESOURCES_LITE_LIT_FADE_OFFSET; + extern int GLTFRESOURCES_LITE_LIT_FADE_SIZE; +} +#define GLTFRESOURCES_LITE_LIT_OPAQUE_DATA (GLTFRESOURCES_LITE_PACKAGE + GLTFRESOURCES_LITE_LIT_OPAQUE_OFFSET) +#define GLTFRESOURCES_LITE_LIT_FADE_DATA (GLTFRESOURCES_LITE_PACKAGE + GLTFRESOURCES_LITE_LIT_FADE_OFFSET) + +#endif diff --git a/ios/include/ibl/Cubemap.h b/ios/include/ibl/Cubemap.h new file mode 100644 index 00000000..2186bdb0 --- /dev/null +++ b/ios/include/ibl/Cubemap.h @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IBL_CUBEMAP_H +#define IBL_CUBEMAP_H + +#include + +#include + +#include +#include +#include + +#include + +namespace filament { +namespace ibl { + +/** + * Generic cubemap class. It handles writing / reading into the 6 faces of a cubemap. + * + * Seamless trilinear filtering is handled. + * + * This class doesn't own the face data, it's just a "view" on the 6 images. + * + * @see CubemapUtils + * + */ +class UTILS_PUBLIC Cubemap { +public: + + /** + * Initialize the cubemap with a given size, but no face is set and no memory is allocated. + * + * Usually Cubemaps are created using CubemapUtils. + * + * @see CubemapUtils + */ + explicit Cubemap(size_t dim); + + Cubemap(Cubemap&&) = default; + Cubemap& operator=(Cubemap&&) = default; + + ~Cubemap(); + + + enum class Face : uint8_t { + PX = 0, // left +----+ + NX, // right | PY | + PY, // bottom +----+----+----+----+ + NY, // top | NX | PZ | PX | NZ | + PZ, // back +----+----+----+----+ + NZ // front | NY | + // +----+ + }; + + using Texel = filament::math::float3; + + + //! releases all images and reset the cubemap size + void resetDimensions(size_t dim); + + //! assigns an image to a face. + void setImageForFace(Face face, const Image& image); + + //! retrieves the image attached to a face + inline const Image& getImageForFace(Face face) const; + + //! retrieves the image attached to a face + inline Image& getImageForFace(Face face); + + //! computes the center of a pixel at coordinate x, y + static inline filament::math::float2 center(size_t x, size_t y); + + //! computes a direction vector from a face and a location of the center of pixel in an Image + inline filament::math::float3 getDirectionFor(Face face, size_t x, size_t y) const; + + //! computes a direction vector from a face and a location in pixel in an Image + inline filament::math::float3 getDirectionFor(Face face, float x, float y) const; + + //! samples the cubemap at the given direction using nearest neighbor filtering + inline Texel const& sampleAt(const filament::math::float3& direction) const; + + //! samples the cubemap at the given direction using bilinear filtering + inline Texel filterAt(const filament::math::float3& direction) const; + + //! samples an image at the given location in pixel using bilinear filtering + static Texel filterAt(const Image& image, float x, float y); + static Texel filterAtCenter(const Image& image, size_t x, size_t y); + + //! samples two cubemaps in a given direction and lerps the result by a given lerp factor + static Texel trilinearFilterAt(const Cubemap& c0, const Cubemap& c1, float lerp, + const filament::math::float3& direction); + + //! reads a texel at a given address + inline static const Texel& sampleAt(void const* data) { + return *static_cast(data); + } + + //! writes a texel at a given address + inline static void writeAt(void* data, const Texel& texel) { + *static_cast(data) = texel; + } + + //! returns the size of the cubemap in pixels + size_t getDimensions() const; + + /** + * Prepares a cubemap for seamless access to its faces. + * + * @warning All faces of the cubemap must be backed-up by the same Image, and must already + * be spaced by 2 lines/rows. + */ + void makeSeamless(); + + struct Address { + Face face; + float s = 0; + float t = 0; + }; + + //! returns the face and texture coordinates of the given direction + static Address getAddressFor(const filament::math::float3& direction); + +private: + size_t mDimensions = 0; + float mScale = 1; + float mUpperBound = 0; + Image mFaces[6]; +}; + +// ------------------------------------------------------------------------------------------------ + +inline const Image& Cubemap::getImageForFace(Face face) const { + return mFaces[int(face)]; +} + +inline Image& Cubemap::getImageForFace(Face face) { + return mFaces[int(face)]; +} + +inline filament::math::float2 Cubemap::center(size_t x, size_t y) { + return { x + 0.5f, y + 0.5f }; +} + +inline filament::math::float3 Cubemap::getDirectionFor(Face face, size_t x, size_t y) const { + return getDirectionFor(face, x + 0.5f, y + 0.5f); +} + +inline filament::math::float3 Cubemap::getDirectionFor(Face face, float x, float y) const { + // map [0, dim] to [-1,1] with (-1,-1) at bottom left + float cx = (x * mScale) - 1; + float cy = 1 - (y * mScale); + + filament::math::float3 dir; + const float l = std::sqrt(cx * cx + cy * cy + 1); + switch (face) { + case Face::PX: dir = { 1, cy, -cx }; break; + case Face::NX: dir = { -1, cy, cx }; break; + case Face::PY: dir = { cx, 1, -cy }; break; + case Face::NY: dir = { cx, -1, cy }; break; + case Face::PZ: dir = { cx, cy, 1 }; break; + case Face::NZ: dir = { -cx, cy, -1 }; break; + } + return dir * (1 / l); +} + +inline Cubemap::Texel const& Cubemap::sampleAt(const filament::math::float3& direction) const { + Cubemap::Address addr(getAddressFor(direction)); + const size_t x = std::min(size_t(addr.s * mDimensions), mDimensions - 1); + const size_t y = std::min(size_t(addr.t * mDimensions), mDimensions - 1); + return sampleAt(getImageForFace(addr.face).getPixelRef(x, y)); +} + +inline Cubemap::Texel Cubemap::filterAt(const filament::math::float3& direction) const { + Cubemap::Address addr(getAddressFor(direction)); + addr.s = std::min(addr.s * mDimensions, mUpperBound); + addr.t = std::min(addr.t * mDimensions, mUpperBound); + return filterAt(getImageForFace(addr.face), addr.s, addr.t); +} + +} // namespace ibl +} // namespace filament + +#endif /* IBL_CUBEMAP_H */ diff --git a/ios/include/ibl/CubemapIBL.h b/ios/include/ibl/CubemapIBL.h new file mode 100644 index 00000000..925e27c0 --- /dev/null +++ b/ios/include/ibl/CubemapIBL.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IBL_CUBEMAPIBL_H +#define IBL_CUBEMAPIBL_H + +#include + +#include +#include + +#include + +#include +#include + +namespace utils { +class JobSystem; +} // namespace utils + +namespace filament { +namespace ibl { + +class Cubemap; +class Image; + +/** + * Generates cubemaps for the IBL. + */ +class UTILS_PUBLIC CubemapIBL { +public: + typedef void (*Progress)(size_t, float, void*); + + /** + * Computes a roughness LOD using prefiltered importance sampling GGX + * + * @param dst the destination cubemap + * @param levels a list of prefiltered lods of the source environment + * @param linearRoughness roughness + * @param maxNumSamples number of samples for importance sampling + * @param updater a callback for the caller to track progress + */ + static void roughnessFilter( + utils::JobSystem& js, Cubemap& dst, const utils::Slice& levels, + float linearRoughness, size_t maxNumSamples, math::float3 mirror, bool prefilter, + Progress updater = nullptr, void* userdata = nullptr); + + static void roughnessFilter( + utils::JobSystem& js, Cubemap& dst, const std::vector& levels, + float linearRoughness, size_t maxNumSamples, math::float3 mirror, bool prefilter, + Progress updater = nullptr, void* userdata = nullptr); + + //! Computes the "DFG" term of the "split-sum" approximation and stores it in a 2D image + static void DFG(utils::JobSystem& js, Image& dst, bool multiscatter, bool cloth); + + /** + * Computes the diffuse irradiance using prefiltered importance sampling GGX + * + * @note Usually this is done using spherical harmonics instead. + * + * @param dst the destination cubemap + * @param levels a list of prefiltered lods of the source environment + * @param maxNumSamples number of samples for importance sampling + * @param updater a callback for the caller to track progress + * + * @see CubemapSH + */ + static void diffuseIrradiance(utils::JobSystem& js, Cubemap& dst, const std::vector& levels, + size_t maxNumSamples = 1024, Progress updater = nullptr, void* userdata = nullptr); + + // for debugging. ignore. + static void brdf(utils::JobSystem& js, Cubemap& dst, float linearRoughness); +}; + +} // namespace ibl +} // namespace filament + +#endif /* IBL_CUBEMAPIBL_H */ diff --git a/ios/include/ibl/CubemapSH.h b/ios/include/ibl/CubemapSH.h new file mode 100644 index 00000000..b9297c74 --- /dev/null +++ b/ios/include/ibl/CubemapSH.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IBL_CUBEMAPSH_H +#define IBL_CUBEMAPSH_H + + +#include + +#include +#include + +#include +#include + +namespace utils { +class JobSystem; +} // namespace utils + +namespace filament { +namespace ibl { + +class Cubemap; + +/** + * Computes spherical harmonics + */ +class UTILS_PUBLIC CubemapSH { +public: + /** + * Spherical Harmonics decomposition of the given cubemap + * Optionally calculates irradiance by convolving with truncated cos. + */ + static std::unique_ptr computeSH( + utils::JobSystem& js, const Cubemap& cm, size_t numBands, bool irradiance); + + /** + * Render given spherical harmonics into a cubemap + */ + static void renderSH(utils::JobSystem& js, Cubemap& cm, + const std::unique_ptr& sh, size_t numBands); + + static void windowSH(std::unique_ptr& sh, size_t numBands, float cutoff); + + /** + * Compute spherical harmonics of the irradiance of the given cubemap. + * The SH basis are pre-scaled for easier rendering by the shader. The resulting coefficients + * are not spherical harmonics (as they're scalled by various factors). In particular they + * cannot be rendered with renderSH() above. Instead use renderPreScaledSH3Bands() which + * is exactly the code ran by our shader. + */ + static void preprocessSHForShader(std::unique_ptr& sh); + + /** + * Render pre-scaled irrandiance SH + */ + static void renderPreScaledSH3Bands(utils::JobSystem& js, Cubemap& cm, + const std::unique_ptr& sh); + + static constexpr size_t getShIndex(ssize_t m, size_t l) { + return SHindex(m, l); + } + +private: + class float5 { + float v[5]; + public: + float5() = default; + constexpr float5(float a, float b, float c, float d, float e) : v{ a, b, c, d, e } {} + constexpr float operator[](size_t i) const { return v[i]; } + float& operator[](size_t i) { return v[i]; } + }; + + static inline const float5 multiply(const float5 M[5], float5 x) noexcept { + return float5{ + M[0][0] * x[0] + M[1][0] * x[1] + M[2][0] * x[2] + M[3][0] * x[3] + M[4][0] * x[4], + M[0][1] * x[0] + M[1][1] * x[1] + M[2][1] * x[2] + M[3][1] * x[3] + M[4][1] * x[4], + M[0][2] * x[0] + M[1][2] * x[1] + M[2][2] * x[2] + M[3][2] * x[3] + M[4][2] * x[4], + M[0][3] * x[0] + M[1][3] * x[1] + M[2][3] * x[2] + M[3][3] * x[3] + M[4][3] * x[4], + M[0][4] * x[0] + M[1][4] * x[1] + M[2][4] * x[2] + M[3][4] * x[3] + M[4][4] * x[4] + }; + }; + + + static inline constexpr size_t SHindex(ssize_t m, size_t l) { + return l * (l + 1) + m; + } + + static void computeShBasis(float* SHb, size_t numBands, const math::float3& s); + + static float Kml(ssize_t m, size_t l); + + static std::vector Ki(size_t numBands); + + static constexpr float computeTruncatedCosSh(size_t l); + + static float sincWindow(size_t l, float w); + + static math::float3 rotateShericalHarmonicBand1(math::float3 band1, math::mat3f const& M); + + static float5 rotateShericalHarmonicBand2(float5 const& band2, math::mat3f const& M); + + // debugging only... + static float Legendre(ssize_t l, ssize_t m, float x); + static float TSH(int l, int m, const math::float3& d); + static void printShBase(std::ostream& out, int l, int m); +}; + +} // namespace ibl +} // namespace filament + +#endif /* IBL_CUBEMAPSH_H */ diff --git a/ios/include/ibl/CubemapUtils.h b/ios/include/ibl/CubemapUtils.h new file mode 100644 index 00000000..3b4a3154 --- /dev/null +++ b/ios/include/ibl/CubemapUtils.h @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IBL_CUBEMAP_UTILS_H +#define IBL_CUBEMAP_UTILS_H + +#include +#include + +#include + +#include + +namespace utils { +class JobSystem; +} // namespace utils + +namespace filament { +namespace ibl { + +class CubemapIBL; + +/** + * Create and convert Cubemap formats + */ +class UTILS_PUBLIC CubemapUtils { +public: + //! Creates a cubemap object and its backing Image + static Cubemap create(Image& image, size_t dim, bool horizontal = true); + + struct EmptyState { + }; + + template + using ScanlineProc = std::function< + void(STATE& state, size_t y, Cubemap::Face f, Cubemap::Texel* data, size_t width)>; + + template + using ReduceProc = std::function; + + //! process the cubemap using multithreading + template + static void process(Cubemap& cm, + utils::JobSystem& js, + ScanlineProc proc, + ReduceProc reduce = [](STATE&) {}, + const STATE& prototype = STATE()); + + //! process the cubemap + template + static void processSingleThreaded(Cubemap& cm, + utils::JobSystem& js, + ScanlineProc proc, + ReduceProc reduce = [](STATE&) {}, + const STATE& prototype = STATE()); + + //! clamps image to acceptable range + static void clamp(Image& src); + + static void highlight(Image& src); + + //! Downsamples a cubemap by helf in x and y using a box filter + static void downsampleCubemapLevelBoxFilter(utils::JobSystem& js, Cubemap& dst, const Cubemap& src); + + //! Return the name of a face (suitable for a file name) + static const char* getFaceName(Cubemap::Face face); + + //! computes the solid angle of a pixel of a face of a cubemap + static float solidAngle(size_t dim, size_t u, size_t v); + + //! Sets a Cubemap faces from a cross image + static void setAllFacesFromCross(Cubemap& cm, const Image& image); + +private: + + //move these into cmgen? + static void setFaceFromCross(Cubemap& cm, Cubemap::Face face, const Image& image); + static Image createCubemapImage(size_t dim, bool horizontal = true); + +#ifndef FILAMENT_IBL_LITE + +public: + + //! Converts horizontal or vertical cross Image to a Cubemap + static void crossToCubemap(utils::JobSystem& js, Cubemap& dst, const Image& src); + + //! Converts equirectangular Image to a Cubemap + static void equirectangularToCubemap(utils::JobSystem& js, Cubemap& dst, const Image& src); + + //! Converts a Cubemap to an equirectangular Image + static void cubemapToEquirectangular(utils::JobSystem& js, Image& dst, const Cubemap& src); + + //! Converts a Cubemap to an octahedron + static void cubemapToOctahedron(utils::JobSystem& js, Image& dst, const Cubemap& src); + + //! mirror the cubemap in the horizontal direction + static void mirrorCubemap(utils::JobSystem& js, Cubemap& dst, const Cubemap& src); + + //! generates a UV grid in the cubemap -- useful for debugging. + static void generateUVGrid(utils::JobSystem& js, Cubemap& cml, size_t gridFrequencyX, size_t gridFrequencyY); + +#endif + + friend class CubemapIBL; +}; + + +} // namespace ibl +} // namespace filament + +#endif /* IBL_CUBEMAP_UTILS_H */ diff --git a/ios/include/ibl/Image.h b/ios/include/ibl/Image.h new file mode 100644 index 00000000..1ebaa62b --- /dev/null +++ b/ios/include/ibl/Image.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IBL_IMAGE_H +#define IBL_IMAGE_H + +#include +#include +#include + +#include + +#include + +namespace filament { +namespace ibl { + +class UTILS_PUBLIC Image { +public: + Image(); + Image(size_t w, size_t h, size_t stride = 0); + + void reset(); + + void set(Image const& image); + + void subset(Image const& image, size_t x, size_t y, size_t w, size_t h); + + bool isValid() const { return mData != nullptr; } + + size_t getWidth() const { return mWidth; } + + size_t getStride() const { return mBpr / getBytesPerPixel(); } + + size_t getHeight() const { return mHeight; } + + size_t getBytesPerRow() const { return mBpr; } + + size_t getBytesPerPixel() const { return sizeof(math::float3); } + + void* getData() const { return mData; } + + size_t getSize() const { return mBpr * mHeight; } + + void* getPixelRef(size_t x, size_t y) const; + + std::unique_ptr detach() { return std::move(mOwnedData); } + +private: + size_t mBpr = 0; + size_t mWidth = 0; + size_t mHeight = 0; + std::unique_ptr mOwnedData; + void* mData = nullptr; +}; + +inline void* Image::getPixelRef(size_t x, size_t y) const { + return static_cast(mData) + y * getBytesPerRow() + x * getBytesPerPixel(); +} + +} // namespace ibl +} // namespace filament + +#endif /* IBL_IMAGE_H */ diff --git a/ios/include/ibl/utilities.h b/ios/include/ibl/utilities.h new file mode 100644 index 00000000..6d40cc03 --- /dev/null +++ b/ios/include/ibl/utilities.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IBL_UTILITIES_H +#define IBL_UTILITIES_H + +#include + +#include +#include + +namespace filament { +namespace ibl { + +template +static inline constexpr T sq(T x) { + return x * x; +} + +template +static inline constexpr T log4(T x) { + // log2(x)/log2(4) + // log2(x)/2 + return std::log2(x) * T(0.5); +} + +inline bool isPOT(size_t x) { + return !(x & (x - 1)); +} + +inline filament::math::float2 hammersley(uint32_t i, float iN) { + constexpr float tof = 0.5f / 0x80000000U; + uint32_t bits = i; + bits = (bits << 16u) | (bits >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); + return { i * iN, bits * tof }; +} + +} // namespace ibl +} // namespace filament +#endif /* IBL_UTILITIES_H */ diff --git a/ios/include/image/ColorTransform.h b/ios/include/image/ColorTransform.h new file mode 100644 index 00000000..334c21b0 --- /dev/null +++ b/ios/include/image/ColorTransform.h @@ -0,0 +1,381 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IMAGE_COLORTRANSFORM_H_ +#define IMAGE_COLORTRANSFORM_H_ + +#include + +#include + +#include +#include +#include +#include + +#include +#include + +namespace image { + +template +uint32_t linearToRGB_10_11_11_REV(const T& linear) { + using fp11 = filament::math::fp<0, 5, 6>; + using fp10 = filament::math::fp<0, 5, 5>; + // the max value for a RGB_11_11_10 is {65024, 65024, 64512} : (2 - 2^-M) * 2^(E-1) + // we clamp to the min of that + fp11 r = fp11::fromf(std::min(64512.0f, linear[0])); + fp11 g = fp11::fromf(std::min(64512.0f, linear[1])); + fp10 b = fp10::fromf(std::min(64512.0f, linear[2])); + uint32_t ir = r.bits & 0x7FF; + uint32_t ig = g.bits & 0x7FF; + uint32_t ib = b.bits & 0x3FF; + return (ib << 22) | (ig << 11) | ir; +} + +template +inline filament::math::float4 linearToRGBM(const T& linear) { + using filament::math::float4; + + float4 RGBM(linear[0], linear[1], linear[2], 1.0f); + + // Linear to gamma space + RGBM.rgb = sqrt(RGBM.rgb); + // Set the range + RGBM.rgb /= 16.0f; + + float maxComponent = std::max(std::max(RGBM.r, RGBM.g), std::max(RGBM.b, 1e-6f)); + // Don't let M go below 1 in the [0..16] range + RGBM.a = filament::math::clamp(maxComponent, 1.0f / 16.0f, 1.0f); + RGBM.a = std::ceil(RGBM.a * 255.0f) / 255.0f; + + RGBM.rgb = saturate(RGBM.rgb / RGBM.a); + + return RGBM; +} + +template +inline filament::math::float3 RGBMtoLinear(const T& rgbm) { + using filament::math::float3; + + float3 linear(rgbm[0], rgbm[1], rgbm[2]); + linear *= rgbm.a * 16.0f; + // Gamma to linear space + return linear * linear; +} + +template +inline filament::math::float3 linearTosRGB(const T& linear) { + using filament::math::float3; + constexpr float a = 0.055f; + constexpr float a1 = 1.055f; + constexpr float p = 1 / 2.4f; + float3 sRGB; + for (size_t i=0 ; i<3 ; i++) { + if (linear[i] <= 0.0031308f) { + sRGB[i] = linear[i] * 12.92f; + } else { + sRGB[i] = a1 * std::pow(linear[i], p) - a; + } + } + return sRGB; +} + +inline float linearTosRGB(float linear) { + if (linear <= 0.0031308f) { + return linear * 12.92f; + } else { + constexpr float a = 0.055f; + constexpr float a1 = 1.055f; + constexpr float p = 1 / 2.4f; + return a1 * std::pow(linear, p) - a; + } +} + +template +T sRGBToLinear(const T& sRGB); + +template<> +inline filament::math::float3 sRGBToLinear(const filament::math::float3& sRGB) { + using filament::math::float3; + constexpr float a = 0.055f; + constexpr float a1 = 1.055f; + constexpr float p = 2.4f; + float3 linear; + for (size_t i=0 ; i<3 ; i++) { + if (sRGB[i] <= 0.04045f) { + linear[i] = sRGB[i] * (1.0f / 12.92f); + } else { + linear[i] = std::pow((sRGB[i] + a) / a1, p); + } + } + return linear; +} + +template<> +inline filament::math::float4 sRGBToLinear(const filament::math::float4& sRGB) { + using filament::math::float4; + constexpr float a = 0.055f; + constexpr float a1 = 1.055f; + constexpr float p = 2.4f; + float4 linear; + for (size_t i=0 ; i<3 ; i++) { + if (sRGB[i] <= 0.04045f) { + linear[i] = sRGB[i] * (1.0f / 12.92f); + } else { + linear[i] = std::pow((sRGB[i] + a) / a1, p); + } + } + linear[3] = sRGB[3]; + return linear; +} + +template +T linearToSRGB(const T& color); + +template<> +inline filament::math::float3 linearToSRGB(const filament::math::float3& color) { + using filament::math::float3; + float3 sRGBColor{color}; + #pragma nounroll + for (size_t i = 0; i < sRGBColor.size(); i++) { + sRGBColor[i] = (sRGBColor[i] <= 0.0031308f) ? + sRGBColor[i] * 12.92f : (powf(sRGBColor[i], 1.0f / 2.4f) * 1.055f) - 0.055f; + } + return sRGBColor; +} + +// Creates a n-channel sRGB image from a linear floating-point image. +// The source image can have more than N channels, but only the first N are converted to sRGB. +template +std::unique_ptr fromLinearTosRGB(const LinearImage& image) { + const size_t w = image.getWidth(); + const size_t h = image.getHeight(); + const size_t nchan = image.getChannels(); + assert(nchan >= N); + std::unique_ptr dst(new uint8_t[w * h * N * sizeof(T)]); + T* d = reinterpret_cast(dst.get()); + for (size_t y = 0; y < h; ++y) { + float const* p = image.getPixelRef(0, y); + for (size_t x = 0; x < w; ++x, p += nchan, d += N) { + for (int n = 0; n < N; n++) { + float source = n < 3 ? linearTosRGB(p[n]) : p[n]; + float target = filament::math::saturate(source) * std::numeric_limits::max() + 0.5f; + d[n] = T(target); + } + } + } + return dst; +} + +// Creates a N-channel RGB u8 image from a f32 image. +template +std::unique_ptr fromLinearToRGB(const LinearImage& image) { + size_t w = image.getWidth(); + size_t h = image.getHeight(); + size_t channels = image.getChannels(); + assert(channels >= N); + std::unique_ptr dst(new uint8_t[w * h * N * sizeof(T)]); + T* d = reinterpret_cast(dst.get()); + for (size_t y = 0; y < h; ++y) { + float const* p = image.getPixelRef(0, y); + for (size_t x = 0; x < w; ++x, p += channels, d += N) { + for (int n = 0; n < N; n++) { + float target = filament::math::saturate(p[n]) * std::numeric_limits::max() + 0.5f; + d[n] = T(target); + } + } + } + return dst; +} + +// Creates a 4-channel RGBM u8 image from a f32 image. +// The source image can have three or more channels, but only the first three are honored. +template +std::unique_ptr fromLinearToRGBM(const LinearImage& image) { + using namespace filament::math; + size_t w = image.getWidth(); + size_t h = image.getHeight(); + UTILS_UNUSED_IN_RELEASE size_t channels = image.getChannels(); + assert(channels >= 3); + std::unique_ptr dst(new uint8_t[w * h * 4 * sizeof(T)]); + T* d = reinterpret_cast(dst.get()); + for (size_t y = 0; y < h; ++y) { + for (size_t x = 0; x < w; ++x, d += 4) { + auto src = image.get((uint32_t) x, (uint32_t) y); + float4 l(linearToRGBM(*src) * std::numeric_limits::max() + 0.5f); + for (size_t i = 0; i < 4; i++) { + d[i] = T(l[i]); + } + } + } + return dst; +} + +// Creates a 3-channel RGB_10_11_11_REV image from a f32 image. +// The source image can have three or more channels, but only the first three are honored. +inline std::unique_ptr fromLinearToRGB_10_11_11_REV(const LinearImage& image) { + using namespace filament::math; + size_t w = image.getWidth(); + size_t h = image.getHeight(); + UTILS_UNUSED_IN_RELEASE size_t channels = image.getChannels(); + assert(channels >= 3); + std::unique_ptr dst(new uint8_t[w * h * sizeof(uint32_t)]); + uint8_t* d = dst.get(); + for (size_t y = 0; y < h; ++y) { + for (size_t x = 0; x < w; ++x, d += sizeof(uint32_t)) { + auto src = image.get((uint32_t)x, (uint32_t)y); + uint32_t v = linearToRGB_10_11_11_REV(*src); + *reinterpret_cast(d) = v; + } + } + return dst; +} + +// Creates a packed single-channel integer-based image from a floating-point image. +// For example if T is uint8_t, then this performs a transformation from [0,1] to [0,255]. +template +std::unique_ptr fromLinearToGrayscale(const LinearImage& image) { + const size_t w = image.getWidth(); + const size_t h = image.getHeight(); + assert(image.getChannels() == 1); + std::unique_ptr dst(new uint8_t[w * h * sizeof(T)]); + T* d = reinterpret_cast(dst.get()); + for (size_t y = 0; y < h; ++y) { + float const* p = image.getPixelRef(0, y); + for (size_t x = 0; x < w; ++x, ++p, ++d) { + const float gray = filament::math::saturate(*p) * std::numeric_limits::max() + 0.5f; + d[0] = T(gray); + } + } + return dst; +} + +// Constructs a 3-channel LinearImage from an untyped data blob. +// The "proc" lambda converts a single color component into a float. +// The "transform" lambda performs an arbitrary float-to-float transformation. +template +static LinearImage toLinear(size_t w, size_t h, size_t bpr, + const uint8_t* src, PROCESS proc, TRANSFORM transform) { + LinearImage result((uint32_t) w, (uint32_t) h, 3); + auto d = result.get< filament::math::float3>(); + for (size_t y = 0; y < h; ++y) { + T const* p = reinterpret_cast(src + y * bpr); + for (size_t x = 0; x < w; ++x, p += 3) { + filament::math::float3 sRGB(proc(p[0]), proc(p[1]), proc(p[2])); + sRGB /= std::numeric_limits::max(); + *d++ = transform(sRGB); + } + } + return result; +} + +// Constructs a 3-channel LinearImage from an untyped data blob. +// The "proc" lambda converts a single color component into a float. +// The "transform" lambda performs an arbitrary float-to-float transformation. +template +static LinearImage toLinear(size_t w, size_t h, size_t bpr, + const std::unique_ptr& src, PROCESS proc, TRANSFORM transform) { + return toLinear(w, h, bpr, src.get(), proc, transform); +} + +// Constructs a 4-channel LinearImage from an untyped data blob. +// The "proc" lambda converts a single color component into a float. +// the "transform" lambda performs an arbitrary float-to-float transformation. +template +static LinearImage toLinearWithAlpha(size_t w, size_t h, size_t bpr, + const uint8_t* src, PROCESS proc, TRANSFORM transform) { + LinearImage result((uint32_t) w, (uint32_t) h, 4); + auto d = result.get< filament::math::float4>(); + for (size_t y = 0; y < h; ++y) { + T const* p = reinterpret_cast(src + y * bpr); + for (size_t x = 0; x < w; ++x, p += 4) { + filament::math::float4 sRGB(proc(p[0]), proc(p[1]), proc(p[2]), proc(p[3])); + sRGB /= std::numeric_limits::max(); + *d++ = transform(sRGB); + } + } + return result; +} + +// Constructs a 4-channel LinearImage from an untyped data blob. +// The "proc" lambda converts a single color component into a float. +// the "transform" lambda performs an arbitrary float-to-float transformation. +template +static LinearImage toLinearWithAlpha(size_t w, size_t h, size_t bpr, + const std::unique_ptr& src, PROCESS proc, TRANSFORM transform) { + return toLinearWithAlpha(w, h, bpr, src.get(), proc, transform); +} + +// Constructs a 3-channel LinearImage from RGBM data. +inline LinearImage toLinearFromRGBM( filament::math::float4 const* src, uint32_t w, uint32_t h) { + LinearImage result(w, h, 3); + auto dst = result.get< filament::math::float3>(); + for (uint32_t row = 0; row < h; ++row) { + for (uint32_t col = 0; col < w; ++col, ++src, ++dst) { + *dst = RGBMtoLinear(*src); + } + } + return result; +} + +inline LinearImage fromLinearToRGBM(const LinearImage& image) { + assert(image.getChannels() == 3); + const uint32_t w = image.getWidth(), h = image.getHeight(); + LinearImage result(w, h, 4); + auto src = image.get< filament::math::float3>(); + auto dst = result.get< filament::math::float4>(); + for (uint32_t row = 0; row < h; ++row) { + for (uint32_t col = 0; col < w; ++col, ++src, ++dst) { + *dst = linearToRGBM(*src); + } + } + return result; +} + +template +static LinearImage toLinearWithAlpha(size_t w, size_t h, size_t bpr, const uint8_t* src) { + LinearImage result(w, h, 4); + filament::math::float4* d = reinterpret_cast(result.getPixelRef(0, 0)); + for (size_t y = 0; y < h; ++y) { + T const* p = reinterpret_cast(src + y * bpr); + for (size_t x = 0; x < w; ++x, p += 4) { + filament::math::float3 sRGB(p[0], p[1], p[2]); + sRGB /= std::numeric_limits::max(); + *d++ = filament::math::float4(sRGBToLinear(sRGB), 1.0f); + } + } + return result; +} + +template +static LinearImage toLinear(size_t w, size_t h, size_t bpr, const uint8_t* src) { + LinearImage result(w, h, 3); + filament::math::float3* d = reinterpret_cast(result.getPixelRef(0, 0)); + for (size_t y = 0; y < h; ++y) { + T const* p = reinterpret_cast(src + y * bpr); + for (size_t x = 0; x < w; ++x, p += 3) { + filament::math::float3 sRGB(p[0], p[1], p[2]); + sRGB /= std::numeric_limits::max(); + *d++ = sRGBToLinear(sRGB); + } + } + return result; +} + +} // namespace Image + +#endif // IMAGE_COLORTRANSFORM_H_ diff --git a/ios/include/image/ImageOps.h b/ios/include/image/ImageOps.h new file mode 100644 index 00000000..50adc90e --- /dev/null +++ b/ios/include/image/ImageOps.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IMAGE_IMAGEOPS_H +#define IMAGE_IMAGEOPS_H + +#include + +#include + +#include +#include + +namespace image { + +// Concatenates images horizontally to create a filmstrip atlas, similar to numpy's hstack. +UTILS_PUBLIC LinearImage horizontalStack(std::initializer_list images); +UTILS_PUBLIC LinearImage horizontalStack(LinearImage const* img, size_t count); + +// Concatenates images vertically to create a filmstrip atlas, similar to numpy's vstack. +UTILS_PUBLIC LinearImage verticalStack(std::initializer_list images); +UTILS_PUBLIC LinearImage verticalStack(LinearImage const* img, size_t count); + +// Horizontally or vertically mirror the given image. +UTILS_PUBLIC LinearImage horizontalFlip(const LinearImage& image); +UTILS_PUBLIC LinearImage verticalFlip(const LinearImage& image); + +// Transforms normals (components live in [-1,+1]) into colors (components live in [0,+1]). +UTILS_PUBLIC LinearImage vectorsToColors(const LinearImage& image); +UTILS_PUBLIC LinearImage colorsToVectors(const LinearImage& image); + +// Creates a single-channel image by extracting the selected channel. +UTILS_PUBLIC LinearImage extractChannel(const LinearImage& image, uint32_t channel); + +// Constructs a multi-channel image by copying data from a sequence of single-channel images. +UTILS_PUBLIC LinearImage combineChannels(std::initializer_list images); +UTILS_PUBLIC LinearImage combineChannels(LinearImage const* img, size_t count); + +// Generates a new image with rows & columns swapped. +UTILS_PUBLIC LinearImage transpose(const LinearImage& image); + +// Extracts pixels by specifying a crop window where (0,0) is the top-left corner of the image. +// The boundary is specified as Left Top Right Bottom. +UTILS_PUBLIC +LinearImage cropRegion(const LinearImage& image, uint32_t l, uint32_t t, uint32_t r, uint32_t b); + +// Lexicographically compares two images, similar to memcmp. +UTILS_PUBLIC int compare(const LinearImage& a, const LinearImage& b, float epsilon = 0.0f); + +// Sets all pixels in all channels to the given value. +UTILS_PUBLIC void clearToValue(LinearImage& img, float value); + +// Called by the coordinate field generator to query if a pixel is within the region of interest. +using PresenceCallback = bool(*)(const LinearImage& img, uint32_t col, uint32_t row, void* user); + +// Generates a two-channel field of non-normalized coordinates that indicate the nearest pixel +// whose presence function returns true. This is the first step before generating a distance +// field or generalized Voronoi map. +UTILS_PUBLIC +LinearImage computeCoordField(const LinearImage& src, PresenceCallback presence, void* user); + +// Generates a single-channel Euclidean distance field with positive values outside the region +// of interest in the source image, and zero values inside. If sqrt is false, the computed +// distances are squared. If signed distance (SDF) is desired, this function can be called a second +// time using an inverted source field. +UTILS_PUBLIC LinearImage edtFromCoordField(const LinearImage& coordField, bool sqrt); + +// Dereferences the given coordinate field. Useful for creating Voronoi diagrams or dilated images. +UTILS_PUBLIC +LinearImage voronoiFromCoordField(const LinearImage& coordField, const LinearImage& src); + +// Copies content of a source image into a target image. Requires width/height/channels to match. +UTILS_PUBLIC void blitImage(LinearImage& target, const LinearImage& source); + +} // namespace image + + +#endif /* IMAGE_LINEARIMAGE_H */ diff --git a/ios/include/image/ImageSampler.h b/ios/include/image/ImageSampler.h new file mode 100644 index 00000000..e01da45e --- /dev/null +++ b/ios/include/image/ImageSampler.h @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IMAGE_IMAGESAMPLER_H +#define IMAGE_IMAGESAMPLER_H + +#include + +#include + +namespace image { + +/** + * Value of a single point sample, allocated according to the number of image channels. + */ +struct UTILS_PUBLIC SingleSample { + float& operator[](int index) { return *(data + index); } + float* data = nullptr; + ~SingleSample(); +}; + +/** + * Controls the weighted average used across a window of source samples. + */ +enum class Filter { + DEFAULT, // Selects MITCHELL or LANCZOS dynamically. + BOX, // Computes the un-weighted average over the filter radius. + NEAREST, // Copies the source sample nearest to the center of the filter. + HERMITE, // Also known as "smoothstep", has some nice properties. + GAUSSIAN_SCALARS, // Standard Gaussian filter with sigma = 0.5 + GAUSSIAN_NORMALS, // Same as GAUSSIAN_SCALARS, but interpolates unitized vectors. + MITCHELL, // Cubic resampling per Mitchell-Netravali, default for magnification. + LANCZOS, // Popular sinc-based filter, default for minification. + MINIMUM // Takes a min val rather than avg, perhaps useful for depth maps and SDF's. +}; + +/** + * Defines a viewport inside the texture such that (0,0) is at the top-left corner of the top-left + * pixel, and (1,1) is at the bottom-right corner of the bottom-corner pixel. + */ +struct Region { + float left; + float top; + float right; + float bottom; +}; + +/** + * Transforms the texel fetching operation when sampling from adjacent images. + */ +enum class Orientation { + STANDARD = 0, + FLIP_X = 1 << 0, + FLIP_Y = 1 << 1, + FLIP_XY = FLIP_X | FLIP_Y +}; + +/** + * Specifies how to generate samples that lie outside the boundaries of the source region. + */ +struct Boundary { + enum { + EXCLUDE, // Ignore the samples and renormalize the filter. This is probably what you want. + REGION, // Keep samples that are outside sourceRegion if they are still within the image. + CLAMP, // Pretend the edge pixel is repeated forever. Gives edge pixels more weight. + REPEAT, // Resample from the region, wrapping back to the front of the row or column. + MIRROR, // Resample from the region but assume that it has been flipped. + COLOR, // Use the specified constant color. + NEIGHBOR // Sample from an adjacent image. + } mode = EXCLUDE; + SingleSample color; // Used only if mode = COLOR + LinearImage* neighbor = nullptr; // Used only if mode = NEIGHBOR + Orientation orientation; // Used only if mode = NEIGHBOR +}; + +/** + * Configuration for the resampleImage function. Provides reasonable defaults. + */ +struct ImageSampler { + Filter horizontalFilter = Filter::DEFAULT; + Filter verticalFilter = Filter::DEFAULT; + Region sourceRegion = {0, 0, 1, 1}; + float filterRadiusMultiplier = 1; + Boundary east; + Boundary north; + Boundary west; + Boundary south; +}; + +/** + * Resizes or blurs the given linear image, producing a new linear image with the given dimensions. + */ +UTILS_PUBLIC +LinearImage resampleImage(const LinearImage& source, uint32_t width, uint32_t height, + const ImageSampler& sampler); + +/** + * Resizes the given linear image using a simplified API that takes target dimensions and filter. + */ +UTILS_PUBLIC +LinearImage resampleImage(const LinearImage& source, uint32_t width, uint32_t height, + Filter filter = Filter::DEFAULT); + +/** + * Computes a single sample for the given texture coordinate and writes the resulting color + * components into the given output holder. + * + * For decent performance, do not call this across the entire image, instead call resampleImage. + * On the first call, pass in a default SingleSample to allocate the result holder. For example: + * + * SingleSample result; + * computeSingleSample(img, 0.5f, 0.5f, &result); + * printf("r g b = %f %f %f\n", result[0], result[1], result[2]); + * computeSingleSample(img, 0.9f, 0.1f, &result); + * printf("r g b = %f %f %f\n", result[0], result[1], result[2]); + * + * The x y coordinates live in "texture space" such that (0.0f, 0.0f) is the upper-left boundary of + * the top-left pixel and (+1.0f, +1.0f) is the lower-right boundary of the bottom-right pixel. + */ +UTILS_PUBLIC +void computeSingleSample(const LinearImage& source, float x, float y, SingleSample* result, + Filter filter = Filter::BOX); + +/** + * Generates a sequence of miplevels using the requested filter. To determine the number of mips + * it would take to get down to 1x1, see getMipmapCount. + * + * Source image need not be power-of-two. In the result vector, the half-size image is returned at + * index 0, the quarter-size image is at index 1, etc. Please note that the original-sized image is + * not included. + */ +UTILS_PUBLIC +void generateMipmaps(const LinearImage& source, Filter, LinearImage* result, uint32_t mipCount); + +/** + * Returns the number of miplevels it would take to downsample the given image down to 1x1. This + * number does not include the original image (i.e. mip 0). + */ +UTILS_PUBLIC +uint32_t getMipmapCount(const LinearImage& source); + +/** + * Given the string name of a filter, converts it to uppercase and returns the corresponding + * enum value. If no corresponding enumerant exists, returns DEFAULT. + */ +UTILS_PUBLIC +Filter filterFromString(const char* name); + +} // namespace image + +#endif /* IMAGE_IMAGESAMPLER_H */ diff --git a/ios/include/image/KtxBundle.h b/ios/include/image/KtxBundle.h new file mode 100644 index 00000000..3975a644 --- /dev/null +++ b/ios/include/image/KtxBundle.h @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IMAGE_KTXBUNDLE_H +#define IMAGE_KTXBUNDLE_H + +#include + +#include + +#include +#include + +namespace image { + +struct KtxInfo { + uint32_t endianness; + uint32_t glType; + uint32_t glTypeSize; + uint32_t glFormat; + uint32_t glInternalFormat; + uint32_t glBaseInternalFormat; + uint32_t pixelWidth; + uint32_t pixelHeight; + uint32_t pixelDepth; +}; + +struct KtxBlobIndex { + uint32_t mipLevel; + uint32_t arrayIndex; + uint32_t cubeFace; +}; + +struct KtxBlobList; +struct KtxMetadata; + +/** + * KtxBundle is a structured set of opaque data blobs that can be passed straight to the GPU, such + * that a single bundle corresponds to a single texture object. It is well suited for storing + * block-compressed texture data. + * + * One bundle may be comprised of several mipmap levels, cubemap faces, and array elements. The + * number of blobs is immutable, and is determined as follows. + * + * blob_count = mip_count * array_length * (cubemap ? 6 : 1) + * + * Bundles can be quickly serialized to a certain file format (see below link), but this class lives + * in the image lib rather than imageio because it has no dependencies, and does not support CPU + * decoding. + * + * https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/ + */ +class UTILS_PUBLIC KtxBundle { +public: + + ~KtxBundle(); + + /** + * Creates a hierarchy of empty texture blobs, to be filled later via setBlob(). + */ + KtxBundle(uint32_t numMipLevels, uint32_t arrayLength, bool isCubemap); + + /** + * Creates a new bundle by deserializing the given data. + * + * Typically, this constructor is used to consume the contents of a KTX file. + */ + KtxBundle(uint8_t const* bytes, uint32_t nbytes); + + /** + * Serializes the bundle into the given target memory. Returns false if there's not enough + * memory. + * + * Typically, this method is used to write out the contents of a KTX file. + */ + bool serialize(uint8_t* destination, uint32_t numBytes) const; + + /** + * Computes the size (in bytes) of the serialized bundle. + */ + uint32_t getSerializedLength() const; + + /** + * Gets or sets information about the texture object, such as format and type. + */ + KtxInfo const& getInfo() const { return mInfo; } + KtxInfo& info() { return mInfo; } + + /** + * Gets or sets key/value metadata. + */ + const char* getMetadata(const char* key, size_t* valueSize = nullptr) const; + void setMetadata(const char* key, const char* value); + + /** + * Parses the key="sh" metadata and returns 3 bands of data. + * + * Assumes 3 bands for a total of 9 RGB coefficients. + * Returns true if successful. + */ + bool getSphericalHarmonics(filament::math::float3* result); + + /** + * Gets the number of miplevels (this is never zero). + */ + uint32_t getNumMipLevels() const { return mNumMipLevels; } + + /** + * Gets the number of array elements (this is never zero). + */ + uint32_t getArrayLength() const { return mArrayLength; } + + /** + * Returns whether or not this is a cubemap. + */ + bool isCubemap() const { return mNumCubeFaces > 1; } + + /** + * Retrieves a weak reference to a given data blob. Returns false if the given blob index is out + * of bounds, or if the blob at the given index is empty. + */ + bool getBlob(KtxBlobIndex index, uint8_t** data, uint32_t* size) const; + + /** + * Copies the given data into the blob at the given index, replacing whatever is already there. + * Returns false if the given blob index is out of bounds. + */ + bool setBlob(KtxBlobIndex index, uint8_t const* data, uint32_t size); + + /** + * Allocates the blob at the given index to the given number of bytes. This allows subsequent + * calls to setBlob to be thread-safe. + */ + bool allocateBlob(KtxBlobIndex index, uint32_t size); + + // The following constants help clients populate the "info" struct. Most of them have corollary + // constants in the OpenGL headers. + + static constexpr uint32_t R8 = 0x8229; + static constexpr uint32_t R8_SNORM = 0x8F94; + static constexpr uint32_t R8UI = 0x8232; + static constexpr uint32_t R8I = 0x8231; + static constexpr uint32_t STENCIL_INDEX8 = 0x8D48; + static constexpr uint32_t R16F = 0x822D; + static constexpr uint32_t R16UI = 0x8234; + static constexpr uint32_t R16I = 0x8233; + static constexpr uint32_t RG8 = 0x822B; + static constexpr uint32_t RG8_SNORM = 0x8F95; + static constexpr uint32_t RG8UI = 0x8238; + static constexpr uint32_t RG8I = 0x8237; + static constexpr uint32_t RGB565 = 0x8D62; + static constexpr uint32_t RGB5_A1 = 0x8057; + static constexpr uint32_t RGBA4 = 0x8056; + static constexpr uint32_t DEPTH_COMPONENT16 = 0x81A5; + static constexpr uint32_t RGB8 = 0x8051; + static constexpr uint32_t SRGB8 = 0x8C41; + static constexpr uint32_t RGB8_SNORM = 0x8F96; + static constexpr uint32_t RGB8UI = 0x8D7D; + static constexpr uint32_t RGB8I = 0x8D8F; + static constexpr uint32_t DEPTH_COMPONENT24 = 0x81A6; + static constexpr uint32_t R32F = 0x822E; + static constexpr uint32_t R32UI = 0x8236; + static constexpr uint32_t R32I = 0x8235; + static constexpr uint32_t RG16F = 0x822F; + static constexpr uint32_t RG16UI = 0x823A; + static constexpr uint32_t RG16I = 0x8239; + static constexpr uint32_t R11F_G11F_B10F = 0x8C3A; + static constexpr uint32_t RGB9_E5 = 0x8C3D; + static constexpr uint32_t RGBA8 = 0x8058; + static constexpr uint32_t SRGB8_ALPHA8 = 0x8C43; + static constexpr uint32_t RGBA8_SNORM = 0x8F97; + static constexpr uint32_t RGB10_A2 = 0x8059; + static constexpr uint32_t RGBA8UI = 0x8D7C; + static constexpr uint32_t RGBA8I = 0x8D8E; + static constexpr uint32_t DEPTH_COMPONENT32F = 0x8CAC; + static constexpr uint32_t DEPTH24_STENCIL8 = 0x88F0; + static constexpr uint32_t DEPTH32F_STENCIL8 = 0x8CAD; + static constexpr uint32_t RGB16F = 0x881B; + static constexpr uint32_t RGB16UI = 0x8D77; + static constexpr uint32_t RGB16I = 0x8D89; + static constexpr uint32_t RG32F = 0x8230; + static constexpr uint32_t RG32UI = 0x823C; + static constexpr uint32_t RG32I = 0x823B; + static constexpr uint32_t RGBA16F = 0x881A; + static constexpr uint32_t RGBA16UI = 0x8D76; + static constexpr uint32_t RGBA16I = 0x8D88; + static constexpr uint32_t RGB32F = 0x8815; + static constexpr uint32_t RGB32UI = 0x8D71; + static constexpr uint32_t RGB32I = 0x8D83; + static constexpr uint32_t RGBA32F = 0x8814; + static constexpr uint32_t RGBA32UI = 0x8D70; + static constexpr uint32_t RGBA32I = 0x8D82; + + static constexpr uint32_t RED = 0x1903; + static constexpr uint32_t RG = 0x8227; + static constexpr uint32_t RGB = 0x1907; + static constexpr uint32_t RGBA = 0x1908; + static constexpr uint32_t BGR = 0x80E0; + static constexpr uint32_t BGRA = 0x80E1; + static constexpr uint32_t LUMINANCE = 0x1909; + static constexpr uint32_t LUMINANCE_ALPHA = 0x190A; + + static constexpr uint32_t UNSIGNED_BYTE = 0x1401; + static constexpr uint32_t UNSIGNED_SHORT = 0x1403; + static constexpr uint32_t HALF_FLOAT = 0x140B; + static constexpr uint32_t FLOAT = 0x1406; + + static constexpr uint32_t ENDIAN_DEFAULT = 0x04030201; + + static constexpr uint32_t RGB_S3TC_DXT1 = 0x83F0; + static constexpr uint32_t RGBA_S3TC_DXT1 = 0x83F1; + static constexpr uint32_t RGBA_S3TC_DXT3 = 0x83F2; + static constexpr uint32_t RGBA_S3TC_DXT5 = 0x83F3; + + static constexpr uint32_t RGBA_ASTC_4x4 = 0x93B0; + static constexpr uint32_t RGBA_ASTC_5x4 = 0x93B1; + static constexpr uint32_t RGBA_ASTC_5x5 = 0x93B2; + static constexpr uint32_t RGBA_ASTC_6x5 = 0x93B3; + static constexpr uint32_t RGBA_ASTC_6x6 = 0x93B4; + static constexpr uint32_t RGBA_ASTC_8x5 = 0x93B5; + static constexpr uint32_t RGBA_ASTC_8x6 = 0x93B6; + static constexpr uint32_t RGBA_ASTC_8x8 = 0x93B7; + static constexpr uint32_t RGBA_ASTC_10x5 = 0x93B8; + static constexpr uint32_t RGBA_ASTC_10x6 = 0x93B9; + static constexpr uint32_t RGBA_ASTC_10x8 = 0x93BA; + static constexpr uint32_t RGBA_ASTC_10x10 = 0x93BB; + static constexpr uint32_t RGBA_ASTC_12x10 = 0x93BC; + static constexpr uint32_t RGBA_ASTC_12x12 = 0x93BD; + static constexpr uint32_t SRGB8_ALPHA8_ASTC_4x4 = 0x93D0; + static constexpr uint32_t SRGB8_ALPHA8_ASTC_5x4 = 0x93D1; + static constexpr uint32_t SRGB8_ALPHA8_ASTC_5x5 = 0x93D2; + static constexpr uint32_t SRGB8_ALPHA8_ASTC_6x5 = 0x93D3; + static constexpr uint32_t SRGB8_ALPHA8_ASTC_6x6 = 0x93D4; + static constexpr uint32_t SRGB8_ALPHA8_ASTC_8x5 = 0x93D5; + static constexpr uint32_t SRGB8_ALPHA8_ASTC_8x6 = 0x93D6; + static constexpr uint32_t SRGB8_ALPHA8_ASTC_8x8 = 0x93D7; + static constexpr uint32_t SRGB8_ALPHA8_ASTC_10x5 = 0x93D8; + static constexpr uint32_t SRGB8_ALPHA8_ASTC_10x6 = 0x93D9; + static constexpr uint32_t SRGB8_ALPHA8_ASTC_10x8 = 0x93DA; + static constexpr uint32_t SRGB8_ALPHA8_ASTC_10x10 = 0x93DB; + static constexpr uint32_t SRGB8_ALPHA8_ASTC_12x10 = 0x93DC; + static constexpr uint32_t SRGB8_ALPHA8_ASTC_12x12 = 0x93DD; + + static constexpr uint32_t R11_EAC = 0x9270; + static constexpr uint32_t SIGNED_R11_EAC = 0x9271; + static constexpr uint32_t RG11_EAC = 0x9272; + static constexpr uint32_t SIGNED_RG11_EAC = 0x9273; + static constexpr uint32_t RGB8_ETC2 = 0x9274; + static constexpr uint32_t SRGB8_ETC2 = 0x9275; + static constexpr uint32_t RGB8_ALPHA1_ETC2 = 0x9276; + static constexpr uint32_t SRGB8_ALPHA1_ETC = 0x9277; + static constexpr uint32_t RGBA8_ETC2_EAC = 0x9278; + static constexpr uint32_t SRGB8_ALPHA8_ETC2_EAC = 0x9279; + +private: + image::KtxInfo mInfo = {}; + uint32_t mNumMipLevels; + uint32_t mArrayLength; + uint32_t mNumCubeFaces; + std::unique_ptr mBlobs; + std::unique_ptr mMetadata; +}; + +} // namespace image + +#endif /* IMAGE_KTXBUNDLE_H */ diff --git a/ios/include/image/KtxUtility.h b/ios/include/image/KtxUtility.h new file mode 100644 index 00000000..7ff4d89f --- /dev/null +++ b/ios/include/image/KtxUtility.h @@ -0,0 +1,367 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IMAGE_KTXUTILITY_H +#define IMAGE_KTXUTILITY_H + +#include +#include + +#include + +namespace image { + +/** + * Allows clients to create Filament textures from KtxBundle objects. + * + * Note that libimage does not have a dependency on libfilament, so for simplicity this is a + * header-only library with inlined functions. + */ +namespace ktx { + + using Texture = filament::Texture; + using Engine = filament::Engine; + + using TextureFormat = Texture::InternalFormat; + using CompressedPixelDataType = Texture::CompressedType; + using PixelDataType = Texture::Type; + using PixelDataFormat = Texture::Format; + using PixelBufferDescriptor = Texture::PixelBufferDescriptor; + + using Callback = void(*)(void* userdata); + + CompressedPixelDataType toCompressedPixelDataType(const KtxInfo& info); + PixelDataType toPixelDataType(const KtxInfo& info); + PixelDataFormat toPixelDataFormat(const KtxInfo& info); + bool isCompressed(const KtxInfo& info); + TextureFormat toTextureFormat(const KtxInfo& info); + TextureFormat toSrgbTextureFormat(TextureFormat tex); + + /** + * Creates a Texture object from a KTX file and populates all of its faces and miplevels. + * + * @param engine Used to create the Filament Texture + * @param ktx In-memory representation of a KTX file + * @param srgb Forces the KTX-specified format into an SRGB format if possible + * @param callback Gets called after all texture data has been uploaded to the GPU + * @param userdata Passed into the callback + */ + inline Texture* createTexture(Engine* engine, const KtxBundle& ktx, bool srgb, + Callback callback, void* userdata) { + using Sampler = Texture::Sampler; + const auto& ktxinfo = ktx.getInfo(); + const uint32_t nmips = ktx.getNumMipLevels(); + const auto cdatatype = toCompressedPixelDataType(ktxinfo); + const auto datatype = toPixelDataType(ktxinfo); + const auto dataformat = toPixelDataFormat(ktxinfo); + + auto texformat = toTextureFormat(ktxinfo); + if (srgb) { + texformat = toSrgbTextureFormat(texformat); + } + + Texture* texture = Texture::Builder() + .width(ktxinfo.pixelWidth) + .height(ktxinfo.pixelHeight) + .levels(static_cast(nmips)) + .sampler(ktx.isCubemap() ? Sampler::SAMPLER_CUBEMAP : Sampler::SAMPLER_2D) + .format(texformat) + .build(*engine); + + struct Userdata { + uint32_t remainingBuffers; + Callback callback; + void* userdata; + }; + + Userdata* cbuser = new Userdata({nmips, callback, userdata}); + + PixelBufferDescriptor::Callback cb = [](void*, size_t, void* cbuserptr) { + Userdata* cbuser = (Userdata*) cbuserptr; + if (--cbuser->remainingBuffers == 0) { + if (cbuser->callback) { + cbuser->callback(cbuser->userdata); + } + delete cbuser; + } + }; + + uint8_t* data; + uint32_t size; + + if (isCompressed(ktxinfo)) { + if (ktx.isCubemap()) { + for (uint32_t level = 0; level < nmips; ++level) { + ktx.getBlob({level, 0, 0}, &data, &size); + PixelBufferDescriptor pbd(data, size * 6, cdatatype, size, cb, cbuser); + texture->setImage(*engine, level, std::move(pbd), Texture::FaceOffsets(size)); + } + return texture; + } + for (uint32_t level = 0; level < nmips; ++level) { + ktx.getBlob({level, 0, 0}, &data, &size); + PixelBufferDescriptor pbd(data, size, cdatatype, size, cb, cbuser); + texture->setImage(*engine, level, std::move(pbd)); + } + return texture; + } + + if (ktx.isCubemap()) { + for (uint32_t level = 0; level < nmips; ++level) { + ktx.getBlob({level, 0, 0}, &data, &size); + PixelBufferDescriptor pbd(data, size * 6, dataformat, datatype, cb, cbuser); + texture->setImage(*engine, level, std::move(pbd), Texture::FaceOffsets(size)); + } + return texture; + } + + for (uint32_t level = 0; level < nmips; ++level) { + ktx.getBlob({level, 0, 0}, &data, &size); + PixelBufferDescriptor pbd(data, size, dataformat, datatype, cb, cbuser); + texture->setImage(*engine, level, std::move(pbd)); + } + return texture; + } + + /** + * Creates a Texture object from a KTX bundle, populates all of its faces and miplevels, + * and automatically destroys the bundle after all the texture data has been uploaded. + * + * @param engine Used to create the Filament Texture + * @param ktx In-memory representation of a KTX file + * @param srgb Forces the KTX-specified format into an SRGB format if possible + */ + inline Texture* createTexture(Engine* engine, KtxBundle* ktx, bool srgb) { + auto freeKtx = [] (void* userdata) { + KtxBundle* ktx = (KtxBundle*) userdata; + delete ktx; + }; + return createTexture(engine, *ktx, srgb, freeKtx, ktx); + } + + template + T toCompressedFilamentEnum(uint32_t format) { + switch (format) { + case KtxBundle::RGB_S3TC_DXT1: return T::DXT1_RGB; + case KtxBundle::RGBA_S3TC_DXT1: return T::DXT1_RGBA; + case KtxBundle::RGBA_S3TC_DXT3: return T::DXT3_RGBA; + case KtxBundle::RGBA_S3TC_DXT5: return T::DXT5_RGBA; + case KtxBundle::RGBA_ASTC_4x4: return T::RGBA_ASTC_4x4; + case KtxBundle::RGBA_ASTC_5x4: return T::RGBA_ASTC_5x4; + case KtxBundle::RGBA_ASTC_5x5: return T::RGBA_ASTC_5x5; + case KtxBundle::RGBA_ASTC_6x5: return T::RGBA_ASTC_6x5; + case KtxBundle::RGBA_ASTC_6x6: return T::RGBA_ASTC_6x6; + case KtxBundle::RGBA_ASTC_8x5: return T::RGBA_ASTC_8x5; + case KtxBundle::RGBA_ASTC_8x6: return T::RGBA_ASTC_8x6; + case KtxBundle::RGBA_ASTC_8x8: return T::RGBA_ASTC_8x8; + case KtxBundle::RGBA_ASTC_10x5: return T::RGBA_ASTC_10x5; + case KtxBundle::RGBA_ASTC_10x6: return T::RGBA_ASTC_10x6; + case KtxBundle::RGBA_ASTC_10x8: return T::RGBA_ASTC_10x8; + case KtxBundle::RGBA_ASTC_10x10: return T::RGBA_ASTC_10x10; + case KtxBundle::RGBA_ASTC_12x10: return T::RGBA_ASTC_12x10; + case KtxBundle::RGBA_ASTC_12x12: return T::RGBA_ASTC_12x12; + case KtxBundle::SRGB8_ALPHA8_ASTC_4x4: return T::SRGB8_ALPHA8_ASTC_4x4; + case KtxBundle::SRGB8_ALPHA8_ASTC_5x4: return T::SRGB8_ALPHA8_ASTC_5x4; + case KtxBundle::SRGB8_ALPHA8_ASTC_5x5: return T::SRGB8_ALPHA8_ASTC_5x5; + case KtxBundle::SRGB8_ALPHA8_ASTC_6x5: return T::SRGB8_ALPHA8_ASTC_6x5; + case KtxBundle::SRGB8_ALPHA8_ASTC_6x6: return T::SRGB8_ALPHA8_ASTC_6x6; + case KtxBundle::SRGB8_ALPHA8_ASTC_8x5: return T::SRGB8_ALPHA8_ASTC_8x5; + case KtxBundle::SRGB8_ALPHA8_ASTC_8x6: return T::SRGB8_ALPHA8_ASTC_8x6; + case KtxBundle::SRGB8_ALPHA8_ASTC_8x8: return T::SRGB8_ALPHA8_ASTC_8x8; + case KtxBundle::SRGB8_ALPHA8_ASTC_10x5: return T::SRGB8_ALPHA8_ASTC_10x5; + case KtxBundle::SRGB8_ALPHA8_ASTC_10x6: return T::SRGB8_ALPHA8_ASTC_10x6; + case KtxBundle::SRGB8_ALPHA8_ASTC_10x8: return T::SRGB8_ALPHA8_ASTC_10x8; + case KtxBundle::SRGB8_ALPHA8_ASTC_10x10: return T::SRGB8_ALPHA8_ASTC_10x10; + case KtxBundle::SRGB8_ALPHA8_ASTC_12x10: return T::SRGB8_ALPHA8_ASTC_12x10; + case KtxBundle::SRGB8_ALPHA8_ASTC_12x12: return T::SRGB8_ALPHA8_ASTC_12x12; + case KtxBundle::R11_EAC: return T::EAC_R11; + case KtxBundle::SIGNED_R11_EAC: return T::EAC_R11_SIGNED; + case KtxBundle::RG11_EAC: return T::EAC_RG11; + case KtxBundle::SIGNED_RG11_EAC: return T::EAC_RG11_SIGNED; + case KtxBundle::RGB8_ETC2: return T::ETC2_RGB8; + case KtxBundle::SRGB8_ETC2: return T::ETC2_SRGB8; + case KtxBundle::RGB8_ALPHA1_ETC2: return T::ETC2_RGB8_A1; + case KtxBundle::SRGB8_ALPHA1_ETC: return T::ETC2_SRGB8_A1; + case KtxBundle::RGBA8_ETC2_EAC: return T::ETC2_EAC_RGBA8; + case KtxBundle::SRGB8_ALPHA8_ETC2_EAC: return T::ETC2_EAC_SRGBA8; + } + return (T) 0xffff; + } + + inline CompressedPixelDataType toCompressedPixelDataType(const KtxInfo& info) { + return toCompressedFilamentEnum(info.glInternalFormat); + } + + inline PixelDataType toPixelDataType(const KtxInfo& info) { + switch (info.glType) { + case KtxBundle::UNSIGNED_BYTE: return PixelDataType::UBYTE; + case KtxBundle::UNSIGNED_SHORT: return PixelDataType::USHORT; + case KtxBundle::HALF_FLOAT: return PixelDataType::HALF; + case KtxBundle::FLOAT: return PixelDataType::FLOAT; + case KtxBundle::R11F_G11F_B10F: return PixelDataType::UINT_10F_11F_11F_REV; + } + return (PixelDataType) 0xff; + } + + inline PixelDataFormat toPixelDataFormat(const KtxInfo& info) { + switch (info.glFormat) { + case KtxBundle::LUMINANCE: + case KtxBundle::RED: return PixelDataFormat::R; + case KtxBundle::RG: return PixelDataFormat::RG; + case KtxBundle::RGB: return PixelDataFormat::RGB; + case KtxBundle::RGBA: return PixelDataFormat::RGBA; + // glFormat should NOT be a sized format according to the spec + // however cmgen was generating incorrect files until after Filament 1.8.0 + // so we keep this line here to preserve compatibility with older assets + case KtxBundle::R11F_G11F_B10F: return PixelDataFormat::RGB; + } + return (PixelDataFormat) 0xff; + } + + inline bool isCompressed(const KtxInfo& info) { + return info.glFormat == 0; + } + + inline TextureFormat toSrgbTextureFormat(TextureFormat format) { + switch(format) { + // Non-compressed + case Texture::InternalFormat::RGB8: + return Texture::InternalFormat::SRGB8; + case Texture::InternalFormat::RGBA8: + return Texture::InternalFormat::SRGB8_A8; + + // ASTC + case Texture::InternalFormat::RGBA_ASTC_4x4: + return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_4x4; + case Texture::InternalFormat::RGBA_ASTC_5x4: + return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_5x4; + case Texture::InternalFormat::RGBA_ASTC_5x5: + return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_5x5; + case Texture::InternalFormat::RGBA_ASTC_6x5: + return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_6x5; + case Texture::InternalFormat::RGBA_ASTC_6x6: + return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_6x6; + case Texture::InternalFormat::RGBA_ASTC_8x5: + return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_8x5; + case Texture::InternalFormat::RGBA_ASTC_8x6: + return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_8x6; + case Texture::InternalFormat::RGBA_ASTC_8x8: + return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_8x8; + case Texture::InternalFormat::RGBA_ASTC_10x5: + return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_10x5; + case Texture::InternalFormat::RGBA_ASTC_10x6: + return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_10x6; + case Texture::InternalFormat::RGBA_ASTC_10x8: + return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_10x8; + case Texture::InternalFormat::RGBA_ASTC_10x10: + return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_10x10; + case Texture::InternalFormat::RGBA_ASTC_12x10: + return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_12x10; + case Texture::InternalFormat::RGBA_ASTC_12x12: + return Texture::InternalFormat::SRGB8_ALPHA8_ASTC_12x12; + + // ETC2 + case Texture::InternalFormat::ETC2_RGB8: + return Texture::InternalFormat::ETC2_SRGB8; + case Texture::InternalFormat::ETC2_RGB8_A1: + return Texture::InternalFormat::ETC2_SRGB8_A1; + case Texture::InternalFormat::ETC2_EAC_RGBA8: + return Texture::InternalFormat::ETC2_EAC_SRGBA8; + + // DXT + case Texture::InternalFormat::DXT1_RGB: + return Texture::InternalFormat::DXT1_SRGB; + case Texture::InternalFormat::DXT1_RGBA: + return Texture::InternalFormat::DXT1_SRGBA; + case Texture::InternalFormat::DXT3_RGBA: + return Texture::InternalFormat::DXT3_SRGBA; + case Texture::InternalFormat::DXT5_RGBA: + return Texture::InternalFormat::DXT5_SRGBA; + + default: + return format; + } + } + + inline TextureFormat toTextureFormat(const KtxInfo& info) { + switch (info.glInternalFormat) { + case KtxBundle::RED: return TextureFormat::R8; + case KtxBundle::RG: return TextureFormat::RG8; + case KtxBundle::RGB: return TextureFormat::RGB8; + case KtxBundle::RGBA: return TextureFormat::RGBA8; + case KtxBundle::LUMINANCE: return TextureFormat::R8; + case KtxBundle::LUMINANCE_ALPHA: return TextureFormat::RG8; + case KtxBundle::R8: return TextureFormat::R8; + case KtxBundle::R8_SNORM: return TextureFormat::R8_SNORM; + case KtxBundle::R8UI: return TextureFormat::R8UI; + case KtxBundle::R8I: return TextureFormat::R8I; + case KtxBundle::STENCIL_INDEX8: return TextureFormat::STENCIL8; + case KtxBundle::R16F: return TextureFormat::R16F; + case KtxBundle::R16UI: return TextureFormat::R16UI; + case KtxBundle::R16I: return TextureFormat::R16I; + case KtxBundle::RG8: return TextureFormat::RG8; + case KtxBundle::RG8_SNORM: return TextureFormat::RG8_SNORM; + case KtxBundle::RG8UI: return TextureFormat::RG8UI; + case KtxBundle::RG8I: return TextureFormat::RG8I; + case KtxBundle::RGB565: return TextureFormat::RGB565; + case KtxBundle::RGB9_E5: return TextureFormat::RGB9_E5; + case KtxBundle::RGB5_A1: return TextureFormat::RGB5_A1; + case KtxBundle::RGBA4: return TextureFormat::RGBA4; + case KtxBundle::DEPTH_COMPONENT16: return TextureFormat::DEPTH16; + case KtxBundle::RGB8: return TextureFormat::RGB8; + case KtxBundle::SRGB8: return TextureFormat::SRGB8; + case KtxBundle::RGB8_SNORM: return TextureFormat::RGB8_SNORM; + case KtxBundle::RGB8UI: return TextureFormat::RGB8UI; + case KtxBundle::RGB8I: return TextureFormat::RGB8I; + case KtxBundle::R32F: return TextureFormat::R32F; + case KtxBundle::R32UI: return TextureFormat::R32UI; + case KtxBundle::R32I: return TextureFormat::R32I; + case KtxBundle::RG16F: return TextureFormat::RG16F; + case KtxBundle::RG16UI: return TextureFormat::RG16UI; + case KtxBundle::RG16I: return TextureFormat::RG16I; + case KtxBundle::R11F_G11F_B10F: return TextureFormat::R11F_G11F_B10F; + case KtxBundle::RGBA8: return TextureFormat::RGBA8; + case KtxBundle::SRGB8_ALPHA8: return TextureFormat::SRGB8_A8; + case KtxBundle::RGBA8_SNORM: return TextureFormat::RGBA8_SNORM; + case KtxBundle::RGB10_A2: return TextureFormat::RGB10_A2; + case KtxBundle::RGBA8UI: return TextureFormat::RGBA8UI; + case KtxBundle::RGBA8I: return TextureFormat::RGBA8I; + case KtxBundle::DEPTH24_STENCIL8: return TextureFormat::DEPTH24_STENCIL8; + case KtxBundle::DEPTH32F_STENCIL8: return TextureFormat::DEPTH32F_STENCIL8; + case KtxBundle::RGB16F: return TextureFormat::RGB16F; + case KtxBundle::RGB16UI: return TextureFormat::RGB16UI; + case KtxBundle::RGB16I: return TextureFormat::RGB16I; + case KtxBundle::RG32F: return TextureFormat::RG32F; + case KtxBundle::RG32UI: return TextureFormat::RG32UI; + case KtxBundle::RG32I: return TextureFormat::RG32I; + case KtxBundle::RGBA16F: return TextureFormat::RGBA16F; + case KtxBundle::RGBA16UI: return TextureFormat::RGBA16UI; + case KtxBundle::RGBA16I: return TextureFormat::RGBA16I; + case KtxBundle::RGB32F: return TextureFormat::RGB32F; + case KtxBundle::RGB32UI: return TextureFormat::RGB32UI; + case KtxBundle::RGB32I: return TextureFormat::RGB32I; + case KtxBundle::RGBA32F: return TextureFormat::RGBA32F; + case KtxBundle::RGBA32UI: return TextureFormat::RGBA32UI; + case KtxBundle::RGBA32I: return TextureFormat::RGBA32I; + } + return toCompressedFilamentEnum(info.glInternalFormat); + } + +} // namespace ktx + +} // namespace image + +#endif diff --git a/ios/include/image/LinearImage.h b/ios/include/image/LinearImage.h new file mode 100644 index 00000000..de46a787 --- /dev/null +++ b/ios/include/image/LinearImage.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IMAGE_LINEARIMAGE_H +#define IMAGE_LINEARIMAGE_H + +#include + +#include + +/** + * Types and free functions for the Filament core imaging library, primarily used for offline tools, + * but with minimal dependencies to support potential use by the renderer. + */ +namespace image { + +/** + * LinearImage is a handle to packed floating point data arranged into a row-major grid. + * + * We use this object as input/output for core algorithms that wish to be agnostic of source and + * destination formats. The number of channels is arbitrary (1 or more) but we often use 3-channel + * images to represent color data. + * + * The underlying pixel data has shared ownership semantics to allow clients to easily pass around + * the image object without incurring a deep copy. Shared access to pixels is not thread safe. + * + * By convention, we do not use channel major order (i.e. planar). However we provide a free + * function in ImageOps to combine planar data. Pixels are stored such that the row stride is simply + * width * channels * sizeof(float). + */ +class UTILS_PUBLIC LinearImage { +public: + + ~LinearImage(); + + /** + * Allocates a zeroed-out image. + */ + LinearImage(uint32_t width, uint32_t height, uint32_t channels); + + /** + * Makes a shallow copy with shared pixel data. + */ + LinearImage(const LinearImage& that); + LinearImage& operator=(const LinearImage& that); + + /** + * Creates an empty (invalid) image. + */ + LinearImage() : mDataRef(nullptr), mData(nullptr), mWidth(0), mHeight(0), mChannels(0) {} + operator bool() const { return mData != nullptr; } + + /** + * Gets a pointer to the underlying pixel data. + */ + float* getPixelRef() { return mData; } + template T* get() { return reinterpret_cast(mData); } + + /** + * Gets a pointer to immutable pixel data. + */ + float const* getPixelRef() const { return mData; } + template T const* get() const { return reinterpret_cast(mData); } + + /** + * Gets a pointer to the pixel data at the given column and row. (not bounds checked) + */ + float* getPixelRef(uint32_t column, uint32_t row) { + return mData + (column + row * mWidth) * mChannels; + } + + template + T* get(uint32_t column, uint32_t row) { + return reinterpret_cast(getPixelRef(column, row)); + } + + /** + * Gets a pointer to the immutable pixel data at the given column and row. (not bounds checked) + */ + float const* getPixelRef(uint32_t column, uint32_t row) const { + return mData + (column + row * mWidth) * mChannels; + } + + template + T const* get(uint32_t column, uint32_t row) const { + return reinterpret_cast(getPixelRef(column, row)); + } + + uint32_t getWidth() const { return mWidth; } + uint32_t getHeight() const { return mHeight; } + uint32_t getChannels() const { return mChannels; } + void reset() { *this = LinearImage(); } + bool isValid() const { return mData; } + +private: + + struct SharedReference; + SharedReference* mDataRef = nullptr; + + float* mData; + uint32_t mWidth; + uint32_t mHeight; + uint32_t mChannels; +}; + +} // namespace image + +#endif /* IMAGE_LINEARIMAGE_H */ diff --git a/ios/include/math/TMatHelpers.h b/ios/include/math/TMatHelpers.h new file mode 100644 index 00000000..f8ff71c9 --- /dev/null +++ b/ios/include/math/TMatHelpers.h @@ -0,0 +1,807 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MATH_TMATHELPERS_H_ +#define MATH_TMATHELPERS_H_ + +#include +#include +#include + +#include // for std::swap +#include // for std:: namespace + +#include +#include +#include + +namespace filament { +namespace math { +namespace details { +// ------------------------------------------------------------------------------------- + +/* + * No user serviceable parts here. + * + * Don't use this file directly, instead include math/mat*.h + */ + + +/* + * Matrix utilities + */ + +namespace matrix { + +/* + * Matrix inversion + */ +template +constexpr MATRIX MATH_PURE gaussJordanInverse(MATRIX src) { + typedef typename MATRIX::value_type T; + constexpr unsigned int N = MATRIX::NUM_ROWS; + MATRIX inverted; + + for (size_t i = 0; i < N; ++i) { + // look for largest element in i'th column + size_t swap = i; + T t = src[i][i] < 0 ? -src[i][i] : src[i][i]; + for (size_t j = i + 1; j < N; ++j) { + const T t2 = src[j][i] < 0 ? -src[j][i] : src[j][i]; + if (t2 > t) { + swap = j; + t = t2; + } + } + + if (swap != i) { + // swap columns. + std::swap(src[i], src[swap]); + std::swap(inverted[i], inverted[swap]); + } + + const T denom(src[i][i]); + for (size_t k = 0; k < N; ++k) { + src[i][k] /= denom; + inverted[i][k] /= denom; + } + + // Factor out the lower triangle + for (size_t j = 0; j < N; ++j) { + if (j != i) { + const T t = src[j][i]; + for (size_t k = 0; k < N; ++k) { + src[j][k] -= src[i][k] * t; + inverted[j][k] -= inverted[i][k] * t; + } + } + } + } + + return inverted; +} + +//------------------------------------------------------------------------------ +// 2x2 matrix inverse is easy. +template +constexpr MATRIX MATH_PURE fastInverse2(const MATRIX& x) { + typedef typename MATRIX::value_type T; + + // Assuming the input matrix is: + // | a b | + // | c d | + // + // The analytic inverse is + // | d -b | + // | -c a | / (a d - b c) + // + // Importantly, our matrices are column-major! + + MATRIX inverted{}; + + const T a = x[0][0]; + const T c = x[0][1]; + const T b = x[1][0]; + const T d = x[1][1]; + + const T det((a * d) - (b * c)); + inverted[0][0] = d / det; + inverted[0][1] = -c / det; + inverted[1][0] = -b / det; + inverted[1][1] = a / det; + return inverted; +} + +//------------------------------------------------------------------------------ +// From the Wikipedia article on matrix inversion's section on fast 3x3 +// matrix inversion: +// http://en.wikipedia.org/wiki/Invertible_matrix#Inversion_of_3.C3.973_matrices +template +constexpr MATRIX MATH_PURE fastInverse3(const MATRIX& x) { + typedef typename MATRIX::value_type T; + + // Assuming the input matrix is: + // | a b c | + // | d e f | + // | g h i | + // + // The analytic inverse is + // | A B C |^T + // | D E F | + // | G H I | / determinant + // + // Which is + // | A D G | + // | B E H | + // | C F I | / determinant + // + // Where: + // A = (ei - fh), B = (fg - di), C = (dh - eg) + // D = (ch - bi), E = (ai - cg), F = (bg - ah) + // G = (bf - ce), H = (cd - af), I = (ae - bd) + // + // and the determinant is a*A + b*B + c*C (The rule of Sarrus) + // + // Importantly, our matrices are column-major! + + MATRIX inverted{}; + + const T a = x[0][0]; + const T b = x[1][0]; + const T c = x[2][0]; + const T d = x[0][1]; + const T e = x[1][1]; + const T f = x[2][1]; + const T g = x[0][2]; + const T h = x[1][2]; + const T i = x[2][2]; + + // Do the full analytic inverse + const T A = e * i - f * h; + const T B = f * g - d * i; + const T C = d * h - e * g; + inverted[0][0] = A; // A + inverted[0][1] = B; // B + inverted[0][2] = C; // C + inverted[1][0] = c * h - b * i; // D + inverted[1][1] = a * i - c * g; // E + inverted[1][2] = b * g - a * h; // F + inverted[2][0] = b * f - c * e; // G + inverted[2][1] = c * d - a * f; // H + inverted[2][2] = a * e - b * d; // I + + const T det(a * A + b * B + c * C); + for (size_t col = 0; col < 3; ++col) { + for (size_t row = 0; row < 3; ++row) { + inverted[col][row] /= det; + } + } + + return inverted; +} + + +//------------------------------------------------------------------------------ +// Determinant and cofactor + +// this is just a dummy matrix helper +template +class Matrix { + T m[ORDER][ORDER]; +public: + constexpr auto operator[](size_t i) const noexcept { return m[i]; } + + constexpr auto& operator[](size_t i) noexcept { return m[i]; } + + static constexpr Matrix submatrix(Matrix in, size_t row, size_t col) noexcept { + size_t colCount = 0, rowCount = 0; + Matrix dest{}; + for (size_t i = 0; i < ORDER; i++) { + if (i != row) { + colCount = 0; + for (size_t j = 0; j < ORDER; j++) { + if (j != col) { + dest[rowCount][colCount] = in[i][j]; + colCount++; + } + } + rowCount++; + } + } + return dest; + } +}; + +template +struct Determinant { + static constexpr T determinant(Matrix in) { + T det = {}; + for (size_t i = 0; i < O; i++) { + T m = Determinant::determinant(Matrix::submatrix(in, 0, i)); + T factor = (i % 2 == 1) ? T(-1) : T(1); + det += factor * in[0][i] * m; + } + return det; + } +}; + +template +struct Determinant { + static constexpr T determinant(Matrix in) { + return + in[0][0] * in[1][1] * in[2][2] + + in[1][0] * in[2][1] * in[0][2] + + in[2][0] * in[0][1] * in[1][2] - + in[2][0] * in[1][1] * in[0][2] - + in[1][0] * in[0][1] * in[2][2] - + in[0][0] * in[2][1] * in[1][2]; + } +}; + +template +struct Determinant { + static constexpr T determinant(Matrix in) { + return in[0][0] * in[1][1] - in[0][1] * in[1][0]; + } +}; + +template +struct Determinant { + static constexpr T determinant(Matrix in) { return in[0][0]; } +}; + +template +constexpr MATRIX MATH_PURE cofactor(const MATRIX& m) { + typedef typename MATRIX::value_type T; + + MATRIX out; + constexpr size_t order = MATRIX::NUM_COLS; + + Matrix in{}; + for (size_t i = 0; i < order; i++) { + for (size_t j = 0; j < order; j++) { + in[i][j] = m[i][j]; + } + } + + for (size_t i = 0; i < order; i++) { + for (size_t j = 0; j < order; j++) { + T factor = ((i + j) % 2 == 1) ? T(-1) : T(1); + out[i][j] = Determinant::determinant( + Matrix::submatrix(in, i, j)) * factor; + } + } + return out; +} + +template +constexpr MATRIX MATH_PURE fastCofactor2(const MATRIX& m) { + typedef typename MATRIX::value_type T; + + // Assuming the input matrix is: + // | a b | + // | c d | + // + // The cofactor are + // | d -c | + // | -b a | + // + // Importantly, our matrices are column-major! + + MATRIX cof{}; + + const T a = m[0][0]; + const T c = m[0][1]; + const T b = m[1][0]; + const T d = m[1][1]; + + cof[0][0] = d; + cof[0][1] = -b; + cof[1][0] = -c; + cof[1][1] = a; + return cof; +} + +template +constexpr MATRIX MATH_PURE fastCofactor3(const MATRIX& m) { + typedef typename MATRIX::value_type T; + + // Assuming the input matrix is: + // | a b c | + // | d e f | + // | g h i | + // + // The cofactor are + // | A B C | + // | D E F | + // | G H I | + + // Where: + // A = (ei - fh), B = (fg - di), C = (dh - eg) + // D = (ch - bi), E = (ai - cg), F = (bg - ah) + // G = (bf - ce), H = (cd - af), I = (ae - bd) + + // Importantly, our matrices are column-major! + + MATRIX cof{}; + + const T a = m[0][0]; + const T b = m[1][0]; + const T c = m[2][0]; + const T d = m[0][1]; + const T e = m[1][1]; + const T f = m[2][1]; + const T g = m[0][2]; + const T h = m[1][2]; + const T i = m[2][2]; + + cof[0][0] = e * i - f * h; // A + cof[0][1] = c * h - b * i; // D + cof[0][2] = b * f - c * e; // G + cof[1][0] = f * g - d * i; // B + cof[1][1] = a * i - c * g; // E + cof[1][2] = c * d - a * f; // H + cof[2][0] = d * h - e * g; // C + cof[2][1] = b * g - a * h; // F + cof[2][2] = a * e - b * d; // I + + return cof; +} + + +/** + * Cofactor function which switches on the matrix size. + */ +template> +inline constexpr MATRIX MATH_PURE cof(const MATRIX& matrix) { + return (MATRIX::NUM_ROWS == 2) ? fastCofactor2(matrix) : + ((MATRIX::NUM_ROWS == 3) ? fastCofactor3(matrix) : + cofactor(matrix)); +} + +/** + * Determinant of a matrix + */ +template> +inline constexpr typename MATRIX::value_type MATH_PURE det(const MATRIX& matrix) { + typedef typename MATRIX::value_type T; + constexpr unsigned int N = MATRIX::NUM_ROWS; + Matrix in{}; + for (size_t i = 0; i < N; i++) { + for (size_t j = 0; j < N; j++) { + in[i][j] = matrix[i][j]; + } + } + return Determinant::determinant(in); +} + +/** + * Inversion function which switches on the matrix size. + * @warning This function assumes the matrix is invertible. The result is + * undefined if it is not. It is the responsibility of the caller to + * make sure the matrix is not singular. + */ +template> +inline constexpr MATRIX MATH_PURE inverse(const MATRIX& matrix) { + return (MATRIX::NUM_ROWS == 2) ? fastInverse2(matrix) : + ((MATRIX::NUM_ROWS == 3) ? fastInverse3(matrix) : + gaussJordanInverse(matrix)); +} + +template> +constexpr MATRIX_R MATH_PURE multiply(MATRIX_A lhs, MATRIX_B rhs) { + // pre-requisite: + // lhs : D columns, R rows + // rhs : C columns, D rows + // res : C columns, R rows + MATRIX_R res{}; + for (size_t col = 0; col < MATRIX_R::NUM_COLS; ++col) { + res[col] = lhs * rhs[col]; + } + return res; +} + +template> +inline constexpr MATRIX MATH_PURE transpose(MATRIX m) { + // for now we only handle square matrix transpose + MATRIX result{}; + for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) { + for (size_t row = 0; row < MATRIX::NUM_ROWS; ++row) { + result[col][row] = m[row][col]; + } + } + return result; +} + +template> +inline constexpr typename MATRIX::value_type MATH_PURE trace(MATRIX m) { + typename MATRIX::value_type result{}; + for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) { + result += m[col][col]; + } + return result; +} + +template> +inline constexpr typename MATRIX::col_type MATH_PURE diag(MATRIX m) { + typename MATRIX::col_type result{}; + for (size_t col = 0; col < MATRIX::NUM_COLS; ++col) { + result[col] = m[col][col]; + } + return result; +} + +//------------------------------------------------------------------------------ +// This is taken from the Imath MatrixAlgo code, and is identical to Eigen. +template +TQuaternion extractQuat(const MATRIX& mat) { + typedef typename MATRIX::value_type T; + + TQuaternion quat(TQuaternion::NO_INIT); + + // Compute the trace to see if it is positive or not. + const T trace = mat[0][0] + mat[1][1] + mat[2][2]; + + // check the sign of the trace + if (MATH_LIKELY(trace > 0)) { + // trace is positive + T s = std::sqrt(trace + 1); + quat.w = T(0.5) * s; + s = T(0.5) / s; + quat.x = (mat[1][2] - mat[2][1]) * s; + quat.y = (mat[2][0] - mat[0][2]) * s; + quat.z = (mat[0][1] - mat[1][0]) * s; + } else { + // trace is negative + + // Find the index of the greatest diagonal + size_t i = 0; + if (mat[1][1] > mat[0][0]) { i = 1; } + if (mat[2][2] > mat[i][i]) { i = 2; } + + // Get the next indices: (n+1)%3 + static constexpr size_t next_ijk[3] = { 1, 2, 0 }; + size_t j = next_ijk[i]; + size_t k = next_ijk[j]; + T s = std::sqrt((mat[i][i] - (mat[j][j] + mat[k][k])) + 1); + quat[i] = T(0.5) * s; + if (s != 0) { + s = T(0.5) / s; + } + quat.w = (mat[j][k] - mat[k][j]) * s; + quat[j] = (mat[i][j] + mat[j][i]) * s; + quat[k] = (mat[i][k] + mat[k][i]) * s; + } + return quat; +} + +} // namespace matrix + +// ------------------------------------------------------------------------------------- + +/* + * TMatProductOperators implements basic arithmetic and basic compound assignments + * operators on a vector of type BASE. + * + * BASE only needs to implement operator[] and size(). + * By simply inheriting from TMatProductOperators BASE will automatically + * get all the functionality here. + */ + +template class BASE, typename T, + template class VEC> +class TMatProductOperators { +public: + // matrix *= matrix + template + constexpr BASE& operator*=(const BASE& rhs) { + BASE& lhs(static_cast< BASE& >(*this)); + lhs = matrix::multiply>(lhs, rhs); + return lhs; + } + + // matrix *= scalar + template> + constexpr BASE& operator*=(U v) { + BASE& lhs(static_cast< BASE& >(*this)); + for (size_t col = 0; col < BASE::NUM_COLS; ++col) { + lhs[col] *= v; + } + return lhs; + } + + // matrix /= scalar + template> + constexpr BASE& operator/=(U v) { + BASE& lhs(static_cast< BASE& >(*this)); + for (size_t col = 0; col < BASE::NUM_COLS; ++col) { + lhs[col] /= v; + } + return lhs; + } + +private: + /* + * NOTE: the functions below ARE NOT member methods. They are friend functions + * with they definition inlined with their declaration. This makes these + * template functions available to the compiler when (and only when) this class + * is instantiated, at which point they're only templated on the 2nd parameter + * (the first one, BASE being known). + */ + + // matrix * matrix + template + friend inline constexpr BASE> MATH_PURE + operator*(BASE lhs, BASE rhs) { + return matrix::multiply>>(lhs, rhs); + } + + // matrix * vector + template + friend inline constexpr typename BASE>::col_type MATH_PURE + operator*(const BASE& lhs, const VEC& rhs) { + typename BASE>::col_type result{}; + for (size_t col = 0; col < BASE::NUM_COLS; ++col) { + result += lhs[col] * rhs[col]; + } + return result; + } + + // row-vector * matrix + template + friend inline constexpr typename BASE>::row_type MATH_PURE + operator*(const VEC& lhs, const BASE& rhs) { + typename BASE>::row_type result{}; + for (size_t col = 0; col < BASE::NUM_COLS; ++col) { + result[col] = dot(lhs, rhs[col]); + } + return result; + } + + // matrix * scalar + template> + friend inline constexpr BASE> MATH_PURE + operator*(const BASE& lhs, U rhs) { + BASE> result{}; + for (size_t col = 0; col < BASE::NUM_COLS; ++col) { + result[col] = lhs[col] * rhs; + } + return result; + } + + // scalar * matrix + template> + friend inline constexpr BASE> MATH_PURE + operator*(U rhs, const BASE& lhs) { + return lhs * rhs; + } + + // matrix / scalar + template> + friend inline constexpr BASE> MATH_PURE + operator/(const BASE& lhs, U rhs) { + BASE> result{}; + for (size_t col = 0; col < BASE::NUM_COLS; ++col) { + result[col] = lhs[col] / rhs; + } + return result; + } +}; + +/* + * TMatSquareFunctions implements functions on a matrix of type BASE. + * + * BASE only needs to implement: + * - operator[] + * - col_type + * - row_type + * - COL_SIZE + * - ROW_SIZE + * + * By simply inheriting from TMatSquareFunctions BASE will automatically + * get all the functionality here. + */ + +template class BASE, typename T> +class TMatSquareFunctions { +private: + /* + * NOTE: the functions below ARE NOT member methods. They are friend functions + * with they definition inlined with their declaration. This makes these + * template functions available to the compiler when (and only when) this class + * is instantiated, at which point they're only templated on the 2nd parameter + * (the first one, BASE being known). + */ + friend inline constexpr BASE MATH_PURE inverse(const BASE& matrix) { + return matrix::inverse(matrix); + } + + friend inline constexpr BASE MATH_PURE cof(const BASE& matrix) { + return matrix::cof(matrix); + } + + friend inline constexpr BASE MATH_PURE transpose(BASE m) { + return matrix::transpose(m); + } + + friend inline constexpr T MATH_PURE trace(BASE m) { + return matrix::trace(m); + } + + friend inline constexpr T MATH_PURE det(const BASE& m) { + return matrix::det(m); + } + + // unclear why we have to use 'auto' here. 'typename BASE::col_type' produces + // error: no type named 'col_type' in 'filament::math::details::TMat44' + friend inline constexpr auto MATH_PURE diag(const BASE& m) { + return matrix::diag(m); + } +}; + +template class BASE, typename T> +class TMatHelpers { +public: + constexpr inline size_t getColumnSize() const { return BASE::COL_SIZE; } + constexpr inline size_t getRowSize() const { return BASE::ROW_SIZE; } + constexpr inline size_t getColumnCount() const { return BASE::NUM_COLS; } + constexpr inline size_t getRowCount() const { return BASE::NUM_ROWS; } + constexpr inline size_t size() const { return BASE::ROW_SIZE; } // for TVec*<> + + // array access + constexpr T const* asArray() const { + return &static_cast const &>(*this)[0][0]; + } + + // element access + inline constexpr T const& operator()(size_t row, size_t col) const { + return static_cast const &>(*this)[col][row]; + } + + inline T& operator()(size_t row, size_t col) { + return static_cast&>(*this)[col][row]; + } + +private: + constexpr friend inline BASE MATH_PURE abs(BASE m) { + for (size_t col = 0; col < BASE::NUM_COLS; ++col) { + m[col] = abs(m[col]); + } + return m; + } +}; + +// functions for 3x3 and 4x4 matrices +template class BASE, typename T> +class TMatTransform { +public: + inline constexpr TMatTransform() { + static_assert(BASE::NUM_ROWS == 3 || BASE::NUM_ROWS == 4, "3x3 or 4x4 matrices only"); + } + + template> + static BASE rotation(A radian, VEC about) { + BASE r; + T c = std::cos(radian); + T s = std::sin(radian); + if (about[0] == 1 && about[1] == 0 && about[2] == 0) { + r[1][1] = c; r[2][2] = c; + r[1][2] = s; r[2][1] = -s; + } else if (about[0] == 0 && about[1] == 1 && about[2] == 0) { + r[0][0] = c; r[2][2] = c; + r[2][0] = s; r[0][2] = -s; + } else if (about[0] == 0 && about[1] == 0 && about[2] == 1) { + r[0][0] = c; r[1][1] = c; + r[0][1] = s; r[1][0] = -s; + } else { + VEC nabout = normalize(about); + typename VEC::value_type x = nabout[0]; + typename VEC::value_type y = nabout[1]; + typename VEC::value_type z = nabout[2]; + T nc = 1 - c; + T xy = x * y; + T yz = y * z; + T zx = z * x; + T xs = x * s; + T ys = y * s; + T zs = z * s; + r[0][0] = x*x*nc + c; r[1][0] = xy*nc - zs; r[2][0] = zx*nc + ys; + r[0][1] = xy*nc + zs; r[1][1] = y*y*nc + c; r[2][1] = yz*nc - xs; + r[0][2] = zx*nc - ys; r[1][2] = yz*nc + xs; r[2][2] = z*z*nc + c; + + // Clamp results to -1, 1. + for (size_t col = 0; col < 3; ++col) { + for (size_t row = 0; row < 3; ++row) { + r[col][row] = std::min(std::max(r[col][row], T(-1)), T(1)); + } + } + } + return r; + } + + /** + * Create a matrix from euler angles using YPR around YXZ respectively + * @param yaw about Y axis + * @param pitch about X axis + * @param roll about Z axis + */ + template> + static BASE eulerYXZ(Y yaw, P pitch, R roll) { + return eulerZYX(roll, pitch, yaw); + } + + /** + * Create a matrix from euler angles using YPR around ZYX respectively + * @param roll about X axis + * @param pitch about Y axis + * @param yaw about Z axis + * + * The euler angles are applied in ZYX order. i.e: a vector is first rotated + * about X (roll) then Y (pitch) and then Z (yaw). + */ + template> + static BASE eulerZYX(Y yaw, P pitch, R roll) { + BASE r; + T cy = std::cos(yaw); + T sy = std::sin(yaw); + T cp = std::cos(pitch); + T sp = std::sin(pitch); + T cr = std::cos(roll); + T sr = std::sin(roll); + T cc = cr * cy; + T cs = cr * sy; + T sc = sr * cy; + T ss = sr * sy; + r[0][0] = cp * cy; + r[0][1] = cp * sy; + r[0][2] = -sp; + r[1][0] = sp * sc - cs; + r[1][1] = sp * ss + cc; + r[1][2] = cp * sr; + r[2][0] = sp * cc + ss; + r[2][1] = sp * cs - sc; + r[2][2] = cp * cr; + + // Clamp results to -1, 1. + for (size_t col = 0; col < 3; ++col) { + for (size_t row = 0; row < 3; ++row) { + r[col][row] = std::min(std::max(r[col][row], T(-1)), T(1)); + } + } + return r; + } + + TQuaternion toQuaternion() const { + return matrix::extractQuat(static_cast&>(*this)); + } +}; + +// ------------------------------------------------------------------------------------- +} // namespace details +} // namespace math +} // namespace filament + +#endif // MATH_TMATHELPERS_H_ diff --git a/ios/include/math/TQuatHelpers.h b/ios/include/math/TQuatHelpers.h new file mode 100644 index 00000000..184420b4 --- /dev/null +++ b/ios/include/math/TQuatHelpers.h @@ -0,0 +1,292 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef MATH_TQUATHELPERS_H_ +#define MATH_TQUATHELPERS_H_ + +#include +#include +#include + +#include +#include +#include + +namespace filament { +namespace math { +namespace details { +// ------------------------------------------------------------------------------------- + +/* + * No user serviceable parts here. + * + * Don't use this file directly, instead include math/quat.h + */ + + +/* + * TQuatProductOperators implements basic arithmetic and basic compound assignment + * operators on a quaternion of type BASE. + * + * BASE only needs to implement operator[] and size(). + * By simply inheriting from TQuatProductOperators BASE will automatically + * get all the functionality here. + */ + +template class QUATERNION, typename T> +class TQuatProductOperators { +public: + /* compound assignment from a another quaternion of the same size but different + * element type. + */ + template + constexpr QUATERNION& operator*=(const QUATERNION& r) { + QUATERNION& q = static_cast&>(*this); + q = q * r; + return q; + } + + /* compound assignment products by a scalar + */ + constexpr QUATERNION& operator*=(T v) { + QUATERNION& lhs = static_cast&>(*this); + for (size_t i = 0; i < QUATERNION::size(); i++) { + lhs[i] *= v; + } + return lhs; + } + + constexpr QUATERNION& operator/=(T v) { + QUATERNION& lhs = static_cast&>(*this); + for (size_t i = 0; i < QUATERNION::size(); i++) { + lhs[i] /= v; + } + return lhs; + } + + /* + * NOTE: the functions below ARE NOT member methods. They are friend functions + * with they definition inlined with their declaration. This makes these + * template functions available to the compiler when (and only when) this class + * is instantiated, at which point they're only templated on the 2nd parameter + * (the first one, BASE being known). + */ + + /* The operators below handle operation between quaternion of the same size + * but of a different element type. + */ + template + friend inline + constexpr QUATERNION MATH_PURE operator*(const QUATERNION& q, const QUATERNION& r) { + // could be written as: + // return QUATERNION( + // q.w*r.w - dot(q.xyz, r.xyz), + // q.w*r.xyz + r.w*q.xyz + cross(q.xyz, r.xyz)); + + return QUATERNION( + q.w * r.w - q.x * r.x - q.y * r.y - q.z * r.z, + q.w * r.x + q.x * r.w + q.y * r.z - q.z * r.y, + q.w * r.y - q.x * r.z + q.y * r.w + q.z * r.x, + q.w * r.z + q.x * r.y - q.y * r.x + q.z * r.w); + } + + template + friend inline + constexpr TVec3 MATH_PURE operator*(const QUATERNION& q, const TVec3& v) { + // note: if q is known to be a unit quaternion, then this simplifies to: + // TVec3 t = 2 * cross(q.xyz, v) + // return v + (q.w * t) + cross(q.xyz, t) + return imaginary(q * QUATERNION(v, 0) * inverse(q)); + } + + + /* For quaternions, we use explicit "by a scalar" products because it's much faster + * than going (implicitly) through the quaternion multiplication. + * For reference: we could use the code below instead, but it would be a lot slower. + * friend inline + * constexpr BASE MATH_PURE operator *(const BASE& q, const BASE& r) { + * return BASE( + * q.w*r.w - q.x*r.x - q.y*r.y - q.z*r.z, + * q.w*r.x + q.x*r.w + q.y*r.z - q.z*r.y, + * q.w*r.y - q.x*r.z + q.y*r.w + q.z*r.x, + * q.w*r.z + q.x*r.y - q.y*r.x + q.z*r.w); + * + */ + friend inline + constexpr QUATERNION MATH_PURE operator*(QUATERNION q, T scalar) { + // don't pass q by reference because we need a copy anyways + return q *= scalar; + } + + friend inline + constexpr QUATERNION MATH_PURE operator*(T scalar, QUATERNION q) { + // don't pass q by reference because we need a copy anyways + return q *= scalar; + } + + friend inline + constexpr QUATERNION MATH_PURE operator/(QUATERNION q, T scalar) { + // don't pass q by reference because we need a copy anyways + return q /= scalar; + } +}; + + +/* + * TQuatFunctions implements functions on a quaternion of type BASE. + * + * BASE only needs to implement operator[] and size(). + * By simply inheriting from TQuatFunctions BASE will automatically + * get all the functionality here. + */ +template class QUATERNION, typename T> +class TQuatFunctions { +public: + /* + * NOTE: the functions below ARE NOT member methods. They are friend functions + * with they definition inlined with their declaration. This makes these + * template functions available to the compiler when (and only when) this class + * is instantiated, at which point they're only templated on the 2nd parameter + * (the first one, BASE being known). + */ + + template + friend inline + constexpr T MATH_PURE dot(const QUATERNION& p, const QUATERNION& q) { + return p.x * q.x + + p.y * q.y + + p.z * q.z + + p.w * q.w; + } + + friend inline + T MATH_PURE norm(const QUATERNION& q) { + return std::sqrt(dot(q, q)); + } + + friend inline + T MATH_PURE length(const QUATERNION& q) { + return norm(q); + } + + friend inline + constexpr T MATH_PURE length2(const QUATERNION& q) { + return dot(q, q); + } + + friend inline + QUATERNION MATH_PURE normalize(const QUATERNION& q) { + return length(q) ? q / length(q) : QUATERNION(static_cast(1)); + } + + friend inline + constexpr QUATERNION MATH_PURE conj(const QUATERNION& q) { + return QUATERNION(q.w, -q.x, -q.y, -q.z); + } + + friend inline + constexpr QUATERNION MATH_PURE inverse(const QUATERNION& q) { + return conj(q) * (1 / dot(q, q)); + } + + friend inline + constexpr T MATH_PURE real(const QUATERNION& q) { + return q.w; + } + + friend inline + constexpr TVec3 MATH_PURE imaginary(const QUATERNION& q) { + return q.xyz; + } + + friend inline + constexpr QUATERNION MATH_PURE unreal(const QUATERNION& q) { + return QUATERNION(q.xyz, 0); + } + + friend inline + constexpr QUATERNION MATH_PURE cross(const QUATERNION& p, const QUATERNION& q) { + return unreal(p * q); + } + + friend inline + QUATERNION MATH_PURE exp(const QUATERNION& q) { + const T nq(norm(q.xyz)); + return std::exp(q.w) * QUATERNION((sin(nq) / nq) * q.xyz, cos(nq)); + } + + friend inline + QUATERNION MATH_PURE log(const QUATERNION& q) { + const T nq(norm(q)); + return QUATERNION((std::acos(q.w / nq) / norm(q.xyz)) * q.xyz, std::log(nq)); + } + + friend inline + QUATERNION MATH_PURE pow(const QUATERNION& q, T a) { + // could also be computed as: exp(a*log(q)); + const T nq(norm(q)); + const T theta(a * std::acos(q.w / nq)); + return std::pow(nq, a) * QUATERNION(normalize(q.xyz) * std::sin(theta), std::cos(theta)); + } + + friend inline + QUATERNION MATH_PURE slerp(const QUATERNION& p, const QUATERNION& q, T t) { + // could also be computed as: pow(q * inverse(p), t) * p; + const T d = dot(p, q); + const T absd = std::abs(d); + static constexpr T value_eps = T(10) * std::numeric_limits::epsilon(); + // Prevent blowing up when slerping between two quaternions that are very near each other. + if ((T(1) - absd) < value_eps) { + return normalize(lerp(d < 0 ? -p : p, q, t)); + } + const T npq = std::sqrt(dot(p, p) * dot(q, q)); // ||p|| * ||q|| + const T a = std::acos(filament::math::clamp(absd / npq, T(-1), T(1))); + const T a0 = a * (1 - t); + const T a1 = a * t; + const T sina = sin(a); + if (sina < value_eps) { + return normalize(lerp(p, q, t)); + } + const T isina = 1 / sina; + const T s0 = std::sin(a0) * isina; + const T s1 = std::sin(a1) * isina; + // ensure we're taking the "short" side + return normalize(s0 * p + ((d < 0) ? (-s1) : (s1)) * q); + } + + friend inline + constexpr QUATERNION MATH_PURE lerp(const QUATERNION& p, const QUATERNION& q, T t) { + return ((1 - t) * p) + (t * q); + } + + friend inline + constexpr QUATERNION MATH_PURE nlerp(const QUATERNION& p, const QUATERNION& q, T t) { + return normalize(lerp(p, q, t)); + } + + friend inline + constexpr QUATERNION MATH_PURE positive(const QUATERNION& q) { + return q.w < 0 ? -q : q; + } +}; + +// ------------------------------------------------------------------------------------- +} // namespace details +} // namespace math +} // namespace filament + +#endif // MATH_TQUATHELPERS_H_ diff --git a/ios/include/math/TVecHelpers.h b/ios/include/math/TVecHelpers.h new file mode 100644 index 00000000..ce66ec1a --- /dev/null +++ b/ios/include/math/TVecHelpers.h @@ -0,0 +1,625 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MATH_TVECHELPERS_H_ +#define MATH_TVECHELPERS_H_ + +#include + +#include // for std:: namespace + +#include +#include +#include + +namespace filament { +namespace math { +namespace details { +// ------------------------------------------------------------------------------------- + +template +inline constexpr U min(U a, U b) noexcept { + return a < b ? a : b; +} + +template +inline constexpr U max(U a, U b) noexcept { + return a > b ? a : b; +} + +template +struct arithmetic_result { + using type = decltype(std::declval() + std::declval()); +}; + +template +using arithmetic_result_t = typename arithmetic_result::type; + +template +using enable_if_arithmetic_t = std::enable_if_t< + is_arithmetic::value && + is_arithmetic::value && + is_arithmetic::value && + is_arithmetic::value>; + +/* + * No user serviceable parts here. + * + * Don't use this file directly, instead include math/vec{2|3|4}.h + */ + +/* + * TVec{Add|Product}Operators implements basic arithmetic and basic compound assignments + * operators on a vector of type BASE. + * + * BASE only needs to implement operator[] and size(). + * By simply inheriting from TVec{Add|Product}Operators BASE will automatically + * get all the functionality here. + */ + +template class VECTOR, typename T> +class TVecAddOperators { +public: + /* compound assignment from a another vector of the same size but different + * element type. + */ + template + constexpr VECTOR& operator+=(const VECTOR& v) { + VECTOR& lhs = static_cast&>(*this); + for (size_t i = 0; i < lhs.size(); i++) { + lhs[i] += v[i]; + } + return lhs; + } + + template> + constexpr VECTOR& operator+=(U v) { + return operator+=(VECTOR(v)); + } + + template + constexpr VECTOR& operator-=(const VECTOR& v) { + VECTOR& lhs = static_cast&>(*this); + for (size_t i = 0; i < lhs.size(); i++) { + lhs[i] -= v[i]; + } + return lhs; + } + + template> + constexpr VECTOR& operator-=(U v) { + return operator-=(VECTOR(v)); + } + +private: + /* + * NOTE: the functions below ARE NOT member methods. They are friend functions + * with they definition inlined with their declaration. This makes these + * template functions available to the compiler when (and only when) this class + * is instantiated, at which point they're only templated on the 2nd parameter + * (the first one, BASE being known). + */ + + template + friend inline constexpr + VECTOR> MATH_PURE operator+(const VECTOR& lv, const VECTOR& rv) { + VECTOR> res(lv); + res += rv; + return res; + } + + template> + friend inline constexpr + VECTOR> MATH_PURE operator+(const VECTOR& lv, U rv) { + return lv + VECTOR(rv); + } + + template> + friend inline constexpr + VECTOR> MATH_PURE operator+(U lv, const VECTOR& rv) { + return VECTOR(lv) + rv; + } + + template + friend inline constexpr + VECTOR> MATH_PURE operator-(const VECTOR& lv, const VECTOR& rv) { + VECTOR> res(lv); + res -= rv; + return res; + } + + template> + friend inline constexpr + VECTOR> MATH_PURE operator-(const VECTOR& lv, U rv) { + return lv - VECTOR(rv); + } + + template> + friend inline constexpr + VECTOR> MATH_PURE operator-(U lv, const VECTOR& rv) { + return VECTOR(lv) - rv; + } +}; + +template class VECTOR, typename T> +class TVecProductOperators { +public: + /* compound assignment from a another vector of the same size but different + * element type. + */ + template + constexpr VECTOR& operator*=(const VECTOR& v) { + VECTOR& lhs = static_cast&>(*this); + for (size_t i = 0; i < lhs.size(); i++) { + lhs[i] *= v[i]; + } + return lhs; + } + + template> + constexpr VECTOR& operator*=(U v) { + return operator*=(VECTOR(v)); + } + + template + constexpr VECTOR& operator/=(const VECTOR& v) { + VECTOR& lhs = static_cast&>(*this); + for (size_t i = 0; i < lhs.size(); i++) { + lhs[i] /= v[i]; + } + return lhs; + } + + template> + constexpr VECTOR& operator/=(U v) { + return operator/=(VECTOR(v)); + } + +private: + /* + * NOTE: the functions below ARE NOT member methods. They are friend functions + * with they definition inlined with their declaration. This makes these + * template functions available to the compiler when (and only when) this class + * is instantiated, at which point they're only templated on the 2nd parameter + * (the first one, BASE being known). + */ + + template + friend inline constexpr + VECTOR> MATH_PURE operator*(const VECTOR& lv, const VECTOR& rv) { + VECTOR> res(lv); + res *= rv; + return res; + } + + template> + friend inline constexpr + VECTOR> MATH_PURE operator*(const VECTOR& lv, U rv) { + return lv * VECTOR(rv); + } + + template> + friend inline constexpr + VECTOR> MATH_PURE operator*(U lv, const VECTOR& rv) { + return VECTOR(lv) * rv; + } + + template + friend inline constexpr + VECTOR> MATH_PURE operator/(const VECTOR& lv, const VECTOR& rv) { + VECTOR> res(lv); + res /= rv; + return res; + } + + template> + friend inline constexpr + VECTOR> MATH_PURE operator/(const VECTOR& lv, U rv) { + return lv / VECTOR(rv); + } + + template> + friend inline constexpr + VECTOR> MATH_PURE operator/(U lv, const VECTOR& rv) { + return VECTOR(lv) / rv; + } +}; + +/* + * TVecUnaryOperators implements unary operators on a vector of type BASE. + * + * BASE only needs to implement operator[] and size(). + * By simply inheriting from TVecUnaryOperators BASE will automatically + * get all the functionality here. + * + * These operators are implemented as friend functions of TVecUnaryOperators + */ +template class VECTOR, typename T> +class TVecUnaryOperators { +public: + constexpr VECTOR operator-() const { + VECTOR r{}; + VECTOR const& rv(static_cast const&>(*this)); + for (size_t i = 0; i < r.size(); i++) { + r[i] = -rv[i]; + } + return r; + } +}; + +/* + * TVecComparisonOperators implements relational/comparison operators + * on a vector of type BASE. + * + * BASE only needs to implement operator[] and size(). + * By simply inheriting from TVecComparisonOperators BASE will automatically + * get all the functionality here. + */ +template class VECTOR, typename T> +class TVecComparisonOperators { +private: + /* + * NOTE: the functions below ARE NOT member methods. They are friend functions + * with they definition inlined with their declaration. This makes these + * template functions available to the compiler when (and only when) this class + * is instantiated, at which point they're only templated on the 2nd parameter + * (the first one, BASE being known). + */ + template + friend inline constexpr + bool MATH_PURE operator==(const VECTOR& lv, const VECTOR& rv) { + // w/ inlining we end-up with many branches that will pollute the BPU cache + MATH_NOUNROLL + for (size_t i = 0; i < lv.size(); i++) { + if (lv[i] != rv[i]) { + return false; + } + } + return true; + } + + template + friend inline constexpr + bool MATH_PURE operator!=(const VECTOR& lv, const VECTOR& rv) { + return !operator==(lv, rv); + } + + template + friend inline constexpr + VECTOR MATH_PURE equal(const VECTOR& lv, const VECTOR& rv) { + VECTOR r{}; + for (size_t i = 0; i < lv.size(); i++) { + r[i] = lv[i] == rv[i]; + } + return r; + } + + template + friend inline constexpr + VECTOR MATH_PURE notEqual(const VECTOR& lv, const VECTOR& rv) { + VECTOR r{}; + for (size_t i = 0; i < lv.size(); i++) { + r[i] = lv[i] != rv[i]; + } + return r; + } + + template + friend inline constexpr + VECTOR MATH_PURE lessThan(const VECTOR& lv, const VECTOR& rv) { + VECTOR r{}; + for (size_t i = 0; i < lv.size(); i++) { + r[i] = lv[i] < rv[i]; + } + return r; + } + + template + friend inline constexpr + VECTOR MATH_PURE lessThanEqual(const VECTOR& lv, const VECTOR& rv) { + VECTOR r{}; + for (size_t i = 0; i < lv.size(); i++) { + r[i] = lv[i] <= rv[i]; + } + return r; + } + + template + friend inline constexpr + VECTOR MATH_PURE greaterThan(const VECTOR& lv, const VECTOR& rv) { + VECTOR r; + for (size_t i = 0; i < lv.size(); i++) { + r[i] = lv[i] > rv[i]; + } + return r; + } + + template + friend inline + VECTOR MATH_PURE greaterThanEqual(const VECTOR& lv, const VECTOR& rv) { + VECTOR r{}; + for (size_t i = 0; i < lv.size(); i++) { + r[i] = lv[i] >= rv[i]; + } + return r; + } +}; + +/* + * TVecFunctions implements functions on a vector of type BASE. + * + * BASE only needs to implement operator[] and size(). + * By simply inheriting from TVecFunctions BASE will automatically + * get all the functionality here. + */ +template class VECTOR, typename T> +class TVecFunctions { +private: + /* + * NOTE: the functions below ARE NOT member methods. They are friend functions + * with they definition inlined with their declaration. This makes these + * template functions available to the compiler when (and only when) this class + * is instantiated, at which point they're only templated on the 2nd parameter + * (the first one, BASE being known). + */ + template + friend constexpr inline + arithmetic_result_t MATH_PURE dot(const VECTOR& lv, const VECTOR& rv) { + arithmetic_result_t r{}; + for (size_t i = 0; i < lv.size(); i++) { + r += lv[i] * rv[i]; + } + return r; + } + + friend inline T MATH_PURE norm(const VECTOR& lv) { + return std::sqrt(dot(lv, lv)); + } + + friend inline T MATH_PURE length(const VECTOR& lv) { + return norm(lv); + } + + friend inline constexpr T MATH_PURE norm2(const VECTOR& lv) { + return dot(lv, lv); + } + + friend inline constexpr T MATH_PURE length2(const VECTOR& lv) { + return norm2(lv); + } + + template + friend inline constexpr + arithmetic_result_t MATH_PURE distance(const VECTOR& lv, const VECTOR& rv) { + return length(rv - lv); + } + + template + friend inline constexpr + arithmetic_result_t MATH_PURE distance2(const VECTOR& lv, const VECTOR& rv) { + return length2(rv - lv); + } + + friend inline VECTOR MATH_PURE normalize(const VECTOR& lv) { + return lv * (T(1) / length(lv)); + } + + friend inline VECTOR MATH_PURE rcp(VECTOR v) { + return T(1) / v; + } + + friend inline constexpr VECTOR MATH_PURE abs(VECTOR v) { + for (size_t i = 0; i < v.size(); i++) { + v[i] = v[i] < 0 ? -v[i] : v[i]; + } + return v; + } + + friend inline VECTOR MATH_PURE floor(VECTOR v) { + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::floor(v[i]); + } + return v; + } + + friend inline VECTOR MATH_PURE ceil(VECTOR v) { + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::ceil(v[i]); + } + return v; + } + + friend inline VECTOR MATH_PURE round(VECTOR v) { + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::round(v[i]); + } + return v; + } + + friend inline VECTOR MATH_PURE inversesqrt(VECTOR v) { + for (size_t i = 0; i < v.size(); i++) { + v[i] = T(1) / std::sqrt(v[i]); + } + return v; + } + + friend inline VECTOR MATH_PURE sqrt(VECTOR v) { + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::sqrt(v[i]); + } + return v; + } + + friend inline VECTOR MATH_PURE cbrt(VECTOR v) { + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::cbrt(v[i]); + } + return v; + } + + friend inline VECTOR MATH_PURE exp(VECTOR v) { + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::exp(v[i]); + } + return v; + } + + friend inline VECTOR MATH_PURE pow(VECTOR v, T p) { + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::pow(v[i], p); + } + return v; + } + + friend inline VECTOR MATH_PURE pow(T v, VECTOR p) { + for (size_t i = 0; i < p.size(); i++) { + p[i] = std::pow(v, p[i]); + } + return p; + } + + friend inline VECTOR MATH_PURE pow(VECTOR v, VECTOR p) { + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::pow(v[i], p[i]); + } + return v; + } + + friend inline VECTOR MATH_PURE log(VECTOR v) { + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::log(v[i]); + } + return v; + } + + friend inline VECTOR MATH_PURE log10(VECTOR v) { + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::log10(v[i]); + } + return v; + } + + friend inline VECTOR MATH_PURE log2(VECTOR v) { + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::log2(v[i]); + } + return v; + } + + friend inline constexpr VECTOR MATH_PURE saturate(const VECTOR& lv) { + return clamp(lv, T(0), T(1)); + } + + friend inline constexpr VECTOR MATH_PURE clamp(VECTOR v, T min, T max) { + for (size_t i = 0; i < v.size(); i++) { + v[i] = details::min(max, details::max(min, v[i])); + } + return v; + } + + friend inline constexpr VECTOR MATH_PURE clamp(VECTOR v, VECTOR min, VECTOR max) { + for (size_t i = 0; i < v.size(); i++) { + v[i] = details::min(max[i], details::max(min[i], v[i])); + } + return v; + } + + friend inline constexpr VECTOR MATH_PURE fma(const VECTOR& lv, const VECTOR& rv, + VECTOR a) { + for (size_t i = 0; i < lv.size(); i++) { + a[i] += (lv[i] * rv[i]); + } + return a; + } + + friend inline constexpr VECTOR MATH_PURE min(const VECTOR& u, VECTOR v) { + for (size_t i = 0; i < v.size(); i++) { + v[i] = details::min(u[i], v[i]); + } + return v; + } + + friend inline constexpr VECTOR MATH_PURE max(const VECTOR& u, VECTOR v) { + for (size_t i = 0; i < v.size(); i++) { + v[i] = details::max(u[i], v[i]); + } + return v; + } + + friend inline constexpr T MATH_PURE max(const VECTOR& v) { + T r(v[0]); + for (size_t i = 1; i < v.size(); i++) { + r = max(r, v[i]); + } + return r; + } + + friend inline constexpr T MATH_PURE min(const VECTOR& v) { + T r(v[0]); + for (size_t i = 1; i < v.size(); i++) { + r = min(r, v[i]); + } + return r; + } + + friend inline constexpr VECTOR MATH_PURE mix(const VECTOR& u, VECTOR v, T a) { + for (size_t i = 0; i < v.size(); i++) { + v[i] = u[i] * (T(1) - a) + v[i] * a; + } + return v; + } + + friend inline constexpr VECTOR MATH_PURE smoothstep(T edge0, T edge1, VECTOR v) { + VECTOR t = saturate((v - edge0) / (edge1 - edge0)); + return t * t * (T(3) - T(2) * t); + } + + friend inline constexpr VECTOR MATH_PURE step(T edge, VECTOR v) { + for (size_t i = 0; i < v.size(); i++) { + v[i] = v[i] < edge ? T(0) : T(1); + } + return v; + } + + friend inline constexpr VECTOR MATH_PURE step(VECTOR edge, VECTOR v) { + for (size_t i = 0; i < v.size(); i++) { + v[i] = v[i] < edge[i] ? T(0) : T(1); + } + return v; + } + + friend inline constexpr bool MATH_PURE any(const VECTOR& v) { + for (size_t i = 0; i < v.size(); i++) { + if (v[i] != T(0)) return true; + } + return false; + } + + friend inline constexpr bool MATH_PURE all(const VECTOR& v) { + bool result = true; + for (size_t i = 0; i < v.size(); i++) { + result &= (v[i] != T(0)); + } + return result; + } +}; + +// ------------------------------------------------------------------------------------- +} // namespace details +} // namespace math +} // namespace filament + +#endif // MATH_TVECHELPERS_H_ diff --git a/ios/include/math/compiler.h b/ios/include/math/compiler.h new file mode 100644 index 00000000..20934fae --- /dev/null +++ b/ios/include/math/compiler.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#if defined (WIN32) + +#ifdef max +#undef max +#endif + +#ifdef min +#undef min +#endif + +#ifdef far +#undef far +#endif + +#ifdef near +#undef near +#endif + +#endif + +// compatibility with non-clang compilers... +#ifndef __has_attribute +#define __has_attribute(x) 0 +#endif +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +#if __has_builtin(__builtin_expect) +# ifdef __cplusplus +# define MATH_LIKELY( exp ) (__builtin_expect( !!(exp), true )) +# define MATH_UNLIKELY( exp ) (__builtin_expect( !!(exp), false )) +# else +# define MATH_LIKELY( exp ) (__builtin_expect( !!(exp), 1 )) +# define MATH_UNLIKELY( exp ) (__builtin_expect( !!(exp), 0 )) +# endif +#else +# define MATH_LIKELY( exp ) (exp) +# define MATH_UNLIKELY( exp ) (exp) +#endif + +#if __has_attribute(unused) +# define MATH_UNUSED __attribute__((unused)) +#else +# define MATH_UNUSED +#endif + +#if __has_attribute(pure) +# define MATH_PURE __attribute__((pure)) +#else +# define MATH_PURE +#endif + +#ifdef _MSC_VER +# define MATH_EMPTY_BASES __declspec(empty_bases) + +// MSVC does not support loop unrolling hints +# define MATH_NOUNROLL + +// Sadly, MSVC does not support __builtin_constant_p +# ifndef MAKE_CONSTEXPR +# define MAKE_CONSTEXPR(e) (e) +# endif + +// About value initialization, the C++ standard says: +// if T is a class type with a default constructor that is neither user-provided nor deleted +// (that is, it may be a class with an implicitly-defined or defaulted default constructor), +// the object is zero-initialized and then it is default-initialized +// if it has a non-trivial default constructor; +// Unfortunately, MSVC always calls the default constructor, even if it is trivial, which +// breaks constexpr-ness. To workaround this, we're always zero-initializing TVecN<> +# define MATH_CONSTEXPR_INIT {} +# define MATH_DEFAULT_CTOR {} +# define MATH_DEFAULT_CTOR_CONSTEXPR constexpr +# define CONSTEXPR_IF_NOT_MSVC // when declared constexpr, msvc fails with "failure was caused by cast of object of dynamic type" + +#else // _MSC_VER + +# define MATH_EMPTY_BASES +// C++11 allows pragmas to be specified as part of defines using the _Pragma syntax. +# define MATH_NOUNROLL _Pragma("nounroll") + +# ifndef MAKE_CONSTEXPR +# define MAKE_CONSTEXPR(e) __builtin_constant_p(e) ? (e) : (e) +# endif + +# define MATH_CONSTEXPR_INIT +# define MATH_DEFAULT_CTOR = default; +# define MATH_DEFAULT_CTOR_CONSTEXPR +# define CONSTEXPR_IF_NOT_MSVC constexpr + +#endif // _MSC_VER + +namespace filament { +namespace math { + +// MSVC 2019 16.4 doesn't seem to like it when we specialize std::is_arithmetic for +// filament::math::half, so we're forced to create our own is_arithmetic here and specialize it +// inside of half.h. +template +struct is_arithmetic : std::integral_constant::value || std::is_floating_point::value> { +}; + +} +} diff --git a/ios/include/math/fast.h b/ios/include/math/fast.h new file mode 100644 index 00000000..7e1e55b0 --- /dev/null +++ b/ios/include/math/fast.h @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_MATH_FAST_H +#define TNT_MATH_FAST_H + +#include +#include +#include + +#include +#include + +#ifdef __ARM_NEON +#include +#endif + +namespace filament { +namespace math { +namespace fast { + +// fast cos(x), ~8 cycles (vs. 66 cycles on ARM) +// can be vectorized +// x between -pi and pi +template::value>> +constexpr T MATH_PURE cos(T x) noexcept { + x *= T(F_1_PI / 2); + x -= T(0.25) + std::floor(x + T(0.25)); + x *= T(16.0) * std::abs(x) - T(8.0); + x += T(0.225) * x * (std::abs(x) - T(1.0)); + return x; +} + +// fast sin(x), ~8 cycles (vs. 66 cycles on ARM) +// can be vectorized +// x between -pi and pi +template::value>> +constexpr T MATH_PURE sin(T x) noexcept { + return filament::math::fast::cos(x - T(F_PI_2)); +} + +constexpr inline float MATH_PURE ilog2(float x) noexcept { + union { + float val; + int32_t x; + } u = { x }; + return float(((u.x >> 23) & 0xff) - 127); +} + +constexpr inline float MATH_PURE log2(float x) noexcept { + union { + float val; + int32_t x; + } u = { x }; + float ilog2 = float(((u.x >> 23) & 0xff) - 128); + u.x = (u.x & 0x007fffff) | 0x3f800000; + return ilog2 + (-0.34484843f * u.val + 2.02466578f) * u.val - 0.67487759f; +} + +// fast 1/sqrt(), on ARMv8 this is 5 cycles vs. 7 cycles, so maybe not worth it. +// we keep this mostly for reference and benchmarking. +inline float MATH_PURE isqrt(float x) noexcept { +#if defined(__ARM_NEON) && defined(__aarch64__) + float y = vrsqrtes_f32(x); + return y * vrsqrtss_f32(x, y * y); +#else + return 1 / std::sqrt(x); +#endif +} + +inline double MATH_PURE isqrt(double x) noexcept { +#if defined(__ARM_NEON) && defined(__aarch64__) + double y = vrsqrted_f64(x); + return y * vrsqrtsd_f64(x, y * y); +#else + return 1 / std::sqrt(x); +#endif +} + +inline int signbit(float x) noexcept { +#if __has_builtin(__builtin_signbitf) + // Note: on Android NDK, signbit() is a function call -- not what we want. + return __builtin_signbitf(x); +#else + return std::signbit(x); +#endif +} + +/* + * constexpr exp(), pow(), factorial() + */ + +constexpr double pow(double x, unsigned int y) noexcept { + return y == 0 ? 1.0 : x * pow(x, y - 1); +} + +constexpr unsigned int factorial(unsigned int x) noexcept { + return x == 0 ? 1 : x * factorial(x - 1); +} + +constexpr double exp(double x) noexcept { + return 1.0 + x + pow(x, 2) / factorial(2) + pow(x, 3) / factorial(3) + + pow(x, 4) / factorial(4) + pow(x, 5) / factorial(5) + + pow(x, 6) / factorial(6) + pow(x, 7) / factorial(7) + + pow(x, 8) / factorial(8) + pow(x, 9) / factorial(9); +} + +constexpr float exp(float x) noexcept { + return float(exp(double(x))); +} + +/* + * unsigned saturated arithmetic + */ + +#if defined(__ARM_NEON) && defined(__aarch64__) +inline uint8_t MATH_PURE qadd(uint8_t a, uint8_t b) noexcept { return vuqaddb_s8(a, b); } +inline uint16_t MATH_PURE qadd(uint16_t a, uint16_t b) noexcept { return vuqaddh_s16(a, b); } +inline uint32_t MATH_PURE qadd(uint32_t a, uint32_t b) noexcept { return vuqadds_s32(a, b); } + +inline uint8_t MATH_PURE qsub(uint8_t a, uint8_t b) noexcept { return vqsubb_s8(a, b); } +inline uint16_t MATH_PURE qsub(uint16_t a, uint16_t b) noexcept { return vqsubh_s16(a, b); } +inline uint32_t MATH_PURE qsub(uint32_t a, uint32_t b) noexcept { return vqsubs_s32(a, b); } +#else + +template::value || + std::is_same::value || + std::is_same::value>> +inline T MATH_PURE qadd(T a, T b) noexcept { + T r = a + b; + return r | -T(r < a); +} + +template::value || + std::is_same::value || + std::is_same::value>> +inline T MATH_PURE qsub(T a, T b) noexcept { + T r = a - b; + return r & -T(r <= a); +} + +#endif + +template +inline T MATH_PURE qinc(T a) noexcept { + return qadd(a, T(1)); +} + +template +inline T MATH_PURE qdec(T a) noexcept { + return qsub(a, T(1)); +} + + +} // namespace fast +} // namespace math +} // namespace filament + +#endif // TNT_MATH_FAST_H diff --git a/ios/include/math/half.h b/ios/include/math/half.h new file mode 100644 index 00000000..72a43be7 --- /dev/null +++ b/ios/include/math/half.h @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_MATH_HALF_H +#define TNT_MATH_HALF_H + +#include +#include + +#include +#include + +#include + +namespace filament { +namespace math { + +template +class fp { + static_assert(S + E + M <= 16, "we only support 16-bits max custom floats"); + + using TYPE = uint16_t; // this should be dynamic + + static constexpr unsigned S_SHIFT = E + M; + static constexpr unsigned E_SHIFT = M; + static constexpr unsigned M_SHIFT = 0; + static constexpr unsigned S_MASK = ((1u << S) - 1u) << S_SHIFT; + static constexpr unsigned E_MASK = ((1u << E) - 1u) << E_SHIFT; + static constexpr unsigned M_MASK = ((1u << M) - 1u) << M_SHIFT; + + struct fp32 { + explicit constexpr fp32(float f) noexcept : fp(f) { } // NOLINT + explicit constexpr fp32(uint32_t b) noexcept : bits(b) { } // NOLINT + constexpr void setS(unsigned int s) noexcept { + bits = uint32_t((bits & 0x7FFFFFFFu) | (s << 31u)); + } + constexpr unsigned int getS() const noexcept { return bits >> 31u; } + constexpr unsigned int getE() const noexcept { return (bits >> 23u) & 0xFFu; } + constexpr unsigned int getM() const noexcept { return bits & 0x7FFFFFu; } + union { + uint32_t bits; + float fp; + }; + }; + +public: + static constexpr fp fromf(float f) noexcept { + fp out; + if (S == 0 && f < 0.0f) { + return out; + } + + fp32 in(f); + unsigned int sign = in.getS(); + in.setS(0); + if (MATH_UNLIKELY(in.getE() == 0xFF)) { // inf or nan + out.setE((1u << E) - 1u); + out.setM(in.getM() ? (1u << (M - 1u)) : 0); + } else { + constexpr fp32 infinity(((1u << E) - 1u) << 23u); // fp infinity in fp32 position + constexpr fp32 magic(((1u << (E - 1u)) - 1u) << 23u); // exponent offset + in.bits &= ~((1u << (22 - M)) - 1u); // erase extra mantissa bits + in.bits += 1u << (22 - M); // rounding + in.fp *= magic.fp; // add exponent offset + in.bits = in.bits < infinity.bits ? in.bits : infinity.bits; + out.bits = uint16_t(in.bits >> (23 - M)); + } + out.setS(sign); + return out; + } + + static constexpr float tof(fp in) noexcept { + constexpr fp32 magic ((0xFE - ((1u << (E - 1u)) - 1u)) << 23u); + constexpr fp32 infnan((0x80 + ((1u << (E - 1u)) - 1u)) << 23u); + fp32 out((in.bits & ((1u << (E + M)) - 1u)) << (23u - M)); + out.fp *= magic.fp; + if (out.fp >= infnan.fp) { + out.bits |= 0xFFu << 23u; + } + out.bits |= (in.bits & S_MASK) << (31u - S_SHIFT); + return out.fp; + } + + TYPE bits{}; + static constexpr size_t getBitCount() noexcept { return S + E + M; } + constexpr fp() noexcept = default; + explicit constexpr fp(TYPE bits) noexcept : bits(bits) { } + constexpr void setS(unsigned int s) noexcept { bits = TYPE((bits & ~S_MASK) | (s << S_SHIFT)); } + constexpr void setE(unsigned int s) noexcept { bits = TYPE((bits & ~E_MASK) | (s << E_SHIFT)); } + constexpr void setM(unsigned int s) noexcept { bits = TYPE((bits & ~M_MASK) | (s << M_SHIFT)); } + constexpr unsigned int getS() const noexcept { return (bits & S_MASK) >> S_SHIFT; } + constexpr unsigned int getE() const noexcept { return (bits & E_MASK) >> E_SHIFT; } + constexpr unsigned int getM() const noexcept { return (bits & M_MASK) >> M_SHIFT; } +}; + +/* + * half-float + * + * 1 5 10 + * +-+------+------------+ + * |s|eee.ee|mm.mmmm.mmmm| + * +-+------+------------+ + * + * minimum (denormal) value: 2^-24 = 5.96e-8 + * minimum (normal) value: 2^-14 = 6.10e-5 + * maximum value: (2 - 2^-10) * 2^15 = 65504 + * + * Integers between 0 and 2048 can be represented exactly + */ + +#ifdef __ARM_NEON + +using half = __fp16; + +inline constexpr uint16_t getBits(half const& h) noexcept { + return MAKE_CONSTEXPR(reinterpret_cast(h)); +} + +inline constexpr half makeHalf(uint16_t bits) noexcept { + return MAKE_CONSTEXPR(reinterpret_cast(bits)); +} + +#else + +class half { + using fp16 = fp<1, 5, 10>; + +public: + half() = default; + constexpr half(float v) noexcept : mBits(fp16::fromf(v)) { } // NOLINT + constexpr operator float() const noexcept { return fp16::tof(mBits); } // NOLINT + +private: + // these are friends, not members (and they're not "private") + friend constexpr uint16_t getBits(half const& h) noexcept { return h.mBits.bits; } + friend constexpr inline half makeHalf(uint16_t bits) noexcept; + + enum Binary { binary }; + explicit constexpr half(Binary, uint16_t bits) noexcept : mBits(bits) { } + + fp16 mBits; +}; + +constexpr inline half makeHalf(uint16_t bits) noexcept { + return half(half::binary, bits); +} + +#endif // __ARM_NEON + +inline constexpr half operator "" _h(long double v) { + return half( static_cast(v) ); +} + +template<> struct is_arithmetic : public std::true_type {}; + +} // namespace math +} // namespace filament + +namespace std { + +template<> struct is_floating_point : public std::true_type {}; + +// note: this shouldn't be needed (is_floating_point<> is enough) but some version of msvc need it +// This stopped working with MSVC 2019 16.4, so we specialize our own version of is_arithmetic in +// the math::filament namespace (see above). +template<> struct is_arithmetic : public std::true_type {}; + +template<> +class numeric_limits { +public: + typedef filament::math::half type; + + static constexpr const bool is_specialized = true; + static constexpr const bool is_signed = true; + static constexpr const bool is_integer = false; + static constexpr const bool is_exact = false; + static constexpr const bool has_infinity = true; + static constexpr const bool has_quiet_NaN = true; + static constexpr const bool has_signaling_NaN = false; + static constexpr const float_denorm_style has_denorm = denorm_absent; + static constexpr const bool has_denorm_loss = true; + static constexpr const bool is_iec559 = false; + static constexpr const bool is_bounded = true; + static constexpr const bool is_modulo = false; + static constexpr const bool traps = false; + static constexpr const bool tinyness_before = false; + static constexpr const float_round_style round_style = round_indeterminate; + + static constexpr const int digits = 11; + static constexpr const int digits10 = 3; + static constexpr const int max_digits10 = 5; + static constexpr const int radix = 2; + static constexpr const int min_exponent = -13; + static constexpr const int min_exponent10 = -4; + static constexpr const int max_exponent = 16; + static constexpr const int max_exponent10 = 4; + + inline static constexpr type round_error() noexcept { return filament::math::makeHalf(0x3800); } + inline static constexpr type min() noexcept { return filament::math::makeHalf(0x0400); } + inline static constexpr type max() noexcept { return filament::math::makeHalf(0x7bff); } + inline static constexpr type lowest() noexcept { return filament::math::makeHalf(0xfbff); } + inline static constexpr type epsilon() noexcept { return filament::math::makeHalf(0x1400); } + inline static constexpr type infinity() noexcept { return filament::math::makeHalf(0x7c00); } + inline static constexpr type quiet_NaN() noexcept { return filament::math::makeHalf(0x7fff); } + inline static constexpr type denorm_min() noexcept { return filament::math::makeHalf(0x0001); } + inline static constexpr type signaling_NaN() noexcept { return filament::math::makeHalf(0x7dff); } +}; + +} // namespace std + +#endif // TNT_MATH_HALF_H diff --git a/ios/include/math/mat2.h b/ios/include/math/mat2.h new file mode 100644 index 00000000..b4857cef --- /dev/null +++ b/ios/include/math/mat2.h @@ -0,0 +1,361 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MATH_MAT2_H_ +#define MATH_MAT2_H_ + +#include +#include +#include +#include +#include + +namespace filament { +namespace math { +// ------------------------------------------------------------------------------------- +namespace details { + +/** + * A 2x2 column-major matrix class. + * + * Conceptually a 2x2 matrix is a an array of 2 column vec2: + * + * mat2 m = + * \f$ + * \left( + * \begin{array}{cc} + * m[0] & m[1] \\ + * \end{array} + * \right) + * \f$ + * = + * \f$ + * \left( + * \begin{array}{cc} + * m[0][0] & m[1][0] \\ + * m[0][1] & m[1][1] \\ + * \end{array} + * \right) + * \f$ + * = + * \f$ + * \left( + * \begin{array}{cc} + * m(0,0) & m(0,1) \\ + * m(1,0) & m(1,1) \\ + * \end{array} + * \right) + * \f$ + * + * m[n] is the \f$ n^{th} \f$ column of the matrix and is a vec2. + * + */ +template +class MATH_EMPTY_BASES TMat22 : + public TVecUnaryOperators, + public TVecComparisonOperators, + public TVecAddOperators, + public TMatProductOperators, + public TMatSquareFunctions, + public TMatHelpers { +public: + enum no_init { + NO_INIT + }; + typedef T value_type; + typedef T& reference; + typedef T const& const_reference; + typedef size_t size_type; + typedef TVec2 col_type; + typedef TVec2 row_type; + + static constexpr size_t COL_SIZE = col_type::SIZE; // size of a column (i.e.: number of rows) + static constexpr size_t ROW_SIZE = row_type::SIZE; // size of a row (i.e.: number of columns) + static constexpr size_t NUM_ROWS = COL_SIZE; + static constexpr size_t NUM_COLS = ROW_SIZE; + +private: + /* + * <-- N columns --> + * + * a[0][0] a[1][0] a[2][0] ... a[N][0] ^ + * a[0][1] a[1][1] a[2][1] ... a[N][1] | + * a[0][2] a[1][2] a[2][2] ... a[N][2] M rows + * ... | + * a[0][M] a[1][M] a[2][M] ... a[N][M] v + * + * COL_SIZE = M + * ROW_SIZE = N + * m[0] = [ a[0][0] a[0][1] a[0][2] ... a[0][M] ] + */ + + col_type m_value[NUM_COLS]; + +public: + // array access + inline constexpr col_type const& operator[](size_t column) const noexcept { + assert(column < NUM_COLS); + return m_value[column]; + } + + inline constexpr col_type& operator[](size_t column) noexcept { + assert(column < NUM_COLS); + return m_value[column]; + } + + /** + * constructors + */ + + /** + * leaves object uninitialized. use with caution. + */ + constexpr explicit TMat22(no_init) noexcept {} + + + /** + * initialize to identity. + * + * \f$ + * \left( + * \begin{array}{cc} + * 1 & 0 \\ + * 0 & 1 \\ + * \end{array} + * \right) + * \f$ + */ + constexpr TMat22() noexcept ; + + /** + * initialize to Identity*scalar. + * + * \f$ + * \left( + * \begin{array}{cc} + * v & 0 \\ + * 0 & v \\ + * \end{array} + * \right) + * \f$ + */ + template + constexpr explicit TMat22(U v) noexcept; + + /** + * sets the diagonal to a vector. + * + * \f$ + * \left( + * \begin{array}{cc} + * v[0] & 0 \\ + * 0 & v[1] \\ + * \end{array} + * \right) + * \f$ + */ + template + constexpr explicit TMat22(const TVec2& v) noexcept; + + /** + * construct from another matrix of the same size + */ + template + constexpr explicit TMat22(const TMat22& rhs) noexcept; + + /** + * construct from 2 column vectors. + * + * \f$ + * \left( + * \begin{array}{cc} + * v0 & v1 \\ + * \end{array} + * \right) + * \f$ + */ + template + constexpr TMat22(const TVec2& v0, const TVec2& v1) noexcept; + + /** construct from 4 elements in column-major form. + * + * \f$ + * \left( + * \begin{array}{cc} + * m[0][0] & m[1][0] \\ + * m[0][1] & m[1][1] \\ + * \end{array} + * \right) + * \f$ + */ + template< + typename A, typename B, + typename C, typename D> + constexpr explicit TMat22(A m00, B m01, C m10, D m11) noexcept ; + + + struct row_major_init { + template + constexpr explicit row_major_init(A m00, B m01, C m10, D m11) noexcept + : m(m00, m10, m01, m11) {} + + private: + friend TMat22; + TMat22 m; + }; + + constexpr explicit TMat22(row_major_init c) noexcept : TMat22(std::move(c.m)) {} + + /** + * Rotate by radians in the 2D plane + */ + static TMat22 rotate(T radian) noexcept { + TMat22 r(TMat22::NO_INIT); + T c = std::cos(radian); + T s = std::sin(radian); + r[0][0] = c; + r[1][1] = c; + r[0][1] = s; + r[1][0] = -s; + return r; + } + + // returns false if the two matrices are different. May return false if they're the + // same, with some elements only differing by +0 or -0. Behaviour is undefined with NaNs. + static constexpr bool fuzzyEqual(TMat22 l, TMat22 r) noexcept { + uint64_t const* const li = reinterpret_cast(&l); + uint64_t const* const ri = reinterpret_cast(&r); + uint64_t result = 0; + // For some reason clang is not able to vectoize this loop when the number of iteration + // is known and constant (!?!?!). Still this is better than operator==. +#pragma clang loop vectorize_width(2) + for (size_t i = 0; i < sizeof(TMat22) / sizeof(uint64_t); i++) { + result |= li[i] ^ ri[i]; + } + return result != 0; + } + + template + static constexpr TMat22 translation(const TVec2& t) noexcept { + TMat22 r; + r[2] = t; + return r; + } + + template + static constexpr TMat22 scaling(const TVec2& s) noexcept { + return TMat22{ s }; + } + + template + static constexpr TMat22 scaling(A s) noexcept { + return TMat22{ TVec2{ s, s }}; + } +}; + +// ---------------------------------------------------------------------------------------- +// Constructors +// ---------------------------------------------------------------------------------------- + +// Since the matrix code could become pretty big quickly, we don't inline most +// operations. + +template +constexpr TMat22::TMat22() noexcept + : m_value{ col_type(1, 0), col_type(0, 1) } { +} + +template +template +constexpr TMat22::TMat22(U v) noexcept + : m_value{ col_type(v, 0), col_type(0, v) } { +} + +template +template +constexpr TMat22::TMat22(const TVec2& v) noexcept + : m_value{ col_type(v[0], 0), col_type(0, v[1]) } { +} + +// construct from 4 scalars. Note that the arrangement +// of values in the constructor is the transpose of the matrix +// notation. +template +template +constexpr TMat22::TMat22(A m00, B m01, C m10, D m11) noexcept + : m_value{ col_type(m00, m01), col_type(m10, m11) } { +} + +template +template +constexpr TMat22::TMat22(const TMat22& rhs) noexcept { + for (size_t col = 0; col < NUM_COLS; ++col) { + m_value[col] = col_type(rhs[col]); + } +} + +// Construct from 2 column vectors. +template +template +constexpr TMat22::TMat22(const TVec2& v0, const TVec2& v1) noexcept + : m_value{ v0, v1 } { +} + +} // namespace details + +// ---------------------------------------------------------------------------------------- + +typedef details::TMat22 mat2; +typedef details::TMat22 mat2f; + +// ---------------------------------------------------------------------------------------- +} // namespace math +} // namespace filament + +namespace std { +template +constexpr void swap(filament::math::details::TMat22& lhs, + filament::math::details::TMat22& rhs) noexcept { + // This generates much better code than the default implementation + // It's unclear why, I believe this is due to an optimization bug in the clang. + // + // filament::math::details::TMat22 t(lhs); + // lhs = rhs; + // rhs = t; + // + // clang always copy lhs on the stack, even if it's never using it (it's using the + // copy it has in registers). + + const T t00 = lhs[0][0]; + const T t01 = lhs[0][1]; + const T t10 = lhs[1][0]; + const T t11 = lhs[1][1]; + + lhs[0][0] = rhs[0][0]; + lhs[0][1] = rhs[0][1]; + lhs[1][0] = rhs[1][0]; + lhs[1][1] = rhs[1][1]; + + rhs[0][0] = t00; + rhs[0][1] = t01; + rhs[1][0] = t10; + rhs[1][1] = t11; +} +} + +#endif // MATH_MAT2_H_ diff --git a/ios/include/math/mat3.h b/ios/include/math/mat3.h new file mode 100644 index 00000000..b854b099 --- /dev/null +++ b/ios/include/math/mat3.h @@ -0,0 +1,490 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MATH_MAT3_H_ +#define MATH_MAT3_H_ + +#include +#include +#include +#include + +#include +#include +#include + +namespace filament { +namespace math { +// ------------------------------------------------------------------------------------- +namespace details { + +/** + * A 3x3 column-major matrix class. + * + * Conceptually a 3x3 matrix is a an array of 3 column vec3: + * + * mat3 m = + * \f$ + * \left( + * \begin{array}{ccc} + * m[0] & m[1] & m[2] \\ + * \end{array} + * \right) + * \f$ + * = + * \f$ + * \left( + * \begin{array}{ccc} + * m[0][0] & m[1][0] & m[2][0] \\ + * m[0][1] & m[1][1] & m[2][1] \\ + * m[0][2] & m[1][2] & m[2][2] \\ + * \end{array} + * \right) + * \f$ + * = + * \f$ + * \left( + * \begin{array}{ccc} + * m(0,0) & m(0,1) & m(0,2) \\ + * m(1,0) & m(1,1) & m(1,2) \\ + * m(2,0) & m(2,1) & m(2,2) \\ + * \end{array} + * \right) + * \f$ + * + * m[n] is the \f$ n^{th} \f$ column of the matrix and is a vec3. + * + */ +template +class MATH_EMPTY_BASES TMat33 : + public TVecUnaryOperators, + public TVecComparisonOperators, + public TVecAddOperators, + public TMatProductOperators, + public TMatSquareFunctions, + public TMatTransform, + public TMatHelpers { +public: + enum no_init { + NO_INIT + }; + typedef T value_type; + typedef T& reference; + typedef T const& const_reference; + typedef size_t size_type; + typedef TVec3 col_type; + typedef TVec3 row_type; + + static constexpr size_t COL_SIZE = col_type::SIZE; // size of a column (i.e.: number of rows) + static constexpr size_t ROW_SIZE = row_type::SIZE; // size of a row (i.e.: number of columns) + static constexpr size_t NUM_ROWS = COL_SIZE; + static constexpr size_t NUM_COLS = ROW_SIZE; + +private: + /* + * <-- N columns --> + * + * a[0][0] a[1][0] a[2][0] ... a[N][0] ^ + * a[0][1] a[1][1] a[2][1] ... a[N][1] | + * a[0][2] a[1][2] a[2][2] ... a[N][2] M rows + * ... | + * a[0][M] a[1][M] a[2][M] ... a[N][M] v + * + * COL_SIZE = M + * ROW_SIZE = N + * m[0] = [ a[0][0] a[0][1] a[0][2] ... a[0][M] ] + */ + + col_type m_value[NUM_COLS]; + +public: + // array access + inline constexpr col_type const& operator[](size_t column) const noexcept { + assert(column < NUM_COLS); + return m_value[column]; + } + + inline constexpr col_type& operator[](size_t column) noexcept { + assert(column < NUM_COLS); + return m_value[column]; + } + + /** + * constructors + */ + + /** + * leaves object uninitialized. use with caution. + */ + constexpr explicit TMat33(no_init) noexcept {} + + + /** + * initialize to identity. + * + * \f$ + * \left( + * \begin{array}{ccc} + * 1 & 0 & 0 \\ + * 0 & 1 & 0 \\ + * 0 & 0 & 1 \\ + * \end{array} + * \right) + * \f$ + */ + constexpr TMat33() noexcept; + + /** + * initialize to Identity*scalar. + * + * \f$ + * \left( + * \begin{array}{ccc} + * v & 0 & 0 \\ + * 0 & v & 0 \\ + * 0 & 0 & v \\ + * \end{array} + * \right) + * \f$ + */ + template + constexpr explicit TMat33(U v) noexcept; + + /** + * sets the diagonal to a vector. + * + * \f$ + * \left( + * \begin{array}{ccc} + * v[0] & 0 & 0 \\ + * 0 & v[1] & 0 \\ + * 0 & 0 & v[2] \\ + * \end{array} + * \right) + * \f$ + */ + template + constexpr explicit TMat33(const TVec3& v) noexcept; + + /** + * construct from another matrix of the same size + */ + template + constexpr explicit TMat33(const TMat33& rhs) noexcept; + + /** + * construct from 3 column vectors. + * + * \f$ + * \left( + * \begin{array}{ccc} + * v0 & v1 & v2 \\ + * \end{array} + * \right) + * \f$ + */ + template + constexpr TMat33(const TVec3& v0, const TVec3& v1, const TVec3& v2) noexcept; + + /** construct from 9 elements in column-major form. + * + * \f$ + * \left( + * \begin{array}{ccc} + * m[0][0] & m[1][0] & m[2][0] \\ + * m[0][1] & m[1][1] & m[2][1] \\ + * m[0][2] & m[1][2] & m[2][2] \\ + * \end{array} + * \right) + * \f$ + */ + template< + typename A, typename B, typename C, + typename D, typename E, typename F, + typename G, typename H, typename I> + constexpr explicit TMat33(A m00, B m01, C m02, + D m10, E m11, F m12, + G m20, H m21, I m22) noexcept; + + + struct row_major_init { + template< + typename A, typename B, typename C, + typename D, typename E, typename F, + typename G, typename H, typename I> + constexpr explicit row_major_init(A m00, B m01, C m02, + D m10, E m11, F m12, + G m20, H m21, I m22) noexcept + : m(m00, m10, m20, + m01, m11, m21, + m02, m12, m22) {} + + private: + friend TMat33; + TMat33 m; + }; + + constexpr explicit TMat33(row_major_init c) noexcept : TMat33(std::move(c.m)) {} + + /** + * construct from a quaternion + */ + template + constexpr explicit TMat33(const TQuaternion& q) noexcept; + + /** + * orthogonalize only works on matrices of size 3x3 + */ + friend inline + constexpr TMat33 orthogonalize(const TMat33& m) noexcept { + TMat33 ret(TMat33::NO_INIT); + ret[0] = normalize(m[0]); + ret[2] = normalize(cross(ret[0], m[1])); + ret[1] = normalize(cross(ret[2], ret[0])); + return ret; + } + + /** + * Returns a matrix suitable for transforming normals + * + * Note that the inverse-transpose of a matrix is equal to its cofactor matrix divided by its + * determinant: + * + * transpose(inverse(M)) = cof(M) / det(M) + * + * The cofactor matrix is faster to compute than the inverse-transpose, and it can be argued + * that it is a more correct way of transforming normals anyway. Some references from Dale + * Weiler, Nathan Reed, Inigo Quilez, and Eric Lengyel: + * + * - https://github.com/graphitemaster/normals_revisited + * - http://www.reedbeta.com/blog/normals-inverse-transpose-part-1/ + * - https://www.shadertoy.com/view/3s33zj + * - FGED Volume 1, section 1.7.5 "Inverses of Small Matrices" + * - FGED Volume 1, section 3.2.2 "Transforming Normal Vectors" + * + * In "Transforming Normal Vectors", Lengyel notes that there are two types of transformed + * normals: one that uses the transposed adjugate (aka cofactor matrix) and one that uses the + * transposed inverse. He goes on to say that this difference is inconsequential, except when + * mirroring is involved. + * + * @param m the transform applied to vertices + * @return a matrix to apply to normals + * + * @warning normals transformed by this matrix must be normalized + */ + static constexpr TMat33 getTransformForNormals(const TMat33& m) noexcept { + return matrix::cof(m); + } + + /** + * Packs the tangent frame represented by the specified matrix into a quaternion. + * Reflection is preserved by encoding it as the sign of the w component in the + * resulting quaternion. Since -0 cannot always be represented on the GPU, this + * function computes a bias to ensure values are always either positive or negative, + * never 0. The bias is computed based on the specified storageSize, which defaults + * to 2 bytes, making the resulting quaternion suitable for storage into an SNORM16 + * vector. + */ + static constexpr TQuaternion packTangentFrame( + const TMat33& m, size_t storageSize = sizeof(int16_t)) noexcept; + + template + static constexpr TMat33 translation(const TVec3& t) noexcept { + TMat33 r; + r[2] = t; + return r; + } + + template + static constexpr TMat33 scaling(const TVec3& s) noexcept { + return TMat33{ s }; + } + + template + static constexpr TMat33 scaling(A s) noexcept { + return TMat33{ TVec3{ s }}; + } +}; + +// ---------------------------------------------------------------------------------------- +// Constructors +// ---------------------------------------------------------------------------------------- + +// Since the matrix code could become pretty big quickly, we don't inline most +// operations. + +template +constexpr TMat33::TMat33() noexcept + : m_value{ + col_type(1, 0, 0), + col_type(0, 1, 0), + col_type(0, 0, 1) } { +} + +template +template +constexpr TMat33::TMat33(U v) noexcept + : m_value{ + col_type(v, 0, 0), + col_type(0, v, 0), + col_type(0, 0, v) } { +} + +template +template +constexpr TMat33::TMat33(const TVec3& v) noexcept + : m_value{ + col_type(v[0], 0, 0), + col_type(0, v[1], 0), + col_type(0, 0, v[2]) } { +} + +// construct from 16 scalars. Note that the arrangement +// of values in the constructor is the transpose of the matrix +// notation. +template +template< + typename A, typename B, typename C, + typename D, typename E, typename F, + typename G, typename H, typename I> +constexpr TMat33::TMat33(A m00, B m01, C m02, + D m10, E m11, F m12, + G m20, H m21, I m22) noexcept + : m_value{ + col_type(m00, m01, m02), + col_type(m10, m11, m12), + col_type(m20, m21, m22) } { +} + +template +template +constexpr TMat33::TMat33(const TMat33& rhs) noexcept { + for (size_t col = 0; col < NUM_COLS; ++col) { + m_value[col] = col_type(rhs[col]); + } +} + +// Construct from 3 column vectors. +template +template +constexpr TMat33::TMat33(const TVec3& v0, const TVec3& v1, const TVec3& v2) noexcept + : m_value{ v0, v1, v2 } { +} + +template +template +constexpr TMat33::TMat33(const TQuaternion& q) noexcept : m_value{} { + const U n = q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w; + const U s = n > 0 ? 2 / n : 0; + const U x = s * q.x; + const U y = s * q.y; + const U z = s * q.z; + const U xx = x * q.x; + const U xy = x * q.y; + const U xz = x * q.z; + const U xw = x * q.w; + const U yy = y * q.y; + const U yz = y * q.z; + const U yw = y * q.w; + const U zz = z * q.z; + const U zw = z * q.w; + m_value[0] = col_type(1 - yy - zz, xy + zw, xz - yw); // NOLINT + m_value[1] = col_type(xy - zw, 1 - xx - zz, yz + xw); // NOLINT + m_value[2] = col_type(xz + yw, yz - xw, 1 - xx - yy); // NOLINT +} + +//------------------------------------------------------------------------------ +template +constexpr TQuaternion TMat33::packTangentFrame(const TMat33& m, size_t storageSize) noexcept { + TQuaternion q = TMat33{ m[0], cross(m[2], m[0]), m[2] }.toQuaternion(); + q = positive(normalize(q)); + + // Ensure w is never 0.0 + // Bias is 2^(nb_bits - 1) - 1 + const T bias = T(1.0) / T((1 << (storageSize * CHAR_BIT - 1)) - 1); + if (q.w < bias) { + q.w = bias; + + const T factor = (T)(std::sqrt(1.0 - (double)bias * (double)bias)); + q.xyz *= factor; + } + + // If there's a reflection ((n x t) . b <= 0), make sure w is negative + if (dot(cross(m[0], m[2]), m[1]) < T(0)) { + q = -q; + } + + return q; +} + +} // namespace details + +// ---------------------------------------------------------------------------------------- + +typedef details::TMat33 mat3; +typedef details::TMat33 mat3f; + +// ---------------------------------------------------------------------------------------- +} // namespace math +} // namespace filament + +namespace std { +template +constexpr void swap(filament::math::details::TMat33& lhs, + filament::math::details::TMat33& rhs) noexcept { + // This generates much better code than the default implementation + // It's unclear why, I believe this is due to an optimization bug in the clang. + // + // filament::math::details::TMat33 t(lhs); + // lhs = rhs; + // rhs = t; + // + // clang always copy lhs on the stack, even if it's never using it (it's using the + // copy it has in registers). + + const T t00 = lhs[0][0]; + const T t01 = lhs[0][1]; + const T t02 = lhs[0][2]; + const T t10 = lhs[1][0]; + const T t11 = lhs[1][1]; + const T t12 = lhs[1][2]; + const T t20 = lhs[2][0]; + const T t21 = lhs[2][1]; + const T t22 = lhs[2][2]; + + lhs[0][0] = rhs[0][0]; + lhs[0][1] = rhs[0][1]; + lhs[0][2] = rhs[0][2]; + lhs[1][0] = rhs[1][0]; + lhs[1][1] = rhs[1][1]; + lhs[1][2] = rhs[1][2]; + lhs[2][0] = rhs[2][0]; + lhs[2][1] = rhs[2][1]; + lhs[2][2] = rhs[2][2]; + + rhs[0][0] = t00; + rhs[0][1] = t01; + rhs[0][2] = t02; + rhs[1][0] = t10; + rhs[1][1] = t11; + rhs[1][2] = t12; + rhs[2][0] = t20; + rhs[2][1] = t21; + rhs[2][2] = t22; +} +} + +#endif // MATH_MAT3_H_ diff --git a/ios/include/math/mat4.h b/ios/include/math/mat4.h new file mode 100644 index 00000000..d3c52da9 --- /dev/null +++ b/ios/include/math/mat4.h @@ -0,0 +1,631 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MATH_MAT4_H_ +#define MATH_MAT4_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace filament { +namespace math { +// ------------------------------------------------------------------------------------- +namespace details { + +template +class TQuaternion; + +/** + * A 4x4 column-major matrix class. + * + * Conceptually a 4x4 matrix is a an array of 4 column double4: + * + * mat4 m = + * \f$ + * \left( + * \begin{array}{cccc} + * m[0] & m[1] & m[2] & m[3] \\ + * \end{array} + * \right) + * \f$ + * = + * \f$ + * \left( + * \begin{array}{cccc} + * m[0][0] & m[1][0] & m[2][0] & m[3][0] \\ + * m[0][1] & m[1][1] & m[2][1] & m[3][1] \\ + * m[0][2] & m[1][2] & m[2][2] & m[3][2] \\ + * m[0][3] & m[1][3] & m[2][3] & m[3][3] \\ + * \end{array} + * \right) + * \f$ + * = + * \f$ + * \left( + * \begin{array}{cccc} + * m(0,0) & m(0,1) & m(0,2) & m(0,3) \\ + * m(1,0) & m(1,1) & m(1,2) & m(1,3) \\ + * m(2,0) & m(2,1) & m(2,2) & m(2,3) \\ + * m(3,0) & m(3,1) & m(3,2) & m(3,3) \\ + * \end{array} + * \right) + * \f$ + * + * m[n] is the \f$ n^{th} \f$ column of the matrix and is a double4. + * + */ +template +class MATH_EMPTY_BASES TMat44 : + public TVecUnaryOperators, + public TVecComparisonOperators, + public TVecAddOperators, + public TMatProductOperators, + public TMatSquareFunctions, + public TMatTransform, + public TMatHelpers { +public: + enum no_init { + NO_INIT + }; + typedef T value_type; + typedef T& reference; + typedef T const& const_reference; + typedef size_t size_type; + typedef TVec4 col_type; + typedef TVec4 row_type; + + static constexpr size_t COL_SIZE = col_type::SIZE; // size of a column (i.e.: number of rows) + static constexpr size_t ROW_SIZE = row_type::SIZE; // size of a row (i.e.: number of columns) + static constexpr size_t NUM_ROWS = COL_SIZE; + static constexpr size_t NUM_COLS = ROW_SIZE; + +private: + /* + * <-- N columns --> + * + * a[0][0] a[1][0] a[2][0] ... a[N][0] ^ + * a[0][1] a[1][1] a[2][1] ... a[N][1] | + * a[0][2] a[1][2] a[2][2] ... a[N][2] M rows + * ... | + * a[0][M] a[1][M] a[2][M] ... a[N][M] v + * + * COL_SIZE = M + * ROW_SIZE = N + * m[0] = [ a[0][0] a[0][1] a[0][2] ... a[0][M] ] + */ + + col_type m_value[NUM_COLS]; + +public: + // array access + inline constexpr col_type const& operator[](size_t column) const noexcept { + assert(column < NUM_COLS); + return m_value[column]; + } + + inline constexpr col_type& operator[](size_t column) noexcept { + assert(column < NUM_COLS); + return m_value[column]; + } + + /* + * constructors + */ + + // leaves object uninitialized. use with caution. + constexpr explicit TMat44(no_init) noexcept {} + + /** initialize to identity. + * + * \f$ + * \left( + * \begin{array}{cccc} + * 1 & 0 & 0 & 0 \\ + * 0 & 1 & 0 & 0 \\ + * 0 & 0 & 1 & 0 \\ + * 0 & 0 & 0 & 1 \\ + * \end{array} + * \right) + * \f$ + */ + constexpr TMat44() noexcept; + + /** initialize to Identity*scalar. + * + * \f$ + * \left( + * \begin{array}{cccc} + * v & 0 & 0 & 0 \\ + * 0 & v & 0 & 0 \\ + * 0 & 0 & v & 0 \\ + * 0 & 0 & 0 & v \\ + * \end{array} + * \right) + * \f$ + */ + template + constexpr explicit TMat44(U v) noexcept; + + /** sets the diagonal to a vector. + * + * \f$ + * \left( + * \begin{array}{cccc} + * v[0] & 0 & 0 & 0 \\ + * 0 & v[1] & 0 & 0 \\ + * 0 & 0 & v[2] & 0 \\ + * 0 & 0 & 0 & v[3] \\ + * \end{array} + * \right) + * \f$ + */ + template + constexpr explicit TMat44(const TVec4& v) noexcept; + + // construct from another matrix of the same size + template + constexpr explicit TMat44(const TMat44& rhs) noexcept; + + /** construct from 4 column vectors. + * + * \f$ + * \left( + * \begin{array}{cccc} + * v0 & v1 & v2 & v3 \\ + * \end{array} + * \right) + * \f$ + */ + template + constexpr TMat44(const TVec4& v0, const TVec4& v1, const TVec4& v2, + const TVec4& v3) noexcept; + + /** construct from 16 elements in column-major form. + * + * \f$ + * \left( + * \begin{array}{cccc} + * m[0][0] & m[1][0] & m[2][0] & m[3][0] \\ + * m[0][1] & m[1][1] & m[2][1] & m[3][1] \\ + * m[0][2] & m[1][2] & m[2][2] & m[3][2] \\ + * m[0][3] & m[1][3] & m[2][3] & m[3][3] \\ + * \end{array} + * \right) + * \f$ + */ + template< + typename A, typename B, typename C, typename D, + typename E, typename F, typename G, typename H, + typename I, typename J, typename K, typename L, + typename M, typename N, typename O, typename P> + constexpr explicit TMat44(A m00, B m01, C m02, D m03, + E m10, F m11, G m12, H m13, + I m20, J m21, K m22, L m23, + M m30, N m31, O m32, P m33) noexcept; + + + struct row_major_init { + template< + typename A, typename B, typename C, typename D, + typename E, typename F, typename G, typename H, + typename I, typename J, typename K, typename L, + typename M, typename N, typename O, typename P> + constexpr explicit row_major_init(A m00, B m01, C m02, D m03, + E m10, F m11, G m12, H m13, + I m20, J m21, K m22, L m23, + M m30, N m31, O m32, P m33) noexcept + : m(m00, m10, m20, m30, + m01, m11, m21, m31, + m02, m12, m22, m32, + m03, m13, m23, m33) {} + + private: + friend TMat44; + TMat44 m; + }; + + constexpr explicit TMat44(row_major_init c) noexcept : TMat44(std::move(c.m)) {} + + /** + * construct from a quaternion + */ + template + constexpr explicit TMat44(const TQuaternion& q) noexcept; + + /** + * construct from a 3x3 matrix + */ + template + constexpr explicit TMat44(const TMat33& matrix) noexcept; + + /** + * construct from a 3x3 matrix and 3d translation + */ + template + constexpr TMat44(const TMat33& matrix, const TVec3& translation) noexcept; + + /** + * construct from a 3x3 matrix and 4d last column. + */ + template + constexpr TMat44(const TMat33& matrix, const TVec4& column3) noexcept; + + /* + * helpers + */ + + // returns false if the two matrices are different. May return false if they're the + // same, with some elements only differing by +0 or -0. Behaviour is undefined with NaNs. + static constexpr bool fuzzyEqual(TMat44 const& l, TMat44 const& r) noexcept { + uint64_t const* const li = reinterpret_cast(&l); + uint64_t const* const ri = reinterpret_cast(&r); + uint64_t result = 0; + // For some reason clang is not able to vectorize this loop when the number of iteration + // is known and constant (!?!?!). Still this is better than operator==. + for (size_t i = 0; i < sizeof(TMat44) / sizeof(uint64_t); i++) { + result |= li[i] ^ ri[i]; + } + return result != 0; + } + + static constexpr TMat44 ortho(T left, T right, T bottom, T top, T near, T far) noexcept; + + static constexpr TMat44 frustum(T left, T right, T bottom, T top, T near, T far) noexcept; + + enum class Fov { + HORIZONTAL, + VERTICAL + }; + static TMat44 perspective(T fov, T aspect, T near, T far, Fov direction = Fov::VERTICAL) noexcept; + + template + static TMat44 lookAt(const TVec3& eye, const TVec3& center, const TVec3& up) noexcept; + + template + static constexpr TVec3 project(const TMat44& projectionMatrix, TVec3 vertice) noexcept{ + TVec4 r = projectionMatrix * TVec4{ vertice, 1 }; + return TVec3{ r[0], r[1], r[2] } * (1 / r[3]); + } + + template + static constexpr TVec4 project(const TMat44& projectionMatrix, TVec4 vertice) noexcept{ + vertice = projectionMatrix * vertice; + return { TVec3{ vertice[0], vertice[1], vertice[2] } * (1 / vertice[3]), 1 }; + } + + /** + * Constructs a 3x3 matrix from the upper-left corner of this 4x4 matrix + */ + inline constexpr TMat33 upperLeft() const noexcept { + const TVec3 v0 = { m_value[0][0], m_value[0][1], m_value[0][2] }; + const TVec3 v1 = { m_value[1][0], m_value[1][1], m_value[1][2] }; + const TVec3 v2 = { m_value[2][0], m_value[2][1], m_value[2][2] }; + return TMat33(v0, v1, v2); + } + + template + static constexpr TMat44 translation(const TVec3& t) noexcept { + TMat44 r; + r[3] = TVec4{ t, 1 }; + return r; + } + + template + static constexpr TMat44 scaling(const TVec3& s) noexcept { + return TMat44{ TVec4{ s, 1 }}; + } + + template + static constexpr TMat44 scaling(A s) noexcept { + return TMat44{ TVec4{ s, s, s, 1 }}; + } +}; + +// ---------------------------------------------------------------------------------------- +// Constructors +// ---------------------------------------------------------------------------------------- + +// Since the matrix code could become pretty big quickly, we don't inline most +// operations. + +template +constexpr TMat44::TMat44() noexcept + : m_value{ + col_type(1, 0, 0, 0), + col_type(0, 1, 0, 0), + col_type(0, 0, 1, 0), + col_type(0, 0, 0, 1) } { +} + +template +template +constexpr TMat44::TMat44(U v) noexcept + : m_value{ + col_type(v, 0, 0, 0), + col_type(0, v, 0, 0), + col_type(0, 0, v, 0), + col_type(0, 0, 0, v) } { +} + +template +template +constexpr TMat44::TMat44(const TVec4& v) noexcept + : m_value{ + col_type(v[0], 0, 0, 0), + col_type(0, v[1], 0, 0), + col_type(0, 0, v[2], 0), + col_type(0, 0, 0, v[3]) } { +} + + +// construct from 16 scalars +template +template< + typename A, typename B, typename C, typename D, + typename E, typename F, typename G, typename H, + typename I, typename J, typename K, typename L, + typename M, typename N, typename O, typename P> +constexpr TMat44::TMat44(A m00, B m01, C m02, D m03, + E m10, F m11, G m12, H m13, + I m20, J m21, K m22, L m23, + M m30, N m31, O m32, P m33) noexcept + : m_value{ + col_type(m00, m01, m02, m03), + col_type(m10, m11, m12, m13), + col_type(m20, m21, m22, m23), + col_type(m30, m31, m32, m33) } { +} + +template +template +constexpr TMat44::TMat44(const TMat44& rhs) noexcept { + for (size_t col = 0; col < NUM_COLS; ++col) { + m_value[col] = col_type(rhs[col]); + } +} + +// Construct from 4 column vectors. +template +template +constexpr TMat44::TMat44(const TVec4& v0, const TVec4& v1, + const TVec4& v2, const TVec4& v3) noexcept + : m_value{ v0, v1, v2, v3 } { +} + +template +template +constexpr TMat44::TMat44(const TQuaternion& q) noexcept : m_value{} { + const U n = q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w; + const U s = n > 0 ? 2 / n : 0; + const U x = s * q.x; + const U y = s * q.y; + const U z = s * q.z; + const U xx = x * q.x; + const U xy = x * q.y; + const U xz = x * q.z; + const U xw = x * q.w; + const U yy = y * q.y; + const U yz = y * q.z; + const U yw = y * q.w; + const U zz = z * q.z; + const U zw = z * q.w; + m_value[0] = col_type(1 - yy - zz, xy + zw, xz - yw, 0); + m_value[1] = col_type(xy - zw, 1 - xx - zz, yz + xw, 0); // NOLINT + m_value[2] = col_type(xz + yw, yz - xw, 1 - xx - yy, 0); // NOLINT + m_value[3] = col_type(0, 0, 0, 1); // NOLINT +} + +template +template +constexpr TMat44::TMat44(const TMat33& m) noexcept + : m_value{ + col_type(m[0][0], m[0][1], m[0][2], 0), + col_type(m[1][0], m[1][1], m[1][2], 0), + col_type(m[2][0], m[2][1], m[2][2], 0), + col_type(0, 0, 0, 1) } // NOLINT +{ +} + +template +template +constexpr TMat44::TMat44(const TMat33& m, const TVec3& v) noexcept + : m_value{ + col_type(m[0][0], m[0][1], m[0][2], 0), + col_type(m[1][0], m[1][1], m[1][2], 0), + col_type(m[2][0], m[2][1], m[2][2], 0), + col_type(v[0], v[1], v[2], 1) } // NOLINT +{ +} + +template +template +constexpr TMat44::TMat44(const TMat33& m, const TVec4& v) noexcept + : m_value{ + col_type(m[0][0], m[0][1], m[0][2], 0), + col_type(m[1][0], m[1][1], m[1][2], 0), + col_type(m[2][0], m[2][1], m[2][2], 0), + col_type(v[0], v[1], v[2], v[3]) } // NOLINT +{ +} + + +// ---------------------------------------------------------------------------------------- +// Helpers +// ---------------------------------------------------------------------------------------- + +template +constexpr TMat44 TMat44::ortho(T left, T right, T bottom, T top, T near, T far) noexcept { + TMat44 m; + m[0][0] = 2 / (right - left); + m[1][1] = 2 / (top - bottom); + m[2][2] = -2 / (far - near); + m[3][0] = -(right + left) / (right - left); + m[3][1] = -(top + bottom) / (top - bottom); + m[3][2] = -(far + near) / (far - near); + return m; +} + +template +constexpr TMat44 TMat44::frustum(T left, T right, T bottom, T top, T near, T far) noexcept { + TMat44 m; + m[0][0] = (2 * near) / (right - left); + m[1][1] = (2 * near) / (top - bottom); + m[2][0] = (right + left) / (right - left); + m[2][1] = (top + bottom) / (top - bottom); + m[2][2] = -(far + near) / (far - near); + m[2][3] = -1; + m[3][2] = -(2 * far * near) / (far - near); + m[3][3] = 0; + return m; +} + +template +TMat44 TMat44::perspective(T fov, T aspect, T near, T far, TMat44::Fov direction) noexcept { + T h, w; + + if (direction == TMat44::Fov::VERTICAL) { + h = std::tan(fov * F_PI / 360.0f) * near; + w = h * aspect; + } else { + w = std::tan(fov * F_PI / 360.0f) * near; + h = w / aspect; + } + return frustum(-w, w, -h, h, near, far); +} + +/* + * Returns a matrix representing the pose of a virtual camera looking towards -Z in its + * local Y-up coordinate system. "eye" is where the camera is located, "center" is the point it's + * looking at and "up" defines where the Y axis of the camera's local coordinate system is. + */ +template +template +TMat44 TMat44::lookAt(const TVec3& eye, const TVec3& center, + const TVec3& up) noexcept { + TVec3 z_axis(normalize(center - eye)); + TVec3 norm_up(normalize(up)); + if (std::abs(dot(z_axis, norm_up)) > T(0.999)) { + // Fix up vector if we're degenerate (looking straight up, basically) + norm_up = { norm_up.z, norm_up.x, norm_up.y }; + } + TVec3 x_axis(normalize(cross(z_axis, norm_up))); + TVec3 y_axis(cross(x_axis, z_axis)); + return TMat44( + TVec4(x_axis, 0), + TVec4(y_axis, 0), + TVec4(-z_axis, 0), + TVec4(eye, 1)); +} + +// ---------------------------------------------------------------------------------------- +// Arithmetic operators outside of class +// ---------------------------------------------------------------------------------------- + +// mat44 * vec3, result is vec3( mat44 * {vec3, 1} ) +template +constexpr typename TMat44::col_type MATH_PURE operator*(const TMat44& lhs, + const TVec3& rhs) noexcept { + return lhs * TVec4{ rhs, 1 }; +} + +} // namespace details + +// ---------------------------------------------------------------------------------------- + +typedef details::TMat44 mat4; +typedef details::TMat44 mat4f; + +// ---------------------------------------------------------------------------------------- +} // namespace math +} // namespace filament + +namespace std { +template +constexpr void swap(filament::math::details::TMat44& lhs, + filament::math::details::TMat44& rhs) noexcept { + // This generates much better code than the default implementation + // It's unclear why, I believe this is due to an optimization bug in the clang. + // + // filament::math::details::TMat44 t(lhs); + // lhs = rhs; + // rhs = t; + // + // clang always copy lhs on the stack, even if it's never using it (it's using the + // copy it has in registers). + + const T t00 = lhs[0][0]; + const T t01 = lhs[0][1]; + const T t02 = lhs[0][2]; + const T t03 = lhs[0][3]; + const T t10 = lhs[1][0]; + const T t11 = lhs[1][1]; + const T t12 = lhs[1][2]; + const T t13 = lhs[1][3]; + const T t20 = lhs[2][0]; + const T t21 = lhs[2][1]; + const T t22 = lhs[2][2]; + const T t23 = lhs[2][3]; + const T t30 = lhs[3][0]; + const T t31 = lhs[3][1]; + const T t32 = lhs[3][2]; + const T t33 = lhs[3][3]; + + lhs[0][0] = rhs[0][0]; + lhs[0][1] = rhs[0][1]; + lhs[0][2] = rhs[0][2]; + lhs[0][3] = rhs[0][3]; + lhs[1][0] = rhs[1][0]; + lhs[1][1] = rhs[1][1]; + lhs[1][2] = rhs[1][2]; + lhs[1][3] = rhs[1][3]; + lhs[2][0] = rhs[2][0]; + lhs[2][1] = rhs[2][1]; + lhs[2][2] = rhs[2][2]; + lhs[2][3] = rhs[2][3]; + lhs[3][0] = rhs[3][0]; + lhs[3][1] = rhs[3][1]; + lhs[3][2] = rhs[3][2]; + lhs[3][3] = rhs[3][3]; + + rhs[0][0] = t00; + rhs[0][1] = t01; + rhs[0][2] = t02; + rhs[0][3] = t03; + rhs[1][0] = t10; + rhs[1][1] = t11; + rhs[1][2] = t12; + rhs[1][3] = t13; + rhs[2][0] = t20; + rhs[2][1] = t21; + rhs[2][2] = t22; + rhs[2][3] = t23; + rhs[3][0] = t30; + rhs[3][1] = t31; + rhs[3][2] = t32; + rhs[3][3] = t33; +} +} + +#endif // MATH_MAT4_H_ diff --git a/ios/include/math/mathfwd.h b/ios/include/math/mathfwd.h new file mode 100644 index 00000000..d37ce335 --- /dev/null +++ b/ios/include/math/mathfwd.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MATH_MATHFWD_H_ +#define MATH_MATHFWD_H_ + +#ifdef _MSC_VER + +// MSVC cannot compute the size of math types correctly when this file is included before the +// actual implementations. +// See github.com/google/filament/issues/2190. +#include +#include +#include +#include +#include +#include + +#else + +#include + +namespace filament { +namespace math { +namespace details { + +template class TVec2; +template class TVec3; +template class TVec4; + +template class TMat22; +template class TMat33; +template class TMat44; + +} // namespace details + +using double2 = details::TVec2; +using float2 = details::TVec2; +using int2 = details::TVec2; +using uint2 = details::TVec2; +using short2 = details::TVec2; +using ushort2 = details::TVec2; +using byte2 = details::TVec2; +using ubyte2 = details::TVec2; +using bool2 = details::TVec2; + +using double3 = details::TVec3; +using float3 = details::TVec3; +using int3 = details::TVec3; +using uint3 = details::TVec3; +using short3 = details::TVec3; +using ushort3 = details::TVec3; +using byte3 = details::TVec3; +using ubyte3 = details::TVec3; +using bool3 = details::TVec3; + +using double4 = details::TVec4; +using float4 = details::TVec4; +using int4 = details::TVec4; +using uint4 = details::TVec4; +using short4 = details::TVec4; +using ushort4 = details::TVec4; +using byte4 = details::TVec4; +using ubyte4 = details::TVec4; +using bool4 = details::TVec4; + +using mat2 = details::TMat22; +using mat2f = details::TMat22; + +using mat3 = details::TMat33; +using mat3f = details::TMat33; + +using mat4 = details::TMat44; +using mat4f = details::TMat44; + +} // namespace math +} // namespace filament + +#endif // _MSC_VER + +#endif // MATH_MATHFWD_H_ diff --git a/ios/include/math/norm.h b/ios/include/math/norm.h new file mode 100644 index 00000000..d4d99ba8 --- /dev/null +++ b/ios/include/math/norm.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_MATH_NORM_H +#define TNT_MATH_NORM_H + +#include +#include + +#include +#include + +namespace filament { +namespace math { + +inline uint16_t packUnorm16(float v) noexcept { + return static_cast(std::round(clamp(v, 0.0f, 1.0f) * 65535.0f)); +} + +inline ushort4 packUnorm16(float4 v) noexcept { + return ushort4{ packUnorm16(v.x), packUnorm16(v.y), packUnorm16(v.z), packUnorm16(v.w) }; +} + +inline int16_t packSnorm16(float v) noexcept { + return static_cast(std::round(clamp(v, -1.0f, 1.0f) * 32767.0f)); +} + +inline short2 packSnorm16(float2 v) noexcept { + return short2{ packSnorm16(v.x), packSnorm16(v.y) }; +} + +inline short4 packSnorm16(float4 v) noexcept { + return short4{ packSnorm16(v.x), packSnorm16(v.y), packSnorm16(v.z), packSnorm16(v.w) }; +} + +inline float unpackUnorm16(uint16_t v) noexcept { + return v / 65535.0f; +} + +inline float4 unpackUnorm16(ushort4 v) noexcept { + return float4{ unpackUnorm16(v.x), unpackUnorm16(v.y), unpackUnorm16(v.z), unpackUnorm16(v.w) }; +} + +inline float unpackSnorm16(int16_t v) noexcept { + return clamp(v / 32767.0f, -1.0f, 1.0f); +} + +inline float4 unpackSnorm16(short4 v) noexcept { + return float4{ unpackSnorm16(v.x), unpackSnorm16(v.y), unpackSnorm16(v.z), unpackSnorm16(v.w) }; +} + +inline uint8_t packUnorm8(float v) noexcept { + return static_cast(std::round(clamp(v, 0.0f, 1.0f) * 255.0)); +} + +inline ubyte4 packUnorm8(float4 v) noexcept { + return ubyte4{ packUnorm8(v.x), packUnorm8(v.y), packUnorm8(v.z), packUnorm8(v.w) }; +} + +inline int8_t packSnorm8(float v) noexcept { + return static_cast(std::round(clamp(v, -1.0f, 1.0f) * 127.0)); +} + +inline byte4 packSnorm8(float4 v) noexcept { + return byte4{ packSnorm8(v.x), packSnorm8(v.y), packSnorm8(v.z), packSnorm8(v.w) }; +} + +inline float unpackUnorm8(uint8_t v) noexcept { + return v / 255.0f; +} + +inline float4 unpackUnorm8(ubyte4 v) noexcept { + return float4{ unpackUnorm8(v.x), unpackUnorm8(v.y), unpackUnorm8(v.z), unpackUnorm8(v.w) }; +} + +inline float unpackSnorm8(int8_t v) noexcept { + return clamp(v / 127.0f, -1.0f, 1.0f); +} + +inline float4 unpackSnorm8(byte4 v) noexcept { + return float4{ unpackSnorm8(v.x), unpackSnorm8(v.y), unpackSnorm8(v.z), unpackSnorm8(v.w) }; +} + +} // namespace math +} // namespace filament + +#endif // TNT_MATH_NORM_H diff --git a/ios/include/math/quat.h b/ios/include/math/quat.h new file mode 100644 index 00000000..31c97c4f --- /dev/null +++ b/ios/include/math/quat.h @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MATH_QUAT_H_ +#define MATH_QUAT_H_ + +#include +#include +#include +#include +#include + +#include +#include + +namespace filament { +namespace math { +// ------------------------------------------------------------------------------------- + +namespace details { + +template +class MATH_EMPTY_BASES TQuaternion : + public TVecAddOperators, + public TVecUnaryOperators, + public TVecComparisonOperators, + public TQuatProductOperators, + public TQuatFunctions { +public: + enum no_init { + NO_INIT + }; + typedef T value_type; + typedef T& reference; + typedef T const& const_reference; + typedef size_t size_type; + + /* + * quaternion internals stored as: + * + * q = w + xi + yj + zk + * + * q[0] = x; + * q[1] = y; + * q[2] = z; + * q[3] = w; + * + */ + union { + struct { T x, y, z, w; }; + TVec4 xyzw; + TVec3 xyz; + TVec2 xy; + }; + + enum { SIZE = 4 }; + inline constexpr static size_type size() { return SIZE; } + + // array access + inline constexpr T const& operator[](size_t i) const { + // only possible in C++0x14 with constexpr + assert(i < SIZE); + return (&x)[i]; + } + + inline constexpr T& operator[](size_t i) { + assert(i < SIZE); + return (&x)[i]; + } + + // ----------------------------------------------------------------------- + // we want the compiler generated versions for these... + TQuaternion(const TQuaternion&) = default; + ~TQuaternion() = default; + TQuaternion& operator=(const TQuaternion&) = default; + + // constructors + + // leaves object uninitialized. use with caution. + explicit constexpr TQuaternion(no_init) {} + + // default constructor. sets all values to zero. + constexpr TQuaternion() : x(0), y(0), z(0), w(0) {} + + // handles implicit conversion to a quat. must not be explicit. + template> + constexpr TQuaternion(A w) : x(0), y(0), z(0), w(w) {} + + // initialize from 4 values to w + xi + yj + zk + template> + constexpr TQuaternion(A w, B x, C y, D z) : x(x), y(y), z(z), w(w) {} + + // initialize from a vec3 + a value to : v.xi + v.yj + v.zk + w + template> + constexpr TQuaternion(const TVec3& v, B w) : x(v.x), y(v.y), z(v.z), w(w) {} + + // initialize from a vec4 + template> + constexpr explicit TQuaternion(const TVec4& v) : x(v.x), y(v.y), z(v.z), w(v.w) {} + + // initialize from a quaternion of a different type + template> + constexpr explicit TQuaternion(const TQuaternion& v) : x(v.x), y(v.y), z(v.z), w(v.w) {} + + // conjugate operator + constexpr TQuaternion operator~() const { + return conj(*this); + } + + // constructs a quaternion from an axis and angle + template> + constexpr static TQuaternion MATH_PURE fromAxisAngle(const TVec3& axis, B angle) { + return TQuaternion(std::sin(angle * 0.5) * normalize(axis), std::cos(angle * 0.5)); + } +}; + +} // namespace details + +// ---------------------------------------------------------------------------------------- + +typedef details::TQuaternion quat; +typedef details::TQuaternion quatf; +typedef details::TQuaternion quath; + +constexpr inline quat operator "" _i(long double v) { + return quat(0.0, double(v), 0.0, 0.0); +} + +constexpr inline quat operator "" _j(long double v) { + return quat(0.0, 0.0, double(v), 0.0); +} + +constexpr inline quat operator "" _k(long double v) { + return quat(0.0, 0.0, 0.0, double(v)); +} + +constexpr inline quat operator "" _i(unsigned long long v) { + return quat(0.0, double(v), 0.0, 0.0); +} + +constexpr inline quat operator "" _j(unsigned long long v) { + return quat(0.0, 0.0, double(v), 0.0); +} + +constexpr inline quat operator "" _k(unsigned long long v) { + return quat(0.0, 0.0, 0.0, double(v)); +} + +// ---------------------------------------------------------------------------------------- +} // namespace math +} // namespace filament + +#endif // MATH_QUAT_H_ diff --git a/ios/include/math/scalar.h b/ios/include/math/scalar.h new file mode 100644 index 00000000..1de77d24 --- /dev/null +++ b/ios/include/math/scalar.h @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_MATH_SCALAR_H +#define TNT_MATH_SCALAR_H + +#include +#include + +namespace filament { +namespace math { + +constexpr const double F_E = 2.71828182845904523536028747135266250; +constexpr const double F_LOG2E = 1.44269504088896340735992468100189214; +constexpr const double F_LOG10E = 0.434294481903251827651128918916605082; +constexpr const double F_LN2 = 0.693147180559945309417232121458176568; +constexpr const double F_LN10 = 2.30258509299404568401799145468436421; +constexpr const double F_PI = 3.14159265358979323846264338327950288; +constexpr const double F_PI_2 = 1.57079632679489661923132169163975144; +constexpr const double F_PI_4 = 0.785398163397448309615660845819875721; +constexpr const double F_1_PI = 0.318309886183790671537767526745028724; +constexpr const double F_2_PI = 0.636619772367581343075535053490057448; +constexpr const double F_2_SQRTPI = 1.12837916709551257389615890312154517; +constexpr const double F_SQRT2 = 1.41421356237309504880168872420969808; +constexpr const double F_SQRT1_2 = 0.707106781186547524400844362104849039; +constexpr const double F_TAU = 2.0 * F_PI; + +namespace d { +constexpr const double E = F_E; +constexpr const double LOG2E = F_LOG2E; +constexpr const double LOG10E = F_LOG10E; +constexpr const double LN2 = F_LN2; +constexpr const double LN10 = F_LN10; +constexpr const double PI = F_PI; +constexpr const double PI_2 = F_PI_2; +constexpr const double PI_4 = F_PI_4; +constexpr const double ONE_OVER_PI = F_1_PI; +constexpr const double TWO_OVER_PI = F_2_PI; +constexpr const double TWO_OVER_SQRTPI = F_2_SQRTPI; +constexpr const double SQRT2 = F_SQRT2; +constexpr const double SQRT1_2 = F_SQRT1_2; +constexpr const double TAU = F_TAU; +constexpr const double DEG_TO_RAD = F_PI / 180.0; +constexpr const double RAD_TO_DEG = 180.0 / F_PI; +} // namespace d + +namespace f { +constexpr const float E = (float)d::E; +constexpr const float LOG2E = (float)d::LOG2E; +constexpr const float LOG10E = (float)d::LOG10E; +constexpr const float LN2 = (float)d::LN2; +constexpr const float LN10 = (float)d::LN10; +constexpr const float PI = (float)d::PI; +constexpr const float PI_2 = (float)d::PI_2; +constexpr const float PI_4 = (float)d::PI_4; +constexpr const float ONE_OVER_PI = (float)d::ONE_OVER_PI; +constexpr const float TWO_OVER_PI = (float)d::TWO_OVER_PI; +constexpr const float TWO_OVER_SQRTPI = (float)d::TWO_OVER_SQRTPI; +constexpr const float SQRT2 = (float)d::SQRT2; +constexpr const float SQRT1_2 = (float)d::SQRT1_2; +constexpr const float TAU = (float)d::TAU; +constexpr const float DEG_TO_RAD = (float)d::DEG_TO_RAD; +constexpr const float RAD_TO_DEG = (float)d::RAD_TO_DEG; +} // namespace f + +template +inline constexpr T MATH_PURE min(T a, T b) noexcept { + return a < b ? a : b; +} + +template +inline constexpr T MATH_PURE max(T a, T b) noexcept { + return a > b ? a : b; +} + +template +inline constexpr T MATH_PURE clamp(T v, T min, T max) noexcept { + assert(min <= max); + return T(math::min(max, math::max(min, v))); +} + +template +inline constexpr T MATH_PURE saturate(T v) noexcept { + return clamp(v, T(0), T(1)); +} + +template +inline constexpr T MATH_PURE mix(T x, T y, T a) noexcept { + return x * (T(1) - a) + y * a; +} + +template +inline constexpr T MATH_PURE lerp(T x, T y, T a) noexcept { + return mix(x, y, a); +} + +template +inline constexpr T MATH_PURE smoothstep(T e0, T e1, T x) noexcept { + T t = clamp((x - e0) / (e1 - e0), T(0), T(1)); + return t * t * (T(3) - T(2) * t); +} + +template +inline constexpr T sign(T x) noexcept { + return x < T(0) ? T(-1) : T(1); +} + +} // namespace math +} // namespace filament + +#endif // TNT_MATH_SCALAR_H diff --git a/ios/include/math/vec2.h b/ios/include/math/vec2.h new file mode 100644 index 00000000..64e091cd --- /dev/null +++ b/ios/include/math/vec2.h @@ -0,0 +1,113 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MATH_VEC2_H_ +#define MATH_VEC2_H_ + +#include +#include +#include +#include +#include +#include + + +namespace filament { +namespace math { +// ------------------------------------------------------------------------------------- + +namespace details { + +template +class MATH_EMPTY_BASES TVec2 : + public TVecProductOperators, + public TVecAddOperators, + public TVecUnaryOperators, + public TVecComparisonOperators, + public TVecFunctions { +public: + typedef T value_type; + typedef T& reference; + typedef T const& const_reference; + typedef size_t size_type; + static constexpr size_t SIZE = 2; + + union { + T v[SIZE] MATH_CONSTEXPR_INIT; + struct { T x, y; }; + struct { T s, t; }; + struct { T r, g; }; + }; + + inline constexpr size_type size() const { return SIZE; } + + // array access + inline constexpr T const& operator[](size_t i) const noexcept { + assert(i < SIZE); + return v[i]; + } + + inline constexpr T& operator[](size_t i) noexcept { + assert(i < SIZE); + return v[i]; + } + + // constructors + + // default constructor + MATH_DEFAULT_CTOR_CONSTEXPR TVec2() MATH_DEFAULT_CTOR + + // handles implicit conversion to a tvec4. must not be explicit. + template> + constexpr TVec2(A v) noexcept : v{ T(v), T(v) } {} + + template> + constexpr TVec2(A x, B y) noexcept : v{ T(x), T(y) } {} + + template> + constexpr TVec2(const TVec2& v) noexcept : v{ T(v[0]), T(v[1]) } {} + + // cross product works only on vectors of size 2 or 3 + template + friend inline constexpr + arithmetic_result_t cross(const TVec2& u, const TVec2& v) noexcept { + return u[0] * v[1] - u[1] * v[0]; + } +}; + +} // namespace details + +// ---------------------------------------------------------------------------------------- + +template> +using vec2 = details::TVec2; + +using double2 = vec2; +using float2 = vec2; +using half2 = vec2; +using int2 = vec2; +using uint2 = vec2; +using short2 = vec2; +using ushort2 = vec2; +using byte2 = vec2; +using ubyte2 = vec2; +using bool2 = vec2; + +// ---------------------------------------------------------------------------------------- +} // namespace math +} // namespace filament + +#endif // MATH_VEC2_H_ diff --git a/ios/include/math/vec3.h b/ios/include/math/vec3.h new file mode 100644 index 00000000..909111fb --- /dev/null +++ b/ios/include/math/vec3.h @@ -0,0 +1,133 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MATH_VEC3_H_ +#define MATH_VEC3_H_ + +#include +#include +#include +#include + + +namespace filament { +namespace math { +// ------------------------------------------------------------------------------------- + +namespace details { + +template +class MATH_EMPTY_BASES TVec3 : + public TVecProductOperators, + public TVecAddOperators, + public TVecUnaryOperators, + public TVecComparisonOperators, + public TVecFunctions { +public: + typedef T value_type; + typedef T& reference; + typedef T const& const_reference; + typedef size_t size_type; + static constexpr size_t SIZE = 3; + + union { + T v[SIZE] MATH_CONSTEXPR_INIT; + TVec2 xy; + TVec2 st; + TVec2 rg; + struct { + union { + T x; + T s; + T r; + }; + union { + struct { T y, z; }; + struct { T t, p; }; + struct { T g, b; }; + TVec2 yz; + TVec2 tp; + TVec2 gb; + }; + }; + }; + + inline constexpr size_type size() const { return SIZE; } + + // array access + inline constexpr T const& operator[](size_t i) const noexcept { + assert(i < SIZE); + return v[i]; + } + + inline constexpr T& operator[](size_t i) noexcept { + assert(i < SIZE); + return v[i]; + } + + // constructors + + // default constructor + MATH_DEFAULT_CTOR_CONSTEXPR TVec3() noexcept MATH_DEFAULT_CTOR + + // handles implicit conversion to a tvec3. must not be explicit. + template> + constexpr TVec3(A v) noexcept : v{ T(v), T(v), T(v) } {} + + template> + constexpr TVec3(A x, B y, C z) noexcept : v{ T(x), T(y), T(z) } {} + + template> + constexpr TVec3(const TVec2& v, B z) noexcept : v{ T(v[0]), T(v[1]), T(z) } {} + + template> + constexpr TVec3(const TVec3& v) noexcept : v{ T(v[0]), T(v[1]), T(v[2]) } {} + + // cross product works only on vectors of size 3 + template + friend inline constexpr + TVec3> cross(const TVec3& u, const TVec3& v) noexcept { + return { + u[1] * v[2] - u[2] * v[1], + u[2] * v[0] - u[0] * v[2], + u[0] * v[1] - u[1] * v[0] }; + } +}; + +} // namespace details + +// ---------------------------------------------------------------------------------------- + +template> +using vec3 = details::TVec3; + +using double3 = vec3; +using float3 = vec3; +using half3 = vec3; +using int3 = vec3; +using uint3 = vec3; +using short3 = vec3; +using ushort3 = vec3; +using byte3 = vec3; +using ubyte3 = vec3; +using bool3 = vec3; + +// ---------------------------------------------------------------------------------------- +} // namespace math +} // namespace filament + +#endif // MATH_VEC3_H_ diff --git a/ios/include/math/vec4.h b/ios/include/math/vec4.h new file mode 100644 index 00000000..da11b9e4 --- /dev/null +++ b/ios/include/math/vec4.h @@ -0,0 +1,132 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MATH_VEC4_H_ +#define MATH_VEC4_H_ + +#include +#include +#include +#include + + +namespace filament { +namespace math { +// ------------------------------------------------------------------------------------- + +namespace details { + +template +class MATH_EMPTY_BASES TVec4 : + public TVecProductOperators, + public TVecAddOperators, + public TVecUnaryOperators, + public TVecComparisonOperators, + public TVecFunctions { +public: + typedef T value_type; + typedef T& reference; + typedef T const& const_reference; + typedef size_t size_type; + static constexpr size_t SIZE = 4; + + union { + T v[SIZE] MATH_CONSTEXPR_INIT; + TVec2 xy, st, rg; + TVec3 xyz, stp, rgb; + struct { + union { T x, s, r; }; + union { + TVec2 yz, tp, gb; + TVec3 yzw, tpq, gba; + struct { + union { T y, t, g; }; + union { + TVec2 zw, pq, ba; + struct { T z, w; }; + struct { T p, q; }; + struct { T b, a; }; + }; + }; + }; + }; + }; + + inline constexpr size_type size() const { return SIZE; } + + // array access + inline constexpr T const& operator[](size_t i) const noexcept { + assert(i < SIZE); + return v[i]; + } + + inline constexpr T& operator[](size_t i) noexcept { + assert(i < SIZE); + return v[i]; + } + + // constructors + + // default constructor + MATH_DEFAULT_CTOR_CONSTEXPR TVec4() noexcept MATH_DEFAULT_CTOR + + // handles implicit conversion to a tvec4. must not be explicit. + template> + constexpr TVec4(A v) noexcept : v{ T(v), T(v), T(v), T(v) } {} + + template> + constexpr TVec4(A x, B y, C z, D w) noexcept : v{ T(x), T(y), T(z), T(w) } {} + + template> + constexpr TVec4(const TVec2& v, B z, C w) noexcept : v{ T(v[0]), T(v[1]), T(z), T(w) } {} + + template> + constexpr TVec4(const TVec2& v, const TVec2& w) noexcept : v{ + T(v[0]), T(v[1]), T(w[0]), T(w[1]) } {} + + template> + constexpr TVec4(const TVec3& v, B w) noexcept : v{ T(v[0]), T(v[1]), T(v[2]), T(w) } {} + + template> + constexpr TVec4(const TVec4& v) noexcept : v{ T(v[0]), T(v[1]), T(v[2]), T(v[3]) } {} +}; + +} // namespace details + +// ---------------------------------------------------------------------------------------- + +template> +using vec4 = details::TVec4; + +using double4 = vec4; +using float4 = vec4; +using half4 = vec4; +using int4 = vec4; +using uint4 = vec4; +using short4 = vec4; +using ushort4 = vec4; +using byte4 = vec4; +using ubyte4 = vec4; +using bool4 = vec4; + + +// ---------------------------------------------------------------------------------------- +} // namespace math +} // namespace filament + +#endif // MATH_VEC4_H_ diff --git a/ios/include/mathio/ostream.h b/ios/include/mathio/ostream.h new file mode 100644 index 00000000..5f29c879 --- /dev/null +++ b/ios/include/mathio/ostream.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#if __has_attribute(visibility) +# define MATHIO_PUBLIC __attribute__((visibility("default"))) +#else +# define MATHIO_PUBLIC +#endif + +namespace filament { +namespace math { + +namespace details { template class TQuaternion; } + +template +MATHIO_PUBLIC +std::ostream& operator<<(std::ostream& out, const details::TVec2& v) noexcept; + +template +MATHIO_PUBLIC +std::ostream& operator<<(std::ostream& out, const details::TVec3& v) noexcept; + +template +MATHIO_PUBLIC +std::ostream& operator<<(std::ostream& out, const details::TVec4& v) noexcept; + +template +MATHIO_PUBLIC +std::ostream& operator<<(std::ostream& out, const details::TMat22& v) noexcept; + +template +MATHIO_PUBLIC +std::ostream& operator<<(std::ostream& out, const details::TMat33& v) noexcept; + +template +MATHIO_PUBLIC +std::ostream& operator<<(std::ostream& out, const details::TMat44& v) noexcept; + +template +MATHIO_PUBLIC +std::ostream& operator<<(std::ostream& out, const details::TQuaternion& v) noexcept; + +} // namespace math +} // namespace filament diff --git a/ios/include/trie/htrie_hash.h b/ios/include/trie/htrie_hash.h new file mode 100644 index 00000000..99ee0724 --- /dev/null +++ b/ios/include/trie/htrie_hash.h @@ -0,0 +1,2090 @@ +/** + * MIT License + * + * Copyright (c) 2017 Thibaut Goetghebuer-Planchon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef TSL_HTRIE_HASH_H +#define TSL_HTRIE_HASH_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "array-hash/array_map.h" +#include "array-hash/array_set.h" + + +/* + * __has_include is a bit useless (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79433), + * check also __cplusplus version. + */ +#ifdef __has_include +# if __has_include() && __cplusplus >= 201703L +# define TSL_HT_HAS_STRING_VIEW +# endif +#endif + + +#ifdef TSL_HT_HAS_STRING_VIEW +# include +#endif + + +#ifdef TSL_DEBUG +# define tsl_ht_assert(expr) assert(expr) +#else +# define tsl_ht_assert(expr) (static_cast(0)) +#endif + + +namespace tsl { + +namespace detail_htrie_hash { + +template +struct is_iterator: std::false_type { +}; + +template +struct is_iterator::iterator_category, void>::value>::type>: std::true_type { +}; + +template +struct is_related: std::false_type {}; + +template +struct is_related: std::is_same::type>::type, + typename std::remove_cv::type>::type> {}; + +template +static T numeric_cast(U value, const char* error_message = "numeric_cast() failed.") { + T ret = static_cast(value); + if(static_cast(ret) != value) { + THROW(std::runtime_error, error_message); + } + + const bool is_same_signedness = (std::is_unsigned::value && std::is_unsigned::value) || + (std::is_signed::value && std::is_signed::value); + if(!is_same_signedness && (ret < T{}) != (value < U{})) { + THROW(std::runtime_error, error_message); + } + + return ret; +} + + +template +struct value_node { + /* + * Avoid conflict with copy constructor 'value_node(const value_node&)'. If we call the copy constructor + * with a mutable reference 'value_node(value_node&)', we don't want the forward constructor to be called. + */ + template::value>::type* = nullptr> + value_node(Args&&... args): m_value(std::forward(args)...) { + } + + T m_value; +}; + +template<> +struct value_node { +}; + + +/** + * T should be void if there is no value associated to a key (in a set for example). + */ +template +class htrie_hash { +private: + template + using has_value = typename std::integral_constant::value>; + + static_assert(std::is_same::value, "char is the only supported CharT type for now."); + + static const std::size_t ALPHABET_SIZE = + std::numeric_limits::type>::max() + 1; + + +public: + template + class htrie_hash_iterator; + + + using char_type = CharT; + using key_size_type = KeySizeT; + using size_type = std::size_t; + using hasher = Hash; + using iterator = htrie_hash_iterator; + using const_iterator = htrie_hash_iterator; + using prefix_iterator = htrie_hash_iterator; + using const_prefix_iterator = htrie_hash_iterator; + + +private: + using array_hash_type = + typename std::conditional< + has_value::value, + tsl::array_map, false, + KeySizeT, std::uint16_t, tsl::ah::power_of_two_growth_policy<4>>, + tsl::array_set, false, + KeySizeT, std::uint16_t, tsl::ah::power_of_two_growth_policy<4>>>::type; + + +private: + /* + * The tree is mainly composed of two nodes types: trie_node and hash_node which both have anode as base class. + * Each child is either a hash_node or a trie_node. + * + * A hash_node is always a leaf node, it doesn't have any child. + * + * Example: + * | ... | a |.. ..................... | f | ... | trie_node_1 + * \ \ + * hash_node_1 |array_hash = {"dd"}| |...| a | ... | trie_node_2 + * / + * |array_hash = {"ble", "bric", "lse"}| hash_node_2 + * + * + * Each trie_node may also have a value node, which contains a value T, if the trie_node marks + * the end of a string value. + * + * A trie node should at least have one child or a value node. There can't be a trie node without + * any child and no value node. + */ + + using value_node = tsl::detail_htrie_hash::value_node; + + + class trie_node; + class hash_node; + + // TODO better encapsulate operations modifying the tree. + class anode { + friend class trie_node; + + public: + /* + * TODO Avoid the virtual to economize 8 bytes. We could use a custom deleter in the std::unique_ptr + * we use (as we know if an anode is a trie_node or hash_node). + */ + virtual ~anode() = default; + + bool is_trie_node() const noexcept { + return m_node_type == node_type::TRIE_NODE; + } + + bool is_hash_node() const noexcept { + return m_node_type == node_type::HASH_NODE; + } + + trie_node& as_trie_node() noexcept { + tsl_ht_assert(is_trie_node()); + return static_cast(*this); + } + + hash_node& as_hash_node() noexcept { + tsl_ht_assert(is_hash_node()); + return static_cast(*this); + } + + const trie_node& as_trie_node() const noexcept { + tsl_ht_assert(is_trie_node()); + return static_cast(*this); + } + + const hash_node& as_hash_node() const noexcept { + tsl_ht_assert(is_hash_node()); + return static_cast(*this); + } + + /** + * @see m_child_of_char + */ + CharT child_of_char() const noexcept { + tsl_ht_assert(parent() != nullptr); + return m_child_of_char; + } + + /** + * Return nullptr if none. + */ + trie_node* parent() noexcept { + return m_parent_node; + } + + const trie_node* parent() const noexcept { + return m_parent_node; + } + + protected: + enum class node_type: unsigned char { + HASH_NODE, + TRIE_NODE + }; + + anode(node_type node_type_): m_node_type(node_type_), m_child_of_char(0), + m_parent_node(nullptr) + { + } + + anode(node_type node_type_, CharT child_of_char): m_node_type(node_type_), + m_child_of_char(child_of_char), + m_parent_node(nullptr) + { + } + + + protected: + node_type m_node_type; + + /** + * If the node has a parent, then it's a descendant of some char. + * + * Example: + * | ... | a | b | ... | trie_node_1 + * \ + * |...| a | ... | trie_node_2 + * / + * |array_hash| hash_node_1 + * + * trie_node_2 is a child of trie_node_1 through 'b', it will have 'b' as m_child_of_char. + * hash_node_1 is a child of trie_node_2 through 'a', it will have 'a' as m_child_of_char. + * + * trie_node_1 has no parent, its m_child_of_char is undefined. + */ + CharT m_child_of_char; + trie_node* m_parent_node; + }; + + // Give the position in trie_node::m_children corresponding to the character c + static std::size_t as_position(CharT c) noexcept { + return static_cast(static_cast::type>(c)); + } + + class trie_node: public anode { + public: + trie_node(): anode(anode::node_type::TRIE_NODE), + m_value_node(nullptr), m_children() + { + } + + trie_node(const trie_node& other): anode(anode::node_type::TRIE_NODE, other.m_child_of_char), + m_value_node(nullptr), m_children() + { + if(other.m_value_node != nullptr) { + m_value_node = make_unique(*other.m_value_node); + } + + // TODO avoid recursion + for(std::size_t ichild = 0; ichild < other.m_children.size(); ichild++) { + if(other.m_children[ichild] != nullptr) { + if(other.m_children[ichild]->is_hash_node()) { + m_children[ichild] = make_unique(other.m_children[ichild]->as_hash_node()); + } + else { + m_children[ichild] = make_unique(other.m_children[ichild]->as_trie_node()); + } + + m_children[ichild]->m_parent_node = this; + } + } + } + + trie_node(trie_node&& other) = delete; + trie_node& operator=(const trie_node& other) = delete; + trie_node& operator=(trie_node&& other) = delete; + + /** + * Return nullptr if none. + */ + anode* first_child() noexcept { + return const_cast(static_cast(this)->first_child()); + } + + const anode* first_child() const noexcept { + for(std::size_t ichild = 0; ichild < m_children.size(); ichild++) { + if(m_children[ichild] != nullptr) { + return m_children[ichild].get(); + } + } + + return nullptr; + } + + + /** + * Get the next_child that come after current_child. Return nullptr if no next child. + */ + anode* next_child(const anode& current_child) noexcept { + return const_cast(static_cast(this)->next_child(current_child)); + } + + const anode* next_child(const anode& current_child) const noexcept { + tsl_ht_assert(current_child.parent() == this); + + for(std::size_t ichild = as_position(current_child.child_of_char()) + 1; + ichild < m_children.size(); + ichild++) + { + if(m_children[ichild] != nullptr) { + return m_children[ichild].get(); + } + } + + return nullptr; + } + + + /** + * Return the first left-descendant trie node with an m_value_node. If none return the most left trie node. + */ + trie_node& most_left_descendant_value_trie_node() noexcept { + return const_cast(static_cast(this)->most_left_descendant_value_trie_node()); + } + + const trie_node& most_left_descendant_value_trie_node() const noexcept { + const trie_node* current_node = this; + while(true) { + if(current_node->m_value_node != nullptr) { + return *current_node; + } + + const anode* first_child = current_node->first_child(); + tsl_ht_assert(first_child != nullptr); // a trie_node must either have a value_node or at least one child. + if(first_child->is_hash_node()) { + return *current_node; + } + + current_node = &first_child->as_trie_node(); + } + } + + + + size_type nb_children() const noexcept { + return std::count_if(m_children.cbegin(), m_children.cend(), + [](const std::unique_ptr& n) { return n != nullptr; }); + } + + bool empty() const noexcept { + return std::all_of(m_children.cbegin(), m_children.cend(), + [](const std::unique_ptr& n) { return n == nullptr; }); + } + + std::unique_ptr& child(CharT for_char) noexcept { + return m_children[as_position(for_char)]; + } + + const std::unique_ptr& child(CharT for_char) const noexcept { + return m_children[as_position(for_char)]; + } + + typename std::array, ALPHABET_SIZE>::iterator begin() noexcept { + return m_children.begin(); + } + + typename std::array, ALPHABET_SIZE>::iterator end() noexcept { + return m_children.end(); + } + + void set_child(CharT for_char, std::unique_ptr child) noexcept { + if(child != nullptr) { + child->m_child_of_char = for_char; + child->m_parent_node = this; + } + + m_children[as_position(for_char)] = std::move(child); + } + + std::unique_ptr& val_node() noexcept { + return m_value_node; + } + + const std::unique_ptr& val_node() const noexcept { + return m_value_node; + } + + private: + // TODO Avoid storing a value_node when has_value::value is false + std::unique_ptr m_value_node; + + /** + * Each character CharT corresponds to one position in the array. To convert a character + * to a position use the as_position method. + * + * TODO Try to reduce the size of m_children with a hash map, linear/binary search on array, ... + * TODO Store number of non-null values in m_children. Check if we can store this value in the alignment + * space as we don't want the node to get bigger (empty() and nb_children() are rarely used so it is + * not an important variable). + */ + std::array, ALPHABET_SIZE> m_children; + }; + + + class hash_node: public anode { + public: + hash_node(const Hash& hash, float max_load_factor): + hash_node(HASH_NODE_DEFAULT_INIT_BUCKETS_COUNT, hash, max_load_factor) + { + } + + hash_node(size_type bucket_count, const Hash& hash, float max_load_factor): + anode(anode::node_type::HASH_NODE), m_array_hash(bucket_count, hash) + { + m_array_hash.max_load_factor(max_load_factor); + } + + hash_node(array_hash_type&& array_hash) noexcept(std::is_nothrow_move_constructible::value): + anode(anode::node_type::HASH_NODE), m_array_hash(std::move(array_hash)) + { + } + + hash_node(const hash_node& other) = default; + + hash_node(hash_node&& other) = delete; + hash_node& operator=(const hash_node& other) = delete; + hash_node& operator=(hash_node&& other) = delete; + + + array_hash_type& array_hash() noexcept { + return m_array_hash; + } + + const array_hash_type& array_hash() const noexcept { + return m_array_hash; + } + + private: + array_hash_type m_array_hash; + }; + + + +public: + template + class htrie_hash_iterator { + friend class htrie_hash; + + private: + using anode_type = typename std::conditional::type; + using trie_node_type = typename std::conditional::type; + using hash_node_type = typename std::conditional::type; + + using array_hash_iterator_type = + typename std::conditional::type; + + public: + using iterator_category = std::forward_iterator_tag; + using value_type = typename std::conditional::value, T, void>::type; + using difference_type = std::ptrdiff_t; + using reference = typename std::conditional< + has_value::value, + typename std::conditional::type, + typename std::add_lvalue_reference::type>::type, + void>::type; + using pointer = typename std::conditional< + has_value::value, + typename std::conditional::type, + void>::type; + + private: + /** + * Start reading from start_hash_node->array_hash().begin(). + */ + htrie_hash_iterator(hash_node_type& start_hash_node) noexcept: + htrie_hash_iterator(start_hash_node, start_hash_node.array_hash().begin()) + { + } + + /** + * Start reading from iterator begin in start_hash_node->array_hash(). + */ + htrie_hash_iterator(hash_node_type& start_hash_node, array_hash_iterator_type begin) noexcept: + m_current_trie_node(start_hash_node.parent()), m_current_hash_node(&start_hash_node), + m_array_hash_iterator(begin), + m_array_hash_end_iterator(start_hash_node.array_hash().end()), + m_read_trie_node_value(false) + { + tsl_ht_assert(!m_current_hash_node->array_hash().empty()); + } + + /** + * Start reading from the value in start_trie_node. start_trie_node->val_node() should be non-null. + */ + htrie_hash_iterator(trie_node_type& start_trie_node) noexcept: + m_current_trie_node(&start_trie_node), m_current_hash_node(nullptr), + m_read_trie_node_value(true) + { + tsl_ht_assert(m_current_trie_node->val_node() != nullptr); + } + + template::type* = nullptr> + htrie_hash_iterator(trie_node_type* tnode, hash_node_type* hnode, + array_hash_iterator_type begin, array_hash_iterator_type end, + bool read_trie_node_value) noexcept: + m_current_trie_node(tnode), m_current_hash_node(hnode), + m_array_hash_iterator(begin), m_array_hash_end_iterator(end), + m_read_trie_node_value(read_trie_node_value) + { + } + + template::type* = nullptr> + htrie_hash_iterator(trie_node_type* tnode, hash_node_type* hnode, + array_hash_iterator_type begin, array_hash_iterator_type end, + bool read_trie_node_value, std::basic_string prefix_filter) noexcept: + m_current_trie_node(tnode), m_current_hash_node(hnode), + m_array_hash_iterator(begin), m_array_hash_end_iterator(end), + m_read_trie_node_value(read_trie_node_value), m_prefix_filter(std::move(prefix_filter)) + { + } + + public: + htrie_hash_iterator() noexcept { + } + + // Copy constructor from iterator to const_iterator. + template::type* = nullptr> + htrie_hash_iterator(const htrie_hash_iterator& other) noexcept: + m_current_trie_node(other.m_current_trie_node), m_current_hash_node(other.m_current_hash_node), + m_array_hash_iterator(other.m_array_hash_iterator), + m_array_hash_end_iterator(other.m_array_hash_end_iterator), + m_read_trie_node_value(other.m_read_trie_node_value) + { + } + + // Copy constructor from iterator to const_iterator. + template::type* = nullptr> + htrie_hash_iterator(const htrie_hash_iterator& other) noexcept: + m_current_trie_node(other.m_current_trie_node), m_current_hash_node(other.m_current_hash_node), + m_array_hash_iterator(other.m_array_hash_iterator), + m_array_hash_end_iterator(other.m_array_hash_end_iterator), + m_read_trie_node_value(other.m_read_trie_node_value), m_prefix_filter(other.m_prefix_filter) + { + } + + htrie_hash_iterator(const htrie_hash_iterator& other) = default; + htrie_hash_iterator(htrie_hash_iterator&& other) = default; + htrie_hash_iterator& operator=(const htrie_hash_iterator& other) = default; + htrie_hash_iterator& operator=(htrie_hash_iterator&& other) = default; + + void key(std::basic_string& key_buffer_out) const { + key_buffer_out.clear(); + + trie_node_type* tnode = m_current_trie_node; + while(tnode != nullptr && tnode->parent() != nullptr) { + key_buffer_out.push_back(tnode->child_of_char()); + tnode = tnode->parent(); + } + + std::reverse(key_buffer_out.begin(), key_buffer_out.end()); + + if(!m_read_trie_node_value) { + tsl_ht_assert(m_current_hash_node != nullptr); + if(m_current_hash_node->parent() != nullptr) { + key_buffer_out.push_back(m_current_hash_node->child_of_char()); + } + + key_buffer_out.append(m_array_hash_iterator.key(), m_array_hash_iterator.key_size()); + } + } + + std::basic_string key() const { + std::basic_string key_buffer; + key(key_buffer); + + return key_buffer; + } + + template::value>::type* = nullptr> + reference value() const { + if(this->m_read_trie_node_value) { + tsl_ht_assert(this->m_current_trie_node != nullptr); + tsl_ht_assert(this->m_current_trie_node->val_node() != nullptr); + + return this->m_current_trie_node->val_node()->m_value; + } + else { + return this->m_array_hash_iterator.value(); + } + } + + template::value>::type* = nullptr> + reference operator*() const { + return value(); + } + + template::value>::type* = nullptr> + pointer operator->() const { + return std::addressof(value()); + } + + htrie_hash_iterator& operator++() { + if(m_read_trie_node_value) { + tsl_ht_assert(m_current_trie_node != nullptr); + + m_read_trie_node_value = false; + + anode_type* child = m_current_trie_node->first_child(); + if(child != nullptr) { + set_most_left_descendant_as_next_node(*child); + } + else if(m_current_trie_node->parent() != nullptr) { + trie_node_type* current_node_child = m_current_trie_node; + m_current_trie_node = m_current_trie_node->parent(); + + set_next_node_ascending(*current_node_child); + } + else { + set_as_end_iterator(); + } + } + else { + ++m_array_hash_iterator; + if(m_array_hash_iterator != m_array_hash_end_iterator) { + filter_prefix(); + } + // End of the road, set the iterator as an end node. + else if(m_current_trie_node == nullptr) { + set_as_end_iterator(); + } + else { + tsl_ht_assert(m_current_hash_node != nullptr); + set_next_node_ascending(*m_current_hash_node); + } + } + + + return *this; + } + + htrie_hash_iterator operator++(int) { + htrie_hash_iterator tmp(*this); + ++*this; + + return tmp; + } + + friend bool operator==(const htrie_hash_iterator& lhs, const htrie_hash_iterator& rhs) { + if(lhs.m_current_trie_node != rhs.m_current_trie_node || + lhs.m_read_trie_node_value != rhs.m_read_trie_node_value) + { + return false; + } + else if(lhs.m_read_trie_node_value) { + return true; + } + else { + if(lhs.m_current_hash_node != rhs.m_current_hash_node) { + return false; + } + else if(lhs.m_current_hash_node == nullptr) { + return true; + } + else { + return lhs.m_array_hash_iterator == rhs.m_array_hash_iterator && + lhs.m_array_hash_end_iterator == rhs.m_array_hash_end_iterator; + } + } + } + + friend bool operator!=(const htrie_hash_iterator& lhs, const htrie_hash_iterator& rhs) { + return !(lhs == rhs); + } + + private: + void hash_node_prefix(std::basic_string& key_buffer_out) const { + tsl_ht_assert(!m_read_trie_node_value); + key_buffer_out.clear(); + + trie_node_type* tnode = m_current_trie_node; + while(tnode != nullptr && tnode->parent() != nullptr) { + key_buffer_out.push_back(tnode->child_of_char()); + tnode = tnode->parent(); + } + + std::reverse(key_buffer_out.begin(), key_buffer_out.end()); + + tsl_ht_assert(m_current_hash_node != nullptr); + if(m_current_hash_node->parent() != nullptr) { + key_buffer_out.push_back(m_current_hash_node->child_of_char()); + } + } + + template::type* = nullptr> + void filter_prefix() { + } + + template::type* = nullptr> + void filter_prefix() { + tsl_ht_assert(m_array_hash_iterator != m_array_hash_end_iterator); + tsl_ht_assert(!m_read_trie_node_value && m_current_hash_node != nullptr); + + if(m_prefix_filter.empty()) { + return; + } + + while((m_prefix_filter.size() > m_array_hash_iterator.key_size() || + m_prefix_filter.compare(0, m_prefix_filter.size(), + m_array_hash_iterator.key(), m_prefix_filter.size()) != 0)) + { + ++m_array_hash_iterator; + if(m_array_hash_iterator == m_array_hash_end_iterator) { + if(m_current_trie_node == nullptr) { + set_as_end_iterator(); + } + else { + tsl_ht_assert(m_current_hash_node != nullptr); + set_next_node_ascending(*m_current_hash_node); + } + + return; + } + } + } + + /** + * Go back up in the tree to get the current_trie_node_child sibling. + * If none, try to go back up more in the tree to check the siblings of the ancestors. + */ + void set_next_node_ascending(anode_type& current_trie_node_child) { + tsl_ht_assert(m_current_trie_node != nullptr); + tsl_ht_assert(current_trie_node_child.parent() == m_current_trie_node); + + anode_type* next_node = m_current_trie_node->next_child(current_trie_node_child); + while(next_node == nullptr && m_current_trie_node->parent() != nullptr) { + anode_type* current_child = m_current_trie_node; + m_current_trie_node = m_current_trie_node->parent(); + next_node = m_current_trie_node->next_child(*current_child); + } + + // End of the road, set the iterator as an end node. + if(next_node == nullptr) { + set_as_end_iterator(); + } + else { + set_most_left_descendant_as_next_node(*next_node); + } + } + + void set_most_left_descendant_as_next_node(anode_type& search_start) { + if(search_start.is_hash_node()) { + set_current_hash_node(search_start.as_hash_node()); + } + else { + m_current_trie_node = &search_start.as_trie_node().most_left_descendant_value_trie_node(); + if(m_current_trie_node->val_node() != nullptr) { + m_read_trie_node_value = true; + } + else { + anode_type* first_child = m_current_trie_node->first_child(); + // a trie_node must either have a value_node or at least one child. + tsl_ht_assert(first_child != nullptr); + + set_current_hash_node(first_child->as_hash_node()); + } + } + } + + void set_current_hash_node(hash_node_type& hnode) { + tsl_ht_assert(!hnode.array_hash().empty()); + + m_current_hash_node = &hnode; + m_array_hash_iterator = m_current_hash_node->array_hash().begin(); + m_array_hash_end_iterator = m_current_hash_node->array_hash().end(); + } + + void set_as_end_iterator() { + m_current_trie_node = nullptr; + m_current_hash_node = nullptr; + m_read_trie_node_value = false; + } + + void skip_hash_node() { + tsl_ht_assert(!m_read_trie_node_value && m_current_hash_node != nullptr); + if(m_current_trie_node == nullptr) { + set_as_end_iterator(); + } + else { + tsl_ht_assert(m_current_hash_node != nullptr); + set_next_node_ascending(*m_current_hash_node); + } + } + + private: + trie_node_type* m_current_trie_node; + hash_node_type* m_current_hash_node; + + array_hash_iterator_type m_array_hash_iterator; + array_hash_iterator_type m_array_hash_end_iterator; + + bool m_read_trie_node_value; + // TODO can't have void if !IsPrefixIterator, use inheritance + typename std::conditional, bool>::type m_prefix_filter; + }; + + + +public: + htrie_hash(const Hash& hash, float max_load_factor, size_type burst_threshold): + m_root(nullptr), m_nb_elements(0), + m_hash(hash), m_max_load_factor(max_load_factor) + { + this->burst_threshold(burst_threshold); + } + + htrie_hash(const htrie_hash& other): m_root(nullptr), m_nb_elements(other.m_nb_elements), + m_hash(other.m_hash), m_max_load_factor(other.m_max_load_factor), + m_burst_threshold(other.m_burst_threshold) + { + if(other.m_root != nullptr) { + if(other.m_root->is_hash_node()) { + m_root = make_unique(other.m_root->as_hash_node()); + } + else { + m_root = make_unique(other.m_root->as_trie_node()); + } + } + } + + htrie_hash(htrie_hash&& other) noexcept(std::is_nothrow_move_constructible::value) + : m_root(std::move(other.m_root)), + m_nb_elements(other.m_nb_elements), + m_hash(std::move(other.m_hash)), + m_max_load_factor(other.m_max_load_factor), + m_burst_threshold(other.m_burst_threshold) + { + other.clear(); + } + + htrie_hash& operator=(const htrie_hash& other) { + if(&other != this) { + std::unique_ptr new_root = nullptr; + if(other.m_root != nullptr) { + if(other.m_root->is_hash_node()) { + new_root = make_unique(other.m_root->as_hash_node()); + } + else { + new_root = make_unique(other.m_root->as_trie_node()); + } + } + + m_hash = other.m_hash; + m_root = std::move(new_root); + m_nb_elements = other.m_nb_elements; + m_max_load_factor = other.m_max_load_factor; + m_burst_threshold = other.m_burst_threshold; + } + + return *this; + } + + htrie_hash& operator=(htrie_hash&& other) { + other.swap(*this); + other.clear(); + + return *this; + } + + /* + * Iterators + */ + iterator begin() noexcept { + return mutable_iterator(cbegin()); + } + + const_iterator begin() const noexcept { + return cbegin(); + } + + const_iterator cbegin() const noexcept { + if(empty()) { + return cend(); + } + + return cbegin(*m_root); + } + + iterator end() noexcept { + iterator it; + it.set_as_end_iterator(); + + return it; + } + + const_iterator end() const noexcept { + return cend(); + } + + const_iterator cend() const noexcept { + const_iterator it; + it.set_as_end_iterator(); + + return it; + } + + + /* + * Capacity + */ + bool empty() const noexcept { + return m_nb_elements == 0; + } + + size_type size() const noexcept { + return m_nb_elements; + } + + size_type max_size() const noexcept { + return std::numeric_limits::max(); + } + + size_type max_key_size() const noexcept { + return array_hash_type::MAX_KEY_SIZE; + } + + void shrink_to_fit() { + auto first = begin(); + auto last = end(); + + while(first != last) { + if(first.m_read_trie_node_value) { + ++first; + } + else { + /* + * shrink_to_fit on array_hash will invalidate the iterators of array_hash. + * Save pointer to array_hash, skip the array_hash_node and then call + * shrink_to_fit on the saved pointer. + */ + hash_node* hnode = first.m_current_hash_node; + first.skip_hash_node(); + + tsl_ht_assert(hnode != nullptr); + hnode->array_hash().shrink_to_fit(); + } + } + } + + + /* + * Modifiers + */ + void clear() noexcept { + m_root.reset(nullptr); + m_nb_elements = 0; + } + + template + std::pair insert(const CharT* key, size_type key_size, ValueArgs&&... value_args) { + if(key_size > max_key_size()) { + THROW(std::length_error, "Key is too long."); + } + + if(m_root == nullptr) { + m_root = make_unique(m_hash, m_max_load_factor); + } + + return insert_impl(*m_root, key, key_size, std::forward(value_args)...); + } + + iterator erase(const_iterator pos) { + return erase(mutable_iterator(pos)); + } + + iterator erase(const_iterator first, const_iterator last) { + // TODO Optimize, could avoid the call to std::distance + const std::size_t nb_to_erase = std::size_t(std::distance(first, last)); + auto to_delete = mutable_iterator(first); + for(std::size_t i = 0; i < nb_to_erase; i++) { + to_delete = erase(to_delete); + } + + return to_delete; + } + + size_type erase(const CharT* key, size_type key_size) { + auto it = find(key, key_size); + if(it != end()) { + erase(it); + return 1; + } + else { + return 0; + } + + } + + size_type erase_prefix(const CharT* prefix, size_type prefix_size) { + if(m_root == nullptr) { + return 0; + } + + anode* current_node = m_root.get(); + for(size_type iprefix = 0; iprefix < prefix_size; iprefix++) { + if(current_node->is_trie_node()) { + trie_node* tnode = ¤t_node->as_trie_node(); + + if(tnode->child(prefix[iprefix]) == nullptr) { + return 0; + } + else { + current_node = tnode->child(prefix[iprefix]).get(); + } + } + else { + hash_node& hnode = current_node->as_hash_node(); + return erase_prefix_hash_node(hnode, prefix + iprefix, prefix_size - iprefix); + } + } + + + if(current_node->is_trie_node()) { + trie_node* parent = current_node->parent(); + + if(parent != nullptr) { + const size_type nb_erased = size_descendants(current_node->as_trie_node()); + + parent->set_child(current_node->child_of_char(), nullptr); + m_nb_elements -= nb_erased; + + if(parent->empty()) { + clear_empty_nodes(*parent); + } + + return nb_erased; + } + else { + const size_type nb_erased = m_nb_elements; + m_root.reset(nullptr); + m_nb_elements = 0; + + return nb_erased; + } + } + else { + const size_type nb_erased = current_node->as_hash_node().array_hash().size(); + + current_node->as_hash_node().array_hash().clear(); + m_nb_elements -= nb_erased; + + clear_empty_nodes(current_node->as_hash_node()); + + return nb_erased; + } + } + + void swap(htrie_hash& other) { + using std::swap; + + swap(m_hash, other.m_hash); + swap(m_root, other.m_root); + swap(m_nb_elements, other.m_nb_elements); + swap(m_max_load_factor, other.m_max_load_factor); + swap(m_burst_threshold, other.m_burst_threshold); + } + + /* + * Lookup + */ + template::value>::type* = nullptr> + U& at(const CharT* key, size_type key_size) { + return const_cast(static_cast(this)->at(key, key_size)); + } + + template::value>::type* = nullptr> + const U& at(const CharT* key, size_type key_size) const { + auto it_find = find(key, key_size); + if(it_find != cend()) { + return it_find.value(); + } + else { + THROW(std::out_of_range, "Couldn't find key."); + } + } + + //TODO optimize + template::value>::type* = nullptr> + U& access_operator(const CharT* key, size_type key_size) { + auto it_find = find(key, key_size); + if(it_find != cend()) { + return it_find.value(); + } + else { + return insert(key, key_size, U{}).first.value(); + } + } + + size_type count(const CharT* key, size_type key_size) const { + if(find(key, key_size) != cend()) { + return 1; + } + else { + return 0; + } + } + + iterator find(const CharT* key, size_type key_size) { + if(m_root == nullptr) { + return end(); + } + + return find_impl(*m_root, key, key_size); + } + + const_iterator find(const CharT* key, size_type key_size) const { + if(m_root == nullptr) { + return cend(); + } + + return find_impl(*m_root, key, key_size); + } + + std::pair equal_range(const CharT* key, size_type key_size) { + iterator it = find(key, key_size); + return std::make_pair(it, (it == end())?it:std::next(it)); + } + + std::pair equal_range(const CharT* key, size_type key_size) const { + const_iterator it = find(key, key_size); + return std::make_pair(it, (it == cend())?it:std::next(it)); + } + + std::pair equal_prefix_range(const CharT* prefix, size_type prefix_size) { + if(m_root == nullptr) { + return std::make_pair(prefix_end(), prefix_end()); + } + + return equal_prefix_range_impl(*m_root, prefix, prefix_size); + } + + std::pair equal_prefix_range(const CharT* prefix, + size_type prefix_size) const + { + if(m_root == nullptr) { + return std::make_pair(prefix_cend(), prefix_cend()); + } + + return equal_prefix_range_impl(*m_root, prefix, prefix_size); + } + + iterator longest_prefix(const CharT* key, size_type key_size) { + if(m_root == nullptr) { + return end(); + } + + return longest_prefix_impl(*m_root, key, key_size); + } + + const_iterator longest_prefix(const CharT* key, size_type key_size) const { + if(m_root == nullptr) { + return cend(); + } + + return longest_prefix_impl(*m_root, key, key_size); + } + + + /* + * Hash policy + */ + float max_load_factor() const { + return m_max_load_factor; + } + + void max_load_factor(float ml) { + m_max_load_factor = ml; + } + + /* + * Burst policy + */ + size_type burst_threshold() const { + return m_burst_threshold; + } + + void burst_threshold(size_type threshold) { + const size_type min_burst_threshold = MIN_BURST_THRESHOLD; + m_burst_threshold = std::max(min_burst_threshold, threshold); + } + + /* + * Observers + */ + hasher hash_function() const { + return m_hash; + } + + /* + * Other + */ + template + void serialize(Serializer& serializer) const { + serialize_impl(serializer); + } + + template + void deserialize(Deserializer& deserializer, bool hash_compatible) { + deserialize_impl(deserializer, hash_compatible); + } + +private: + /** + * Get the begin iterator by searching for the most left descendant node starting at search_start_node. + */ + template + Iterator cbegin(const anode& search_start_node) const noexcept { + if(search_start_node.is_hash_node()) { + return Iterator(search_start_node.as_hash_node()); + } + + const trie_node& tnode = search_start_node.as_trie_node().most_left_descendant_value_trie_node(); + if(tnode.val_node() != nullptr) { + return Iterator(tnode); + } + else { + const anode* first_child = tnode.first_child(); + tsl_ht_assert(first_child != nullptr); + + return Iterator(first_child->as_hash_node()); + } + } + + /** + * Get an iterator to the node that come just after the last descendant of search_start_node. + */ + template + Iterator cend(const anode& search_start_node) const noexcept { + if(search_start_node.parent() == nullptr) { + Iterator it; + it.set_as_end_iterator(); + + return it; + } + + const trie_node* current_trie_node = search_start_node.parent(); + const anode* next_node = current_trie_node->next_child(search_start_node); + + while(next_node == nullptr && current_trie_node->parent() != nullptr) { + const anode* current_child = current_trie_node; + current_trie_node = current_trie_node->parent(); + next_node = current_trie_node->next_child(*current_child); + } + + if(next_node == nullptr) { + Iterator it; + it.set_as_end_iterator(); + + return it; + } + else { + return cbegin(*next_node); + } + } + + prefix_iterator prefix_end() noexcept { + prefix_iterator it; + it.set_as_end_iterator(); + + return it; + } + + const_prefix_iterator prefix_cend() const noexcept { + const_prefix_iterator it; + it.set_as_end_iterator(); + + return it; + } + + size_type size_descendants(const anode& start_node) const { + auto first = cbegin(start_node); + auto last = cend(start_node); + + size_type nb_elements = 0; + while(first != last) { + if(first.m_read_trie_node_value) { + nb_elements++; + ++first; + } + else { + nb_elements += first.m_current_hash_node->array_hash().size(); + first.skip_hash_node(); + } + } + + return nb_elements; + } + + template + std::pair insert_impl(anode& search_start_node, + const CharT* key, size_type key_size, ValueArgs&&... value_args) + { + anode* current_node = &search_start_node; + + for(size_type ikey = 0; ikey < key_size; ikey++) { + if(current_node->is_trie_node()) { + trie_node& tnode = current_node->as_trie_node(); + + if(tnode.child(key[ikey]) != nullptr) { + current_node = tnode.child(key[ikey]).get(); + } + else { + auto hnode = make_unique(m_hash, m_max_load_factor); + auto insert_it = hnode->array_hash().emplace_ks(key + ikey + 1, key_size - ikey - 1, + std::forward(value_args)...); + + tnode.set_child(key[ikey], std::move(hnode)); + m_nb_elements++; + + + return std::make_pair(iterator(tnode.child(key[ikey])->as_hash_node(), + insert_it.first), true); + } + } + else { + return insert_in_hash_node(current_node->as_hash_node(), + key + ikey, key_size - ikey, std::forward(value_args)...); + } + } + + + if(current_node->is_trie_node()) { + trie_node& tnode = current_node->as_trie_node(); + if(tnode.val_node() != nullptr) { + return std::make_pair(iterator(tnode), false); + } + else { + tnode.val_node() = make_unique(std::forward(value_args)...); + m_nb_elements++; + + return std::make_pair(iterator(tnode), true); + } + } + else { + return insert_in_hash_node(current_node->as_hash_node(), + "", 0, std::forward(value_args)...); + } + } + + template + std::pair insert_in_hash_node(hash_node& hnode, + const CharT* key, size_type key_size, ValueArgs&&... value_args) + { + if(need_burst(hnode)) { + std::unique_ptr new_node = burst(hnode); + if(hnode.parent() == nullptr) { + tsl_ht_assert(m_root.get() == &hnode); + + m_root = std::move(new_node); + return insert_impl(*m_root, key, key_size, std::forward(value_args)...); + } + else { + trie_node* parent = hnode.parent(); + const CharT child_of_char = hnode.child_of_char(); + + parent->set_child(child_of_char, std::move(new_node)); + + return insert_impl(*parent->child(child_of_char), + key, key_size, std::forward(value_args)...); + } + } + else { + auto it_insert = hnode.array_hash().emplace_ks(key, key_size, + std::forward(value_args)...); + if(it_insert.second) { + m_nb_elements++; + } + + return std::make_pair(iterator(hnode, it_insert.first), it_insert.second); + } + } + + + iterator erase(iterator pos) { + iterator next_pos = std::next(pos); + + if(pos.m_read_trie_node_value) { + tsl_ht_assert(pos.m_current_trie_node != nullptr && pos.m_current_trie_node->val_node() != nullptr); + + pos.m_current_trie_node->val_node().reset(nullptr); + m_nb_elements--; + + if(pos.m_current_trie_node->empty()) { + clear_empty_nodes(*pos.m_current_trie_node); + } + + return next_pos; + } + else { + tsl_ht_assert(pos.m_current_hash_node != nullptr); + auto next_array_hash_it = pos.m_current_hash_node->array_hash().erase(pos.m_array_hash_iterator); + m_nb_elements--; + + if(next_array_hash_it != pos.m_current_hash_node->array_hash().end()) { + // The erase on array_hash invalidated the next_pos iterator, return the right one. + return iterator(*pos.m_current_hash_node, next_array_hash_it); + } + else { + if(pos.m_current_hash_node->array_hash().empty()) { + clear_empty_nodes(*pos.m_current_hash_node); + } + + return next_pos; + } + } + } + + /** + * Clear all the empty nodes from the tree starting from empty_node (empty for a hash_node means that + * the array hash is empty, for a trie_node it means the node doesn't have any child or value_node + * associated to it). + */ + void clear_empty_nodes(anode& empty_node) noexcept { + tsl_ht_assert(!empty_node.is_trie_node() || + (empty_node.as_trie_node().empty() && empty_node.as_trie_node().val_node() == nullptr)); + tsl_ht_assert(!empty_node.is_hash_node() || empty_node.as_hash_node().array_hash().empty()); + + + trie_node* parent = empty_node.parent(); + if(parent == nullptr) { + tsl_ht_assert(m_root.get() == &empty_node); + tsl_ht_assert(m_nb_elements == 0); + m_root.reset(nullptr); + } + else if(parent->val_node() != nullptr || parent->nb_children() > 1) { + parent->child(empty_node.child_of_char()).reset(nullptr); + } + else if(parent->parent() == nullptr) { + tsl_ht_assert(m_root.get() == empty_node.parent()); + tsl_ht_assert(m_nb_elements == 0); + m_root.reset(nullptr); + } + else { + /** + * Parent is empty if we remove its empty_node child. + * Put empty_node as new child of the grand parent instead of parent (move hnode up, + * and delete the parent). And recurse. + * + * We can't just set grand_parent->child(parent->child_of_char()) to nullptr as + * the grand_parent may also become empty. We don't want empty trie_node with no value_node + * in the tree. + */ + trie_node* grand_parent = parent->parent(); + grand_parent->set_child(parent->child_of_char(), + std::move(parent->child(empty_node.child_of_char()))); + + + clear_empty_nodes(empty_node); + } + } + + + + + iterator find_impl(const anode& search_start_node, const CharT* key, size_type key_size) { + return mutable_iterator(static_cast(this)->find_impl(search_start_node, key, key_size)); + } + + const_iterator find_impl(const anode& search_start_node, const CharT* key, size_type key_size) const { + const anode* current_node = &search_start_node; + + for(size_type ikey = 0; ikey < key_size; ikey++) { + if(current_node->is_trie_node()) { + const trie_node* tnode = ¤t_node->as_trie_node(); + + if(tnode->child(key[ikey]) == nullptr) { + return cend(); + } + else { + current_node = tnode->child(key[ikey]).get(); + } + } + else { + return find_in_hash_node(current_node->as_hash_node(), + key + ikey, key_size - ikey); + } + } + + + if(current_node->is_trie_node()) { + const trie_node& tnode = current_node->as_trie_node(); + return (tnode.val_node() != nullptr)?const_iterator(tnode):cend(); + } + else { + return find_in_hash_node(current_node->as_hash_node(), "", 0); + } + } + + const_iterator find_in_hash_node(const hash_node& hnode, + const CharT* key, size_type key_size) const + { + auto it = hnode.array_hash().find_ks(key, key_size); + if(it != hnode.array_hash().end()) { + return const_iterator(hnode, it); + } + else { + return cend(); + } + } + + + iterator longest_prefix_impl(const anode& search_start_node, + const CharT* value, size_type value_size) + { + return mutable_iterator(static_cast(this)->longest_prefix_impl(search_start_node, + value, value_size)); + } + + const_iterator longest_prefix_impl(const anode& search_start_node, + const CharT* value, size_type value_size) const + { + const anode* current_node = &search_start_node; + const_iterator longest_found_prefix = cend(); + + for(size_type ivalue = 0; ivalue < value_size; ivalue++) { + if(current_node->is_trie_node()) { + const trie_node& tnode = current_node->as_trie_node(); + + if(tnode.val_node() != nullptr) { + longest_found_prefix = const_iterator(tnode); + } + + if(tnode.child(value[ivalue]) == nullptr) { + return longest_found_prefix; + } + else { + current_node = tnode.child(value[ivalue]).get(); + } + } + else { + const hash_node& hnode = current_node->as_hash_node(); + + /** + * Test the presence in the hash node of each substring from the + * remaining [ivalue, value_size) string starting from the longest. + * Also test the empty string. + */ + for(std::size_t i = ivalue; i <= value_size; i++) { + auto it = hnode.array_hash().find_ks(value + ivalue, (value_size - i)); + if(it != hnode.array_hash().end()) { + return const_iterator(hnode, it); + } + } + + return longest_found_prefix; + } + } + + if(current_node->is_trie_node()) { + const trie_node& tnode = current_node->as_trie_node(); + + if(tnode.val_node() != nullptr) { + longest_found_prefix = const_iterator(tnode); + } + } + else { + const hash_node& hnode = current_node->as_hash_node(); + + auto it = hnode.array_hash().find_ks("", 0); + if(it != hnode.array_hash().end()) { + longest_found_prefix = const_iterator(hnode, it); + } + } + + return longest_found_prefix; + } + + + std::pair equal_prefix_range_impl( + anode& search_start_node, + const CharT* prefix, size_type prefix_size) + { + auto range = static_cast(this)->equal_prefix_range_impl(search_start_node, + prefix, prefix_size); + return std::make_pair(mutable_iterator(range.first), mutable_iterator(range.second)); + } + + std::pair equal_prefix_range_impl( + const anode& search_start_node, + const CharT* prefix, size_type prefix_size) const + { + const anode* current_node = &search_start_node; + + for(size_type iprefix = 0; iprefix < prefix_size; iprefix++) { + if(current_node->is_trie_node()) { + const trie_node* tnode = ¤t_node->as_trie_node(); + + if(tnode->child(prefix[iprefix]) == nullptr) { + return std::make_pair(prefix_cend(), prefix_cend()); + } + else { + current_node = tnode->child(prefix[iprefix]).get(); + } + } + else { + const hash_node& hnode = current_node->as_hash_node(); + const_prefix_iterator begin(hnode.parent(), &hnode, + hnode.array_hash().begin(), hnode.array_hash().end(), + false, std::basic_string(prefix + iprefix, prefix_size - iprefix)); + begin.filter_prefix(); + + const_prefix_iterator end = cend(*current_node); + + return std::make_pair(begin, end); + } + } + + + const_prefix_iterator begin = cbegin(*current_node); + const_prefix_iterator end = cend(*current_node); + + return std::make_pair(begin, end); + } + + size_type erase_prefix_hash_node(hash_node& hnode, const CharT* prefix, size_type prefix_size) { + size_type nb_erased = 0; + + auto it = hnode.array_hash().begin(); + while(it != hnode.array_hash().end()) { + if(it.key_size() >= prefix_size && + std::memcmp(prefix, it.key(), prefix_size * sizeof(CharT)) == 0) + { + it = hnode.array_hash().erase(it); + ++nb_erased; + --m_nb_elements; + } + else { + ++it; + } + } + + return nb_erased; + } + + + /* + * Burst + */ + bool need_burst(hash_node& node) const { + return node.array_hash().size() >= m_burst_threshold; + } + + + /** + * Burst the node and use the copy constructor instead of move constructor for the values. + * Also use this method for trivial value types like int, int*, ... as it requires + * less book-keeping (thus faster) than the burst using move constructors. + */ + template::value && + std::is_copy_constructible::value && + (!std::is_nothrow_move_constructible::value || + !std::is_nothrow_move_assignable::value || + std::is_arithmetic::value || + std::is_pointer::value)>::type* = nullptr> + std::unique_ptr burst(hash_node& node) { + const std::array first_char_count = + get_first_char_count(node.array_hash().cbegin(), + node.array_hash().cend()); + + + auto new_node = make_unique(); + for(auto it = node.array_hash().cbegin(); it != node.array_hash().cend(); ++it) { + if(it.key_size() == 0) { + new_node->val_node() = make_unique(it.value()); + } + else { + hash_node& hnode = get_hash_node_for_char(first_char_count, *new_node, it.key()[0]); + hnode.array_hash().insert_ks(it.key() + 1, it.key_size() - 1, it.value()); + } + } + + + tsl_ht_assert(new_node->val_node() != nullptr || !new_node->empty()); + return new_node; + } + + /** + * Burst the node and use the move constructor and move assign operator + */ + template::value && + std::is_nothrow_move_constructible::value && + std::is_nothrow_move_assignable::value && + !std::is_arithmetic::value && + !std::is_pointer::value>::type* = nullptr> + std::unique_ptr burst(hash_node& node) { + /** + * We burst the node->array_hash() into multiple arrays hash. While doing so, we move each value in + * the node->array_hash() into the new arrays hash. After each move, we save a pointer to where the value + * has been moved. In case of exception, we rollback these values into the original node->array_hash(). + */ + std::vector moved_values_rollback; + moved_values_rollback.reserve(node.array_hash().size()); + + const std::array first_char_count = + get_first_char_count(node.array_hash().cbegin(), node.array_hash().cend()); + + + auto new_node = make_unique(); + for(auto it = node.array_hash().begin(); it != node.array_hash().end(); ++it) { + if(it.key_size() == 0) { + new_node->val_node() = make_unique(std::move(it.value())); + moved_values_rollback.push_back(std::addressof(new_node->val_node()->m_value)); + } + else { + hash_node& hnode = get_hash_node_for_char(first_char_count, *new_node, it.key()[0]); + auto it_insert = hnode.array_hash().insert_ks(it.key() + 1, it.key_size() - 1, + std::move(it.value())); + moved_values_rollback.push_back(std::addressof(it_insert.first.value())); + } + } + + + tsl_ht_assert(new_node->val_node() != nullptr || !new_node->empty()); + return new_node; + } + + template::value>::type* = nullptr> + std::unique_ptr burst(hash_node& node) { + const std::array first_char_count = + get_first_char_count(node.array_hash().begin(), node.array_hash().end()); + + + auto new_node = make_unique(); + for(auto it = node.array_hash().cbegin(); it != node.array_hash().cend(); ++it) { + if(it.key_size() == 0) { + new_node->val_node() = make_unique(); + } + else { + hash_node& hnode = get_hash_node_for_char(first_char_count, *new_node, it.key()[0]); + hnode.array_hash().insert_ks(it.key() + 1, it.key_size() - 1); + } + } + + + tsl_ht_assert(new_node->val_node() != nullptr || !new_node->empty()); + return new_node; + } + + std::array get_first_char_count(typename array_hash_type::const_iterator begin, + typename array_hash_type::const_iterator end) const + { + std::array count{{}}; + for(auto it = begin; it != end; ++it) { + if(it.key_size() == 0) { + continue; + } + + count[as_position(it.key()[0])]++; + } + + return count; + } + + + hash_node& get_hash_node_for_char(const std::array& first_char_count, + trie_node& tnode, CharT for_char) + { + if(tnode.child(for_char) == nullptr) { + const size_type nb_buckets = + size_type( + std::ceil(float(first_char_count[as_position(for_char)] + + HASH_NODE_DEFAULT_INIT_BUCKETS_COUNT/2) + / m_max_load_factor + )); + + tnode.set_child(for_char, + make_unique(nb_buckets, m_hash, m_max_load_factor)); + } + + return tnode.child(for_char)->as_hash_node(); + } + + iterator mutable_iterator(const_iterator it) noexcept { + // end iterator or reading from a trie node value + if(it.m_current_hash_node == nullptr || it.m_read_trie_node_value) { + typename array_hash_type::iterator default_it; + + return iterator(const_cast(it.m_current_trie_node), nullptr, + default_it, default_it, it.m_read_trie_node_value); + } + else { + hash_node* hnode = const_cast(it.m_current_hash_node); + return iterator(const_cast(it.m_current_trie_node), hnode, + hnode->array_hash().mutable_iterator(it.m_array_hash_iterator), + hnode->array_hash().mutable_iterator(it.m_array_hash_end_iterator), + it.m_read_trie_node_value); + } + } + + prefix_iterator mutable_iterator(const_prefix_iterator it) noexcept { + // end iterator or reading from a trie node value + if(it.m_current_hash_node == nullptr || it.m_read_trie_node_value) { + typename array_hash_type::iterator default_it; + + return prefix_iterator(const_cast(it.m_current_trie_node), nullptr, + default_it, default_it, it.m_read_trie_node_value, ""); + } + else { + hash_node* hnode = const_cast(it.m_current_hash_node); + return prefix_iterator(const_cast(it.m_current_trie_node), hnode, + hnode->array_hash().mutable_iterator(it.m_array_hash_iterator), + hnode->array_hash().mutable_iterator(it.m_array_hash_end_iterator), + it.m_read_trie_node_value, it.m_prefix_filter); + } + } + + template + void serialize_impl(Serializer& serializer) const { + const slz_size_type version = SERIALIZATION_PROTOCOL_VERSION; + serializer(version); + + const slz_size_type nb_elements = m_nb_elements; + serializer(nb_elements); + + const float max_load_factor = m_max_load_factor; + serializer(max_load_factor); + + const slz_size_type burst_threshold = m_burst_threshold; + serializer(burst_threshold); + + + std::basic_string str_buffer; + + auto it = begin(); + auto last = end(); + + while(it != last) { + // Serialize trie node value + if(it.m_read_trie_node_value) { + const CharT node_type = static_cast::type>(slz_node_type::TRIE_NODE); + serializer(&node_type, 1); + + it.key(str_buffer); + + const slz_size_type str_size = str_buffer.size(); + serializer(str_size); + serializer(str_buffer.data(), str_buffer.size()); + serialize_value(serializer, it); + + + ++it; + } + // Serialize hash node values + else { + const CharT node_type = static_cast::type>(slz_node_type::HASH_NODE); + serializer(&node_type, 1); + + it.hash_node_prefix(str_buffer); + + const slz_size_type str_size = str_buffer.size(); + serializer(str_size); + serializer(str_buffer.data(), str_buffer.size()); + + const hash_node* hnode = it.m_current_hash_node; + tsl_ht_assert(hnode != nullptr); + hnode->array_hash().serialize(serializer); + + + it.skip_hash_node(); + } + } + } + + template::value>::type* = nullptr> + void serialize_value(Serializer& /*serializer*/, const_iterator /*it*/) const { + } + + template::value>::type* = nullptr> + void serialize_value(Serializer& serializer, const_iterator it) const { + serializer(it.value()); + } + + template + void deserialize_impl(Deserializer& deserializer, bool hash_compatible) { + tsl_ht_assert(m_nb_elements == 0 && m_root == nullptr); // Current trie must be empty + + const slz_size_type version = deserialize_value(deserializer); + // For now we only have one version of the serialization protocol. + // If it doesn't match there is a problem with the file. + if(version != SERIALIZATION_PROTOCOL_VERSION) { + THROW(std::runtime_error, "Can't deserialize the htrie_map/set. The protocol version header is invalid."); + } + + + const slz_size_type nb_elements = deserialize_value(deserializer); + const float max_load_factor = deserialize_value(deserializer); + const slz_size_type burst_threshold = deserialize_value(deserializer); + + this->burst_threshold(numeric_cast(burst_threshold, "Deserialized burst_threshold is too big.")); + this->max_load_factor(max_load_factor); + + + std::vector str_buffer; + while(m_nb_elements < nb_elements) { + CharT node_type_marker; + deserializer(&node_type_marker, 1); + + static_assert(std::is_same::type>::value, ""); + const slz_node_type node_type = static_cast(node_type_marker); + if(node_type == slz_node_type::TRIE_NODE) { + const std::size_t str_size = numeric_cast(deserialize_value(deserializer), + "Deserialized str_size is too big."); + + str_buffer.resize(str_size); + deserializer(str_buffer.data(), str_size); + + + trie_node* current_node = insert_prefix_trie_nodes(str_buffer.data(), str_size); + deserialize_value_node(deserializer, current_node); + m_nb_elements++; + } + else if(node_type == slz_node_type::HASH_NODE) { + const std::size_t str_size = numeric_cast(deserialize_value(deserializer), + "Deserialized str_size is too big."); + + if(str_size == 0) { + tsl_ht_assert(m_nb_elements == 0 && !m_root); + + m_root = make_unique(array_hash_type::deserialize(deserializer, hash_compatible)); + m_nb_elements += m_root->as_hash_node().array_hash().size(); + + tsl_ht_assert(m_nb_elements == nb_elements); + } + else { + str_buffer.resize(str_size); + deserializer(str_buffer.data(), str_size); + + + auto hnode = make_unique(array_hash_type::deserialize(deserializer, hash_compatible)); + m_nb_elements += hnode->array_hash().size(); + + trie_node* current_node = insert_prefix_trie_nodes(str_buffer.data(), str_size - 1); + current_node->set_child(str_buffer[str_size - 1], std::move(hnode)); + } + } + else { + THROW(std::runtime_error, "Unknown deserialized node type."); + } + } + + tsl_ht_assert(m_nb_elements == nb_elements); + } + + trie_node* insert_prefix_trie_nodes(const CharT* prefix, std::size_t prefix_size) { + if(m_root == nullptr) { + m_root = make_unique(); + } + + trie_node* current_node = &m_root->as_trie_node(); + for(std::size_t iprefix = 0; iprefix < prefix_size; iprefix++) { + if(current_node->child(prefix[iprefix]) == nullptr) { + current_node->set_child(prefix[iprefix], make_unique()); + } + + current_node = ¤t_node->child(prefix[iprefix])->as_trie_node(); + } + + return current_node; + } + + template::value>::type* = nullptr> + void deserialize_value_node(Deserializer& /*deserializer*/, trie_node* current_node) { + tsl_ht_assert(!current_node->val_node()); + current_node->val_node() = make_unique(); + } + + template::value>::type* = nullptr> + void deserialize_value_node(Deserializer& deserializer, trie_node* current_node) { + tsl_ht_assert(!current_node->val_node()); + current_node->val_node() = make_unique(deserialize_value(deserializer)); + } + + template + static U deserialize_value(Deserializer& deserializer) { + // MSVC < 2017 is not conformant, circumvent the problem by removing the template keyword + #if defined (_MSC_VER) && _MSC_VER < 1910 + return deserializer.Deserializer::operator()(); + #else + return deserializer.Deserializer::template operator()(); + #endif + } + + // Same as std::make_unique for non-array types which is only available in C++14 (we need to support C++11). + template + static std::unique_ptr make_unique(Args&&... args) { + return std::unique_ptr(new U(std::forward(args)...)); + } + +public: + static constexpr float HASH_NODE_DEFAULT_MAX_LOAD_FACTOR = 8.0f; + static const size_type DEFAULT_BURST_THRESHOLD = 16384; + +private: + + /** + * Fixed size type used to represent size_type values on serialization. Need to be big enough + * to represent a std::size_t on 32 and 64 bits platforms, and must be the same size on both platforms. + */ + using slz_size_type = std::uint64_t; + enum class slz_node_type: CharT { TRIE_NODE = 0, HASH_NODE = 1 }; + + /** + * Protocol version currenlty used for serialization. + */ + static const slz_size_type SERIALIZATION_PROTOCOL_VERSION = 1; + + static const size_type HASH_NODE_DEFAULT_INIT_BUCKETS_COUNT = 32; + static const size_type MIN_BURST_THRESHOLD = 4; + + std::unique_ptr m_root; + size_type m_nb_elements; + Hash m_hash; + float m_max_load_factor; + size_type m_burst_threshold; + +}; + +} // end namespace detail_htrie_hash +} // end namespace tsl + +#endif diff --git a/ios/include/trie/htrie_map.h b/ios/include/trie/htrie_map.h new file mode 100644 index 00000000..59712c5e --- /dev/null +++ b/ios/include/trie/htrie_map.h @@ -0,0 +1,647 @@ +/** + * MIT License + * + * Copyright (c) 2017 Thibaut Goetghebuer-Planchon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef TSL_HTRIE_MAP_H +#define TSL_HTRIE_MAP_H + +#include +#include +#include +#include +#include +#include "htrie_hash.h" + +namespace tsl { + +/** + * Implementation of a hat-trie map. + * + * The value T must be either nothrow move-constructible/assignable, copy-constructible or both. + * + * The size of a key string is limited to std::numeric_limits::max() - 1. + * That is 65 535 characters by default, but can be raised with the KeySizeT template parameter. + * See max_key_size() for an easy access to this limit. + * + * Iterators invalidation: + * - clear, operator=: always invalidate the iterators. + * - insert, emplace, operator[]: always invalidate the iterators. + * - erase: always invalidate the iterators. + */ +template, + class KeySizeT = std::uint16_t> +class htrie_map { +private: + template + using is_iterator = tsl::detail_array_hash::is_iterator; + + using ht = tsl::detail_htrie_hash::htrie_hash; + +public: + using char_type = typename ht::char_type; + using mapped_type = T; + using key_size_type = typename ht::key_size_type; + using size_type = typename ht::size_type; + using hasher = typename ht::hasher; + using iterator = typename ht::iterator; + using const_iterator = typename ht::const_iterator; + using prefix_iterator = typename ht::prefix_iterator; + using const_prefix_iterator = typename ht::const_prefix_iterator; + +public: + explicit htrie_map(const Hash& hash = Hash()): m_ht(hash, ht::HASH_NODE_DEFAULT_MAX_LOAD_FACTOR, + ht::DEFAULT_BURST_THRESHOLD) + { + } + + explicit htrie_map(size_type burst_threshold, + const Hash& hash = Hash()): m_ht(hash, ht::HASH_NODE_DEFAULT_MAX_LOAD_FACTOR, + burst_threshold) + { + } + + template::value>::type* = nullptr> + htrie_map(InputIt first, InputIt last, + const Hash& hash = Hash()): htrie_map(hash) + { + insert(first, last); + } + + + +#ifdef TSL_HT_HAS_STRING_VIEW + htrie_map(std::initializer_list, T>> init, + const Hash& hash = Hash()): htrie_map(hash) + { + insert(init); + } +#else + htrie_map(std::initializer_list> init, + const Hash& hash = Hash()): htrie_map(hash) + { + insert(init); + } +#endif + + + +#ifdef TSL_HT_HAS_STRING_VIEW + htrie_map& operator=(std::initializer_list, T>> ilist) { + clear(); + insert(ilist); + + return *this; + } +#else + htrie_map& operator=(std::initializer_list> ilist) { + clear(); + insert(ilist); + + return *this; + } +#endif + + + + /* + * Iterators + */ + iterator begin() noexcept { return m_ht.begin(); } + const_iterator begin() const noexcept { return m_ht.begin(); } + const_iterator cbegin() const noexcept { return m_ht.cbegin(); } + + iterator end() noexcept { return m_ht.end(); } + const_iterator end() const noexcept { return m_ht.end(); } + const_iterator cend() const noexcept { return m_ht.cend(); } + + + /* + * Capacity + */ + bool empty() const noexcept { return m_ht.empty(); } + size_type size() const noexcept { return m_ht.size(); } + size_type max_size() const noexcept { return m_ht.max_size(); } + size_type max_key_size() const noexcept { return m_ht.max_key_size(); } + + /** + * Call shrink_to_fit() on each hash node of the hat-trie to reduce its size. + */ + void shrink_to_fit() { m_ht.shrink_to_fit(); } + + + /* + * Modifiers + */ + void clear() noexcept { m_ht.clear(); } + + + + std::pair insert_ks(const CharT* key, size_type key_size, const T& value) { + return m_ht.insert(key, key_size, value); + } +#ifdef TSL_HT_HAS_STRING_VIEW + std::pair insert(const std::basic_string_view& key, const T& value) { + return m_ht.insert(key.data(), key.size(), value); + } +#else + std::pair insert(const CharT* key, const T& value) { + return m_ht.insert(key, std::strlen(key), value); + } + + std::pair insert(const std::basic_string& key, const T& value) { + return m_ht.insert(key.data(), key.size(), value); + } +#endif + + + + std::pair insert_ks(const CharT* key, size_type key_size, T&& value) { + return m_ht.insert(key, key_size, std::move(value)); + } +#ifdef TSL_HT_HAS_STRING_VIEW + std::pair insert(const std::basic_string_view& key, T&& value) { + return m_ht.insert(key.data(), key.size(), std::move(value)); + } +#else + std::pair insert(const CharT* key, T&& value) { + return m_ht.insert(key, std::strlen(key), std::move(value)); + } + + std::pair insert(const std::basic_string& key, T&& value) { + return m_ht.insert(key.data(), key.size(), std::move(value)); + } +#endif + + + + template::value>::type* = nullptr> + void insert(InputIt first, InputIt last) { + for(auto it = first; it != last; ++it) { + insert_pair(*it); + } + } + + + +#ifdef TSL_HT_HAS_STRING_VIEW + void insert(std::initializer_list, T>> ilist) { + insert(ilist.begin(), ilist.end()); + } +#else + void insert(std::initializer_list> ilist) { + insert(ilist.begin(), ilist.end()); + } +#endif + + + + template + std::pair emplace_ks(const CharT* key, size_type key_size, Args&&... args) { + return m_ht.insert(key, key_size, std::forward(args)...); + } +#ifdef TSL_HT_HAS_STRING_VIEW + template + std::pair emplace(const std::basic_string_view& key, Args&&... args) { + return m_ht.insert(key.data(), key.size(), std::forward(args)...); + } +#else + template + std::pair emplace(const CharT* key, Args&&... args) { + return m_ht.insert(key, std::strlen(key), std::forward(args)...); + } + + template + std::pair emplace(const std::basic_string& key, Args&&... args) { + return m_ht.insert(key.data(), key.size(), std::forward(args)...); + } +#endif + + + + iterator erase(const_iterator pos) { return m_ht.erase(pos); } + iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); } + + + + size_type erase_ks(const CharT* key, size_type key_size) { + return m_ht.erase(key, key_size); + } +#ifdef TSL_HT_HAS_STRING_VIEW + size_type erase(const std::basic_string_view& key) { + return m_ht.erase(key.data(), key.size()); + } +#else + size_type erase(const CharT* key) { + return m_ht.erase(key, std::strlen(key)); + } + + size_type erase(const std::basic_string& key) { + return m_ht.erase(key.data(), key.size()); + } +#endif + + + + /** + * Erase all the elements which have 'prefix' as prefix. Return the number of erase elements. + */ + size_type erase_prefix_ks(const CharT* prefix, size_type prefix_size) { + return m_ht.erase_prefix(prefix, prefix_size); + } +#ifdef TSL_HT_HAS_STRING_VIEW + /** + * @copydoc erase_prefix_ks(const CharT* prefix, size_type prefix_size) + */ + size_type erase_prefix(const std::basic_string_view& prefix) { + return m_ht.erase_prefix(prefix.data(), prefix.size()); + } +#else + /** + * @copydoc erase_prefix_ks(const CharT* prefix, size_type prefix_size) + */ + size_type erase_prefix(const CharT* prefix) { + return m_ht.erase_prefix(prefix, std::strlen(prefix)); + } + + /** + * @copydoc erase_prefix_ks(const CharT* prefix, size_type prefix_size) + */ + size_type erase_prefix(const std::basic_string& prefix) { + return m_ht.erase_prefix(prefix.data(), prefix.size()); + } +#endif + + + + void swap(htrie_map& other) { other.m_ht.swap(m_ht); } + + /* + * Lookup + */ + T& at_ks(const CharT* key, size_type key_size) { return m_ht.at(key, key_size); } + const T& at_ks(const CharT* key, size_type key_size) const { return m_ht.at(key, key_size); } + +#ifdef TSL_HT_HAS_STRING_VIEW + T& at(const std::basic_string_view& key) { return m_ht.at(key.data(), key.size()); } + const T& at(const std::basic_string_view& key) const { return m_ht.at(key.data(), key.size()); } +#else + T& at(const CharT* key) { return m_ht.at(key, std::strlen(key)); } + const T& at(const CharT* key) const { return m_ht.at(key, std::strlen(key)); } + + T& at(const std::basic_string& key) { return m_ht.at(key.data(), key.size()); } + const T& at(const std::basic_string& key) const { return m_ht.at(key.data(), key.size()); } +#endif + + + +#ifdef TSL_HT_HAS_STRING_VIEW + T& operator[](const std::basic_string_view& key) { return m_ht.access_operator(key.data(), key.size()); } +#else + T& operator[](const CharT* key) { return m_ht.access_operator(key, std::strlen(key)); } + T& operator[](const std::basic_string& key) { return m_ht.access_operator(key.data(), key.size()); } +#endif + + + + size_type count_ks(const CharT* key, size_type key_size) const { return m_ht.count(key, key_size); } +#ifdef TSL_HT_HAS_STRING_VIEW + size_type count(const std::basic_string_view& key) const { return m_ht.count(key.data(), key.size()); } +#else + size_type count(const CharT* key) const { return m_ht.count(key, std::strlen(key)); } + size_type count(const std::basic_string& key) const { return m_ht.count(key.data(), key.size()); } +#endif + + + + iterator find_ks(const CharT* key, size_type key_size) { + return m_ht.find(key, key_size); + } + + const_iterator find_ks(const CharT* key, size_type key_size) const { + return m_ht.find(key, key_size); + } +#ifdef TSL_HT_HAS_STRING_VIEW + iterator find(const std::basic_string_view& key) { + return m_ht.find(key.data(), key.size()); + } + + const_iterator find(const std::basic_string_view& key) const { + return m_ht.find(key.data(), key.size()); + } +#else + iterator find(const CharT* key) { + return m_ht.find(key, std::strlen(key)); + } + + const_iterator find(const CharT* key) const { + return m_ht.find(key, std::strlen(key)); + } + + iterator find(const std::basic_string& key) { + return m_ht.find(key.data(), key.size()); + } + + const_iterator find(const std::basic_string& key) const { + return m_ht.find(key.data(), key.size()); + } +#endif + + + + std::pair equal_range_ks(const CharT* key, size_type key_size) { + return m_ht.equal_range(key, key_size); + } + + std::pair equal_range_ks(const CharT* key, size_type key_size) const { + return m_ht.equal_range(key, key_size); + } +#ifdef TSL_HT_HAS_STRING_VIEW + std::pair equal_range(const std::basic_string_view& key) { + return m_ht.equal_range(key.data(), key.size()); + } + + std::pair equal_range(const std::basic_string_view& key) const { + return m_ht.equal_range(key.data(), key.size()); + } +#else + std::pair equal_range(const CharT* key) { + return m_ht.equal_range(key, std::strlen(key)); + } + + std::pair equal_range(const CharT* key) const { + return m_ht.equal_range(key, std::strlen(key)); + } + + std::pair equal_range(const std::basic_string& key) { + return m_ht.equal_range(key.data(), key.size()); + } + + std::pair equal_range(const std::basic_string& key) const { + return m_ht.equal_range(key.data(), key.size()); + } +#endif + + + /** + * Return a range containing all the elements which have 'prefix' as prefix. The range is defined by a pair + * of iterator, the first being the begin iterator and the second being the end iterator. + */ + std::pair equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) { + return m_ht.equal_prefix_range(prefix, prefix_size); + } + + /** + * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) + */ + std::pair equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) const { + return m_ht.equal_prefix_range(prefix, prefix_size); + } +#ifdef TSL_HT_HAS_STRING_VIEW + /** + * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) + */ + std::pair equal_prefix_range(const std::basic_string_view& prefix) { + return m_ht.equal_prefix_range(prefix.data(), prefix.size()); + } + + /** + * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) + */ + std::pair equal_prefix_range(const std::basic_string_view& prefix) const { + return m_ht.equal_prefix_range(prefix.data(), prefix.size()); + } +#else + /** + * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) + */ + std::pair equal_prefix_range(const CharT* prefix) { + return m_ht.equal_prefix_range(prefix, std::strlen(prefix)); + } + + /** + * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) + */ + std::pair equal_prefix_range(const CharT* prefix) const { + return m_ht.equal_prefix_range(prefix, std::strlen(prefix)); + } + + /** + * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) + */ + std::pair equal_prefix_range(const std::basic_string& prefix) { + return m_ht.equal_prefix_range(prefix.data(), prefix.size()); + } + + /** + * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) + */ + std::pair equal_prefix_range(const std::basic_string& prefix) const { + return m_ht.equal_prefix_range(prefix.data(), prefix.size()); + } +#endif + + + + /** + * Return the element in the trie which is the longest prefix of `key`. If no + * element in the trie is a prefix of `key`, the end iterator is returned. + * + * Example: + * + * tsl::htrie_map map = {{"/foo", 1}, {"/foo/bar", 1}}; + * + * map.longest_prefix("/foo"); // returns {"/foo", 1} + * map.longest_prefix("/foo/baz"); // returns {"/foo", 1} + * map.longest_prefix("/foo/bar/baz"); // returns {"/foo/bar", 1} + * map.longest_prefix("/foo/bar/"); // returns {"/foo/bar", 1} + * map.longest_prefix("/bar"); // returns end() + * map.longest_prefix(""); // returns end() + */ + iterator longest_prefix_ks(const CharT* key, size_type key_size) { + return m_ht.longest_prefix(key, key_size); + } + + /** + * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) + */ + const_iterator longest_prefix_ks(const CharT* key, size_type key_size) const { + return m_ht.longest_prefix(key, key_size); + } +#ifdef TSL_HT_HAS_STRING_VIEW + /** + * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) + */ + iterator longest_prefix(const std::basic_string_view& key) { + return m_ht.longest_prefix(key.data(), key.size()); + } + + /** + * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) + */ + const_iterator longest_prefix(const std::basic_string_view& key) const { + return m_ht.longest_prefix(key.data(), key.size()); + } +#else + /** + * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) + */ + iterator longest_prefix(const CharT* key) { + return m_ht.longest_prefix(key, std::strlen(key)); + } + + /** + * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) + */ + const_iterator longest_prefix(const CharT* key) const { + return m_ht.longest_prefix(key, std::strlen(key)); + } + + /** + * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) + */ + iterator longest_prefix(const std::basic_string& key) { + return m_ht.longest_prefix(key.data(), key.size()); + } + + /** + * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) + */ + const_iterator longest_prefix(const std::basic_string& key) const { + return m_ht.longest_prefix(key.data(), key.size()); + } +#endif + + + + /* + * Hash policy + */ + float max_load_factor() const { return m_ht.max_load_factor(); } + void max_load_factor(float ml) { m_ht.max_load_factor(ml); } + + + /* + * Burst policy + */ + size_type burst_threshold() const { return m_ht.burst_threshold(); } + void burst_threshold(size_type threshold) { m_ht.burst_threshold(threshold); } + + + /* + * Observers + */ + hasher hash_function() const { return m_ht.hash_function(); } + + + + /* + * Other + */ + + /** + * Serialize the map through the `serializer` parameter. + * + * The `serializer` parameter must be a function object that supports the following calls: + * - `void operator()(const U& value);` where the types `std::uint64_t`, `float` and `T` must be supported for U. + * - `void operator()(const CharT* value, std::size_t value_size);` + * + * The implementation leaves binary compatibility (endianness, IEEE 754 for floats, ...) of the types it serializes + * in the hands of the `Serializer` function object if compatibility is required. + */ + template + void serialize(Serializer& serializer) const { + m_ht.serialize(serializer); + } + + + /** + * Deserialize a previously serialized map through the `deserializer` parameter. + * + * The `deserializer` parameter must be a function object that supports the following calls: + * - `template U operator()();` where the types `std::uint64_t`, `float` and `T` must be supported for U. + * - `void operator()(CharT* value_out, std::size_t value_size);` + * + * If the deserialized hash map part of the hat-trie is hash compatible with the serialized map, the deserialization process + * can be sped up by setting `hash_compatible` to true. To be hash compatible, the Hash (take care of the 32-bits vs 64 bits), + * and KeySizeT must behave the same than the ones used in the serialized map. Otherwise the behaviour is undefined + * with `hash_compatible` sets to true. + * + * The behaviour is undefined if the type `CharT` and `T` of the `htrie_map` are not the same as the + * types used during serialization. + * + * The implementation leaves binary compatibility (endianness, IEEE 754 for floats, size of int, ...) of the types it + * deserializes in the hands of the `Deserializer` function object if compatibility is required. + */ + template + static htrie_map deserialize(Deserializer& deserializer, bool hash_compatible = false) { + htrie_map map; + map.m_ht.deserialize(deserializer, hash_compatible); + + return map; + } + + friend bool operator==(const htrie_map& lhs, const htrie_map& rhs) { + if(lhs.size() != rhs.size()) { + return false; + } + + std::string key_buffer; + for(auto it = lhs.cbegin(); it != lhs.cend(); ++it) { + it.key(key_buffer); + + const auto it_element_rhs = rhs.find(key_buffer); + if(it_element_rhs == rhs.cend() || it.value() != it_element_rhs.value()) { + return false; + } + } + + return true; + } + + friend bool operator!=(const htrie_map& lhs, const htrie_map& rhs) { + return !operator==(lhs, rhs); + } + + friend void swap(htrie_map& lhs, htrie_map& rhs) { + lhs.swap(rhs); + } + +private: + template + void insert_pair(const std::pair& value) { + insert(value.first, value.second); + } + + template + void insert_pair(std::pair&& value) { + insert(value.first, std::move(value.second)); + } + +private: + ht m_ht; +}; + +} // end namespace tsl + +#endif diff --git a/ios/include/trie/htrie_set.h b/ios/include/trie/htrie_set.h new file mode 100644 index 00000000..e2f40adc --- /dev/null +++ b/ios/include/trie/htrie_set.h @@ -0,0 +1,586 @@ +/** + * MIT License + * + * Copyright (c) 2017 Thibaut Goetghebuer-Planchon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef TSL_HTRIE_SET_H +#define TSL_HTRIE_SET_H + +#include +#include +#include +#include +#include +#include "htrie_hash.h" + +namespace tsl { + +/** + * Implementation of a hat-trie set. + * + * The size of a key string is limited to std::numeric_limits::max() - 1. + * That is 65 535 characters by default, but can be raised with the KeySizeT template parameter. + * See max_key_size() for an easy access to this limit. + * + * Iterators invalidation: + * - clear, operator=: always invalidate the iterators. + * - insert: always invalidate the iterators. + * - erase: always invalidate the iterators. + */ +template, + class KeySizeT = std::uint16_t> +class htrie_set { +private: + template + using is_iterator = tsl::detail_array_hash::is_iterator; + + using ht = tsl::detail_htrie_hash::htrie_hash; + +public: + using char_type = typename ht::char_type; + using key_size_type = typename ht::key_size_type; + using size_type = typename ht::size_type; + using hasher = typename ht::hasher; + using iterator = typename ht::iterator; + using const_iterator = typename ht::const_iterator; + using prefix_iterator = typename ht::prefix_iterator; + using const_prefix_iterator = typename ht::const_prefix_iterator; + +public: + explicit htrie_set(const Hash& hash = Hash()): m_ht(hash, ht::HASH_NODE_DEFAULT_MAX_LOAD_FACTOR, + ht::DEFAULT_BURST_THRESHOLD) + { + } + + explicit htrie_set(size_type burst_threshold, + const Hash& hash = Hash()): m_ht(hash, ht::HASH_NODE_DEFAULT_MAX_LOAD_FACTOR, + burst_threshold) + { + } + + template::value>::type* = nullptr> + htrie_set(InputIt first, InputIt last, + const Hash& hash = Hash()): htrie_set(hash) + { + insert(first, last); + } + + + +#ifdef TSL_HT_HAS_STRING_VIEW + htrie_set(std::initializer_list> init, + const Hash& hash = Hash()): htrie_set(hash) + { + insert(init); + } +#else + htrie_set(std::initializer_list init, + const Hash& hash = Hash()): htrie_set(hash) + { + insert(init); + } +#endif + + + +#ifdef TSL_HT_HAS_STRING_VIEW + htrie_set& operator=(std::initializer_list> ilist) { + clear(); + insert(ilist); + + return *this; + } +#else + htrie_set& operator=(std::initializer_list ilist) { + clear(); + insert(ilist); + + return *this; + } +#endif + + + + /* + * Iterators + */ + iterator begin() noexcept { return m_ht.begin(); } + const_iterator begin() const noexcept { return m_ht.begin(); } + const_iterator cbegin() const noexcept { return m_ht.cbegin(); } + + iterator end() noexcept { return m_ht.end(); } + const_iterator end() const noexcept { return m_ht.end(); } + const_iterator cend() const noexcept { return m_ht.cend(); } + + + /* + * Capacity + */ + bool empty() const noexcept { return m_ht.empty(); } + size_type size() const noexcept { return m_ht.size(); } + size_type max_size() const noexcept { return m_ht.max_size(); } + size_type max_key_size() const noexcept { return m_ht.max_key_size(); } + + /** + * Call shrink_to_fit() on each hash node of the hat-trie to reduce its size. + */ + void shrink_to_fit() { m_ht.shrink_to_fit(); } + + + /* + * Modifiers + */ + void clear() noexcept { m_ht.clear(); } + + + + std::pair insert_ks(const CharT* key, size_type key_size) { + return m_ht.insert(key, key_size); + } +#ifdef TSL_HT_HAS_STRING_VIEW + std::pair insert(const std::basic_string_view& key) { + return m_ht.insert(key.data(), key.size()); + } +#else + std::pair insert(const CharT* key) { + return m_ht.insert(key, std::strlen(key)); + } + + std::pair insert(const std::basic_string& key) { + return m_ht.insert(key.data(), key.size()); + } +#endif + + + + template::value>::type* = nullptr> + void insert(InputIt first, InputIt last) { + for(auto it = first; it != last; ++it) { + insert(*it); + } + } + + + +#ifdef TSL_HT_HAS_STRING_VIEW + void insert(std::initializer_list> ilist) { + insert(ilist.begin(), ilist.end()); + } +#else + void insert(std::initializer_list ilist) { + insert(ilist.begin(), ilist.end()); + } +#endif + + + + std::pair emplace_ks(const CharT* key, size_type key_size) { + return m_ht.insert(key, key_size); + } +#ifdef TSL_HT_HAS_STRING_VIEW + std::pair emplace(const std::basic_string_view& key) { + return m_ht.insert(key.data(), key.size()); + } +#else + std::pair emplace(const CharT* key) { + return m_ht.insert(key, std::strlen(key)); + } + + std::pair emplace(const std::basic_string& key) { + return m_ht.insert(key.data(), key.size()); + } +#endif + + + + iterator erase(const_iterator pos) { return m_ht.erase(pos); } + iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); } + + + + size_type erase_ks(const CharT* key, size_type key_size) { + return m_ht.erase(key, key_size); + } +#ifdef TSL_HT_HAS_STRING_VIEW + size_type erase(const std::basic_string_view& key) { + return m_ht.erase(key.data(), key.size()); + } +#else + size_type erase(const CharT* key) { + return m_ht.erase(key, std::strlen(key)); + } + + size_type erase(const std::basic_string& key) { + return m_ht.erase(key.data(), key.size()); + } +#endif + + + + /** + * Erase all the elements which have 'prefix' as prefix. Return the number of erase elements. + */ + size_type erase_prefix_ks(const CharT* prefix, size_type prefix_size) { + return m_ht.erase_prefix(prefix, prefix_size); + } +#ifdef TSL_HT_HAS_STRING_VIEW + /** + * @copydoc erase_prefix_ks(const CharT* prefix, size_type prefix_size) + */ + size_type erase_prefix(const std::basic_string_view& prefix) { + return m_ht.erase_prefix(prefix.data(), prefix.size()); + } +#else + /** + * @copydoc erase_prefix_ks(const CharT* prefix, size_type prefix_size) + */ + size_type erase_prefix(const CharT* prefix) { + return m_ht.erase_prefix(prefix, std::strlen(prefix)); + } + + /** + * @copydoc erase_prefix_ks(const CharT* prefix, size_type prefix_size) + */ + size_type erase_prefix(const std::basic_string& prefix) { + return m_ht.erase_prefix(prefix.data(), prefix.size()); + } +#endif + + + + void swap(htrie_set& other) { other.m_ht.swap(m_ht); } + + + /* + * Lookup + */ + size_type count_ks(const CharT* key, size_type key_size) const { return m_ht.count(key, key_size); } +#ifdef TSL_HT_HAS_STRING_VIEW + size_type count(const std::basic_string_view& key) const { return m_ht.count(key.data(), key.size()); } +#else + size_type count(const CharT* key) const { return m_ht.count(key, std::strlen(key)); } + size_type count(const std::basic_string& key) const { return m_ht.count(key.data(), key.size()); } +#endif + + + + iterator find_ks(const CharT* key, size_type key_size) { + return m_ht.find(key, key_size); + } + + const_iterator find_ks(const CharT* key, size_type key_size) const { + return m_ht.find(key, key_size); + } +#ifdef TSL_HT_HAS_STRING_VIEW + iterator find(const std::basic_string_view& key) { + return m_ht.find(key.data(), key.size()); + } + + const_iterator find(const std::basic_string_view& key) const { + return m_ht.find(key.data(), key.size()); + } +#else + iterator find(const CharT* key) { + return m_ht.find(key, std::strlen(key)); + } + + const_iterator find(const CharT* key) const { + return m_ht.find(key, std::strlen(key)); + } + + iterator find(const std::basic_string& key) { + return m_ht.find(key.data(), key.size()); + } + + const_iterator find(const std::basic_string& key) const { + return m_ht.find(key.data(), key.size()); + } +#endif + + + + std::pair equal_range_ks(const CharT* key, size_type key_size) { + return m_ht.equal_range(key, key_size); + } + + std::pair equal_range_ks(const CharT* key, size_type key_size) const { + return m_ht.equal_range(key, key_size); + } +#ifdef TSL_HT_HAS_STRING_VIEW + std::pair equal_range(const std::basic_string_view& key) { + return m_ht.equal_range(key.data(), key.size()); + } + + std::pair equal_range(const std::basic_string_view& key) const { + return m_ht.equal_range(key.data(), key.size()); + } +#else + std::pair equal_range(const CharT* key) { + return m_ht.equal_range(key, std::strlen(key)); + } + + std::pair equal_range(const CharT* key) const { + return m_ht.equal_range(key, std::strlen(key)); + } + + std::pair equal_range(const std::basic_string& key) { + return m_ht.equal_range(key.data(), key.size()); + } + + std::pair equal_range(const std::basic_string& key) const { + return m_ht.equal_range(key.data(), key.size()); + } +#endif + + + + /** + * Return a range containing all the elements which have 'prefix' as prefix. The range is defined by a pair + * of iterator, the first being the begin iterator and the second being the end iterator. + */ + std::pair equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) { + return m_ht.equal_prefix_range(prefix, prefix_size); + } + + /** + * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) + */ + std::pair equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) const { + return m_ht.equal_prefix_range(prefix, prefix_size); + } +#ifdef TSL_HT_HAS_STRING_VIEW + /** + * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) + */ + std::pair equal_prefix_range(const std::basic_string_view& prefix) { + return m_ht.equal_prefix_range(prefix.data(), prefix.size()); + } + + /** + * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) + */ + std::pair equal_prefix_range(const std::basic_string_view& prefix) const { + return m_ht.equal_prefix_range(prefix.data(), prefix.size()); + } +#else + /** + * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) + */ + std::pair equal_prefix_range(const CharT* prefix) { + return m_ht.equal_prefix_range(prefix, std::strlen(prefix)); + } + + /** + * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) + */ + std::pair equal_prefix_range(const CharT* prefix) const { + return m_ht.equal_prefix_range(prefix, std::strlen(prefix)); + } + + /** + * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) + */ + std::pair equal_prefix_range(const std::basic_string& prefix) { + return m_ht.equal_prefix_range(prefix.data(), prefix.size()); + } + + /** + * @copydoc equal_prefix_range_ks(const CharT* prefix, size_type prefix_size) + */ + std::pair equal_prefix_range(const std::basic_string& prefix) const { + return m_ht.equal_prefix_range(prefix.data(), prefix.size()); + } +#endif + + + + /** + * Return the element in the trie which is the longest prefix of `key`. If no + * element in the trie is a prefix of `key`, the end iterator is returned. + * + * Example: + * + * tsl::htrie_set set = {"/foo", "/foo/bar"}; + * + * set.longest_prefix("/foo"); // returns "/foo" + * set.longest_prefix("/foo/baz"); // returns "/foo" + * set.longest_prefix("/foo/bar/baz"); // returns "/foo/bar" + * set.longest_prefix("/foo/bar/"); // returns "/foo/bar" + * set.longest_prefix("/bar"); // returns end() + * set.longest_prefix(""); // returns end() + */ + iterator longest_prefix_ks(const CharT* key, size_type key_size) { + return m_ht.longest_prefix(key, key_size); + } + + /** + * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) + */ + const_iterator longest_prefix_ks(const CharT* key, size_type key_size) const { + return m_ht.longest_prefix(key, key_size); + } +#ifdef TSL_HT_HAS_STRING_VIEW + /** + * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) + */ + iterator longest_prefix(const std::basic_string_view& key) { + return m_ht.longest_prefix(key.data(), key.size()); + } + + /** + * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) + */ + const_iterator longest_prefix(const std::basic_string_view& key) const { + return m_ht.longest_prefix(key.data(), key.size()); + } +#else + /** + * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) + */ + iterator longest_prefix(const CharT* key) { + return m_ht.longest_prefix(key, std::strlen(key)); + } + + /** + * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) + */ + const_iterator longest_prefix(const CharT* key) const { + return m_ht.longest_prefix(key, std::strlen(key)); + } + + /** + * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) + */ + iterator longest_prefix(const std::basic_string& key) { + return m_ht.longest_prefix(key.data(), key.size()); + } + + /** + * @copydoc longest_prefix_ks(const CharT* key, size_type key_size) + */ + const_iterator longest_prefix(const std::basic_string& key) const { + return m_ht.longest_prefix(key.data(), key.size()); + } +#endif + + + + /* + * Hash policy + */ + float max_load_factor() const { return m_ht.max_load_factor(); } + void max_load_factor(float ml) { m_ht.max_load_factor(ml); } + + + /* + * Burst policy + */ + size_type burst_threshold() const { return m_ht.burst_threshold(); } + void burst_threshold(size_type threshold) { m_ht.burst_threshold(threshold); } + + + /* + * Observers + */ + hasher hash_function() const { return m_ht.hash_function(); } + + + + /* + * Other + */ + + /** + * Serialize the set through the `serializer` parameter. + * + * The `serializer` parameter must be a function object that supports the following calls: + * - `void operator()(const U& value);` where the types `std::uint64_t` and `float` must be supported for U. + * - `void operator()(const CharT* value, std::size_t value_size);` + * + * The implementation leaves binary compatibility (endianness, IEEE 754 for floats, ...) of the types it serializes + * in the hands of the `Serializer` function object if compatibility is required. + */ + template + void serialize(Serializer& serializer) const { + m_ht.serialize(serializer); + } + + + /** + * Deserialize a previously serialized set through the `deserializer` parameter. + * + * The `deserializer` parameter must be a function object that supports the following calls: + * - `template U operator()();` where the types `std::uint64_t` and `float` must be supported for U. + * - `void operator()(CharT* value_out, std::size_t value_size);` + * + * If the deserialized hash set part of the hat-trie is hash compatible with the serialized set, the deserialization process + * can be sped up by setting `hash_compatible` to true. To be hash compatible, the Hash (take care of the 32-bits vs 64 bits), + * and KeySizeT must behave the same than the ones used in the serialized set. Otherwise the behaviour is undefined + * with `hash_compatible` sets to true. + * + * The behaviour is undefined if the type `CharT` of the `htrie_set` is not the same as the + * type used during serialization. + * + * The implementation leaves binary compatibility (endianness, IEEE 754 for floats, size of int, ...) of the types it + * deserializes in the hands of the `Deserializer` function object if compatibility is required. + */ + template + static htrie_set deserialize(Deserializer& deserializer, bool hash_compatible = false) { + htrie_set set; + set.m_ht.deserialize(deserializer, hash_compatible); + + return set; + } + + friend bool operator==(const htrie_set& lhs, const htrie_set& rhs) { + if(lhs.size() != rhs.size()) { + return false; + } + + std::string key_buffer; + for(auto it = lhs.cbegin(); it != lhs.cend(); ++it) { + it.key(key_buffer); + + const auto it_element_rhs = rhs.find(key_buffer); + if(it_element_rhs == rhs.cend()) { + return false; + } + } + + return true; + } + + friend bool operator!=(const htrie_set& lhs, const htrie_set& rhs) { + return !operator==(lhs, rhs); + } + + friend void swap(htrie_set& lhs, htrie_set& rhs) { + lhs.swap(rhs); + } + +private: + ht m_ht; +}; + +} // end namespace tsl + +#endif diff --git a/ios/include/tsl/robin_growth_policy.h b/ios/include/tsl/robin_growth_policy.h new file mode 100644 index 00000000..61595f0a --- /dev/null +++ b/ios/include/tsl/robin_growth_policy.h @@ -0,0 +1,290 @@ +/** + * MIT License + * + * Copyright (c) 2017 Tessil + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef TSL_ROBIN_GROWTH_POLICY_H +#define TSL_ROBIN_GROWTH_POLICY_H + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __EXCEPTIONS +# define THROW(_e, _m) throw _e(_m) +#else +# include +# ifndef NDEBUG +# define THROW(_e, _m) do { fprintf(stderr, _m); std::terminate(); } while(0) +# else +# define THROW(_e, _m) std::terminate() +# endif +#endif + +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +#if __has_builtin(__builtin_expect) +# define TSL_LIKELY( exp ) (__builtin_expect( !!(exp), true )) +#else +# define TSL_LIKELY( exp ) (exp) +#endif + +namespace tsl { +namespace rh { + +/** + * Grow the hash table by a factor of GrowthFactor keeping the bucket count to a power of two. It allows + * the table to use a mask operation instead of a modulo operation to map a hash to a bucket. + * + * GrowthFactor must be a power of two >= 2. + */ +template +class power_of_two_growth_policy { +public: + /** + * Called on the hash table creation and on rehash. The number of buckets for the table is passed in parameter. + * This number is a minimum, the policy may update this value with a higher value if needed (but not lower). + */ + power_of_two_growth_policy(std::size_t& min_bucket_count_in_out) { + if(min_bucket_count_in_out > max_bucket_count()) { + THROW(std::length_error, "The hash table exceeds its maxmimum size."); + } + + static_assert(MIN_BUCKETS_SIZE > 0, "MIN_BUCKETS_SIZE must be > 0."); + const std::size_t min_bucket_count = MIN_BUCKETS_SIZE; + + min_bucket_count_in_out = std::max(min_bucket_count, min_bucket_count_in_out); + min_bucket_count_in_out = round_up_to_power_of_two(min_bucket_count_in_out); + m_mask = min_bucket_count_in_out - 1; + } + + /** + * Return the bucket [0, bucket_count()) to which the hash belongs. + */ + std::size_t bucket_for_hash(std::size_t hash) const noexcept { + return hash & m_mask; + } + + /** + * Return the bucket count to use when the bucket array grows on rehash. + */ + std::size_t next_bucket_count() const { + if((m_mask + 1) > max_bucket_count() / GrowthFactor) { + THROW(std::length_error, "The hash table exceeds its maxmimum size."); + } + + return (m_mask + 1) * GrowthFactor; + } + + /** + * Return the maximum number of buckets supported by the policy. + */ + std::size_t max_bucket_count() const { + // Largest power of two. + return (std::numeric_limits::max() / 2) + 1; + } + +private: + static std::size_t round_up_to_power_of_two(std::size_t value) { + if(is_power_of_two(value)) { + return value; + } + + if(value == 0) { + return 1; + } + + --value; + for(std::size_t i = 1; i < sizeof(std::size_t) * CHAR_BIT; i *= 2) { + value |= value >> i; + } + + return value + 1; + } + + static constexpr bool is_power_of_two(std::size_t value) { + return value != 0 && (value & (value - 1)) == 0; + } + +protected: + static const std::size_t MIN_BUCKETS_SIZE = 2; + static_assert(is_power_of_two(GrowthFactor) && GrowthFactor >= 2, "GrowthFactor must be a power of two >= 2."); + + std::size_t m_mask; +}; + + +/** + * Grow the hash table by GrowthFactor::num / GrowthFactor::den and use a modulo to map a hash + * to a bucket. Slower but it can be usefull if you want a slower growth. + */ +template> +class mod_growth_policy { +public: + mod_growth_policy(std::size_t& min_bucket_count_in_out) { + if(min_bucket_count_in_out > max_bucket_count()) { + THROW(std::length_error, "The hash table exceeds its maxmimum size."); + } + + static_assert(MIN_BUCKETS_SIZE > 0, "MIN_BUCKETS_SIZE must be > 0."); + const std::size_t min_bucket_count = MIN_BUCKETS_SIZE; + + min_bucket_count_in_out = std::max(min_bucket_count, min_bucket_count_in_out); + m_bucket_count = min_bucket_count_in_out; + } + + std::size_t bucket_for_hash(std::size_t hash) const noexcept { + return hash % m_bucket_count; + } + + std::size_t next_bucket_count() const { + if(m_bucket_count == max_bucket_count()) { + THROW(std::length_error, "The hash table exceeds its maxmimum size."); + } + + const double next_bucket_count = std::ceil(double(m_bucket_count) * REHASH_SIZE_MULTIPLICATION_FACTOR); + if(!std::isnormal(next_bucket_count)) { + THROW(std::length_error, "The hash table exceeds its maxmimum size."); + } + + if(next_bucket_count > double(max_bucket_count())) { + return max_bucket_count(); + } + else { + return std::size_t(next_bucket_count); + } + } + + std::size_t max_bucket_count() const { + return MAX_BUCKET_COUNT; + } + +private: + static const std::size_t MIN_BUCKETS_SIZE = 2; + static constexpr double REHASH_SIZE_MULTIPLICATION_FACTOR = 1.0 * GrowthFactor::num / GrowthFactor::den; + static const std::size_t MAX_BUCKET_COUNT = + std::size_t(double( + std::numeric_limits::max() / REHASH_SIZE_MULTIPLICATION_FACTOR + )); + + static_assert(REHASH_SIZE_MULTIPLICATION_FACTOR >= 1.1, "Growth factor should be >= 1.1."); + + std::size_t m_bucket_count; +}; + + + +namespace detail { + +static constexpr const std::array PRIMES = {{ + 5ul, 17ul, 29ul, 37ul, 53ul, 67ul, 79ul, 97ul, 131ul, 193ul, 257ul, 389ul, 521ul, 769ul, 1031ul, 1543ul, 2053ul, + 3079ul, 6151ul, 12289ul, 24593ul, 49157ul, 98317ul, 196613ul, 393241ul, 786433ul, 1572869ul, 3145739ul, + 6291469ul, 12582917ul, 25165843ul, 50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul, + 1610612741ul, 3221225473ul, 4294967291ul +}}; + +template +static constexpr std::size_t mod(std::size_t hash) { return hash % PRIMES[IPrime]; } + +// MOD_PRIME[iprime](hash) returns hash % PRIMES[iprime]. This table allows for faster modulo as the +// compiler can optimize the modulo code better with a constant known at the compilation. +static constexpr const std::array MOD_PRIME = {{ + &mod<0>, &mod<1>, &mod<2>, &mod<3>, &mod<4>, &mod<5>, &mod<6>, &mod<7>, &mod<8>, &mod<9>, &mod<10>, + &mod<11>, &mod<12>, &mod<13>, &mod<14>, &mod<15>, &mod<16>, &mod<17>, &mod<18>, &mod<19>, &mod<20>, + &mod<21>, &mod<22>, &mod<23>, &mod<24>, &mod<25>, &mod<26>, &mod<27>, &mod<28>, &mod<29>, &mod<30>, + &mod<31>, &mod<32>, &mod<33>, &mod<34>, &mod<35>, &mod<36>, &mod<37> , &mod<38> +}}; + +} + +/** + * Grow the hash table by using prime numbers as bucket count. Slower than tsl::rh::power_of_two_growth_policy in + * general but will probably distribute the values around better in the buckets with a poor hash function. + * + * To allow the compiler to optimize the modulo operation, a lookup table is used with constant primes numbers. + * + * With a switch the code would look like: + * \code + * switch(iprime) { // iprime is the current prime of the hash table + * case 0: hash % 5ul; + * break; + * case 1: hash % 17ul; + * break; + * case 2: hash % 29ul; + * break; + * ... + * } + * \endcode + * + * Due to the constant variable in the modulo the compiler is able to optimize the operation + * by a series of multiplications, substractions and shifts. + * + * The 'hash % 5' could become something like 'hash - (hash * 0xCCCCCCCD) >> 34) * 5' in a 64 bits environement. + */ +class prime_growth_policy { +public: + prime_growth_policy(std::size_t& min_bucket_count_in_out) { + auto it_prime = std::lower_bound(detail::PRIMES.begin(), + detail::PRIMES.end(), min_bucket_count_in_out); + if(it_prime == detail::PRIMES.end()) { + THROW(std::length_error, "The hash table exceeds its maxmimum size."); + } + + m_iprime = static_cast(std::distance(detail::PRIMES.begin(), it_prime)); + min_bucket_count_in_out = *it_prime; + } + + std::size_t bucket_for_hash(std::size_t hash) const noexcept { + return detail::MOD_PRIME[m_iprime](hash); + } + + std::size_t next_bucket_count() const { + if(m_iprime + 1 >= detail::PRIMES.size()) { + THROW(std::length_error, "The hash table exceeds its maxmimum size."); + } + + return detail::PRIMES[m_iprime + 1]; + } + + std::size_t max_bucket_count() const { + return detail::PRIMES.back(); + } + +private: + unsigned int m_iprime; + + static_assert(std::numeric_limits::max() >= detail::PRIMES.size(), + "The type of m_iprime is not big enough."); +}; + +} +} + +#endif diff --git a/ios/include/tsl/robin_hash.h b/ios/include/tsl/robin_hash.h new file mode 100644 index 00000000..3f2b8fa4 --- /dev/null +++ b/ios/include/tsl/robin_hash.h @@ -0,0 +1,1252 @@ +/** + * MIT License + * + * Copyright (c) 2017 Tessil + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef TSL_ROBIN_HASH_H +#define TSL_ROBIN_HASH_H + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "robin_growth_policy.h" + + + +#ifndef tsl_assert + #ifdef TSL_DEBUG + #define tsl_assert(expr) assert(expr) + #else + #define tsl_assert(expr) (static_cast(0)) + #endif +#endif + + + +namespace tsl { + +namespace detail_robin_hash { + +template +struct make_void { + using type = void; +}; + +template +struct has_is_transparent: std::false_type { +}; + +template +struct has_is_transparent::type>: std::true_type { +}; + +template +struct is_power_of_two_policy: std::false_type { +}; + +template +struct is_power_of_two_policy>: std::true_type { +}; + + + +using truncated_hash_type = std::uint_least32_t; + +/** + * Helper class that store a truncated hash if StoreHash is true and nothing otherwise. + */ +template +class bucket_entry_hash { +public: + bool bucket_hash_equal(std::size_t /*hash*/) const noexcept { + return true; + } + + truncated_hash_type truncated_hash() const noexcept { + return 0; + } + +protected: + void set_hash(truncated_hash_type /*hash*/) noexcept { + } +}; + +template<> +class bucket_entry_hash { +public: + bool bucket_hash_equal(std::size_t hash) const noexcept { + return m_hash == truncated_hash_type(hash); + } + + truncated_hash_type truncated_hash() const noexcept { + return m_hash; + } + +protected: + void set_hash(truncated_hash_type hash) noexcept { + m_hash = truncated_hash_type(hash); + } + +private: + truncated_hash_type m_hash; +}; + + +/** + * Each bucket entry has: + * - A value of type `ValueType`. + * - An integer to store how far the value of the bucket, if any, is from its ideal bucket + * (ex: if the current bucket 5 has the value 'foo' and `hash('foo') % nb_buckets` == 3, + * `dist_from_ideal_bucket()` will return 2 as the current value of the bucket is two + * buckets away from its ideal bucket) + * If there is no value in the bucket (i.e. `empty()` is true) `dist_from_ideal_bucket()` will be < 0. + * - A marker which tells us if the bucket is the last bucket of the bucket array (useful for the + * iterator of the hash table). + * - If `StoreHash` is true, 32 bits of the hash of the value, if any, are also stored in the bucket. + * If the size of the hash is more than 32 bits, it is truncated. We don't store the full hash + * as storing the hash is a potential opportunity to use the unused space due to the alignement + * of the bucket_entry structure. We can thus potentially store the hash without any extra space + * (which would not be possible with 64 bits of the hash). + */ +template +class bucket_entry: public bucket_entry_hash { + using bucket_hash = bucket_entry_hash; + +public: + using value_type = ValueType; + using distance_type = std::int_least16_t; + + + bucket_entry() noexcept: bucket_hash(), m_dist_from_ideal_bucket(EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET), + m_last_bucket(false) + { + tsl_assert(empty()); + } + + bucket_entry(const bucket_entry& other) noexcept(std::is_nothrow_copy_constructible::value): + bucket_hash(other), + m_dist_from_ideal_bucket(EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET), + m_last_bucket(other.m_last_bucket) + { + if(!other.empty()) { + ::new (static_cast(std::addressof(m_value))) value_type(other.value()); + m_dist_from_ideal_bucket = other.m_dist_from_ideal_bucket; + } + } + + /** + * Never really used, but still necessary as we must call resize on an empty `std::vector`. + * and we need to support move-only types. See robin_hash constructor for details. + */ + bucket_entry(bucket_entry&& other) noexcept(std::is_nothrow_move_constructible::value): + bucket_hash(std::move(other)), + m_dist_from_ideal_bucket(EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET), + m_last_bucket(other.m_last_bucket) + { + if(!other.empty()) { + ::new (static_cast(std::addressof(m_value))) value_type(std::move(other.value())); + m_dist_from_ideal_bucket = other.m_dist_from_ideal_bucket; + } + } + + bucket_entry& operator=(const bucket_entry& other) + noexcept(std::is_nothrow_copy_constructible::value) + { + if(this != &other) { + clear(); + + bucket_hash::operator=(other); + if(!other.empty()) { + ::new (static_cast(std::addressof(m_value))) value_type(other.value()); + } + + m_dist_from_ideal_bucket = other.m_dist_from_ideal_bucket; + m_last_bucket = other.m_last_bucket; + } + + return *this; + } + + bucket_entry& operator=(bucket_entry&& ) = delete; + + ~bucket_entry() noexcept { + clear(); + } + + void clear() noexcept { + if(!empty()) { + destroy_value(); + m_dist_from_ideal_bucket = EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET; + } + } + + bool empty() const noexcept { + return m_dist_from_ideal_bucket == EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET; + } + + value_type& value() noexcept { + tsl_assert(!empty()); + return *reinterpret_cast(std::addressof(m_value)); + } + + const value_type& value() const noexcept { + tsl_assert(!empty()); + return *reinterpret_cast(std::addressof(m_value)); + } + + distance_type dist_from_ideal_bucket() const noexcept { + return m_dist_from_ideal_bucket; + } + + bool last_bucket() const noexcept { + return m_last_bucket; + } + + void set_as_last_bucket() noexcept { + m_last_bucket = true; + } + + template + void set_value_of_empty_bucket(distance_type dist_from_ideal_bucket, + truncated_hash_type hash, Args&&... value_type_args) + { + tsl_assert(dist_from_ideal_bucket >= 0); + tsl_assert(empty()); + + ::new (static_cast(std::addressof(m_value))) value_type(std::forward(value_type_args)...); + this->set_hash(hash); + m_dist_from_ideal_bucket = dist_from_ideal_bucket; + + tsl_assert(!empty()); + } + + void swap_with_value_in_bucket(distance_type& dist_from_ideal_bucket, + truncated_hash_type& hash, value_type& value) + { + tsl_assert(!empty()); + + using std::swap; + swap(value, this->value()); + swap(dist_from_ideal_bucket, m_dist_from_ideal_bucket); + + // Avoid warning of unused variable if StoreHash is false + (void) hash; + if(StoreHash) { + const truncated_hash_type tmp_hash = this->truncated_hash(); + this->set_hash(hash); + hash = tmp_hash; + } + } + + static truncated_hash_type truncate_hash(std::size_t hash) noexcept { + return truncated_hash_type(hash); + } + +private: + void destroy_value() noexcept { + tsl_assert(!empty()); + value().~value_type(); + } + +private: + using storage = typename std::aligned_storage::type; + + static const distance_type EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET = -1; + + distance_type m_dist_from_ideal_bucket; + bool m_last_bucket; + storage m_value; +}; + + + +/** + * Internal common class used by `robin_map` and `robin_set`. + * + * ValueType is what will be stored by `robin_hash` (usually `std::pair` for map and `Key` for set). + * + * `KeySelect` should be a `FunctionObject` which takes a `ValueType` in parameter and returns a + * reference to the key. + * + * `ValueSelect` should be a `FunctionObject` which takes a `ValueType` in parameter and returns a + * reference to the value. `ValueSelect` should be void if there is no value (in a set for example). + * + * The strong exception guarantee only holds if the expression + * `std::is_nothrow_swappable::value && std::is_nothrow_move_constructible::value` is true. + * + * Behaviour is undefined if the destructor of `ValueType` throws. + */ +template +class robin_hash: private Hash, private KeyEqual, private GrowthPolicy { +private: + template + using has_mapped_type = typename std::integral_constant::value>; + + +public: + template + class robin_iterator; + + using key_type = typename KeySelect::key_type; + using value_type = ValueType; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using hasher = Hash; + using key_equal = KeyEqual; + using allocator_type = Allocator; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + using iterator = robin_iterator; + using const_iterator = robin_iterator; + + +private: + /** + * Either store the hash because we are asked by the `StoreHash` template parameter + * or store the hash because it doesn't cost us anything in size and can be used to speed up rehash. + */ + static constexpr bool STORE_HASH = StoreHash || + ( + (sizeof(tsl::detail_robin_hash::bucket_entry) == + sizeof(tsl::detail_robin_hash::bucket_entry)) + && + (sizeof(std::size_t) == sizeof(truncated_hash_type) || + is_power_of_two_policy::value) + && + // Don't store the hash for primitive types with default hash. + (!std::is_arithmetic::value || + !std::is_same>::value) + ); + + /** + * Only use the stored hash on lookup if we are explictly asked. We are not sure how slow + * the KeyEqual operation is. An extra comparison may slow things down with a fast KeyEqual. + */ + static constexpr bool USE_STORED_HASH_ON_LOOKUP = StoreHash; + + /** + * We can only use the hash on rehash if the size of the hash type is the same as the stored one or + * if we use a power of two modulo. In the case of the power of two modulo, we just mask + * the least significant bytes, we just have to check that the truncated_hash_type didn't truncated + * more bytes. + */ + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-pragmas" +#pragma clang diagnostic ignored "-Wunknown-warning-option" +#pragma clang diagnostic ignored "-Wtautological-constant-compare" +#endif + + static bool USE_STORED_HASH_ON_REHASH(size_type bucket_count) { + (void) bucket_count; + if(STORE_HASH && sizeof(std::size_t) == sizeof(truncated_hash_type)) { + return true; + } + else if(STORE_HASH && is_power_of_two_policy::value) { + tsl_assert(bucket_count > 0); + return (bucket_count - 1) <= std::numeric_limits::max(); + } + else { + return false; + } + } + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + + using bucket_entry = tsl::detail_robin_hash::bucket_entry; + using distance_type = typename bucket_entry::distance_type; + + using buckets_allocator = typename std::allocator_traits::template rebind_alloc; + using buckets_container_type = std::vector; + + +public: + /** + * The 'operator*()' and 'operator->()' methods return a const reference and const pointer respectively to the + * stored value type. + * + * In case of a map, to get a mutable reference to the value associated to a key (the '.second' in the + * stored pair), you have to call 'value()'. + * + * The main reason for this is that if we returned a `std::pair&` instead + * of a `const std::pair&`, the user may modify the key which will put the map in a undefined state. + */ + template + class robin_iterator { + friend class robin_hash; + + private: + using iterator_bucket = typename std::conditional::type; + + + robin_iterator(iterator_bucket it) noexcept: m_iterator(it) { + } + + public: + using iterator_category = std::forward_iterator_tag; + using value_type = const typename robin_hash::value_type; + using difference_type = std::ptrdiff_t; + using reference = value_type&; + using pointer = value_type*; + + + robin_iterator() noexcept { + } + + robin_iterator(const robin_iterator& other) noexcept: m_iterator(other.m_iterator) { + } + + const typename robin_hash::key_type& key() const { + return KeySelect()(m_iterator->value()); + } + + template::value && IsConst>::type* = nullptr> + const typename U::value_type& value() const { + return U()(m_iterator->value()); + } + + template::value && !IsConst>::type* = nullptr> + typename U::value_type& value() { + return U()(m_iterator->value()); + } + + reference operator*() const { + return m_iterator->value(); + } + + pointer operator->() const { + return std::addressof(m_iterator->value()); + } + + robin_iterator& operator++() { + while(true) { + if(m_iterator->last_bucket()) { + ++m_iterator; + return *this; + } + + ++m_iterator; + if(!m_iterator->empty()) { + return *this; + } + } + } + + robin_iterator operator++(int) { + robin_iterator tmp(*this); + ++*this; + + return tmp; + } + + friend bool operator==(const robin_iterator& lhs, const robin_iterator& rhs) { + return lhs.m_iterator == rhs.m_iterator; + } + + friend bool operator!=(const robin_iterator& lhs, const robin_iterator& rhs) { + return !(lhs == rhs); + } + + private: + iterator_bucket m_iterator; + }; + + +public: + robin_hash(size_type bucket_count, + const Hash& hash, + const KeyEqual& equal, + const Allocator& alloc, + float max_load_factor): Hash(hash), KeyEqual(equal), + // We need a non-zero bucket_count + GrowthPolicy(bucket_count == 0?++bucket_count:bucket_count), + m_buckets(alloc), + m_bucket_count(bucket_count), + m_nb_elements(0), + m_grow_on_next_insert(false) + { + if(bucket_count > max_bucket_count()) { + THROW(std::length_error, "The map exceeds its maxmimum size."); + } + + /* + * We can't use the `vector(size_type count, const Allocator& alloc)` constructor + * as it's only available in C++14 and we need to support C++11. We thus must resize after using + * the `vector(const Allocator& alloc)` constructor. + * + * We can't use `vector(size_type count, const T& value, const Allocator& alloc)` as it requires the + * value T to be copyable. + */ + m_buckets.resize(m_bucket_count); + + tsl_assert(!m_buckets.empty()); + m_buckets.back().set_as_last_bucket(); + + + this->max_load_factor(max_load_factor); + } + + robin_hash(const robin_hash& other) = default; + + robin_hash(robin_hash&& other) noexcept(std::is_nothrow_move_constructible::value && + std::is_nothrow_move_constructible::value && + std::is_nothrow_move_constructible::value && + std::is_nothrow_move_constructible::value) + : Hash(std::move(static_cast(other))), + KeyEqual(std::move(static_cast(other))), + GrowthPolicy(std::move(static_cast(other))), + m_buckets(std::move(other.m_buckets)), + m_bucket_count(other.m_bucket_count), + m_nb_elements(other.m_nb_elements), + m_load_threshold(other.m_load_threshold), + m_max_load_factor(other.m_max_load_factor), + m_grow_on_next_insert(other.m_grow_on_next_insert) + { + other.clear(); + } + + robin_hash& operator=(const robin_hash& other) = default; + + robin_hash& operator=(robin_hash&& other) { + other.swap(*this); + other.clear(); + + return *this; + } + + allocator_type get_allocator() const { + return m_buckets.get_allocator(); + } + + + /* + * Iterators + */ + iterator begin() noexcept { + auto begin = m_buckets.begin(); + while(begin != m_buckets.end() && begin->empty()) { + ++begin; + } + + return iterator(begin); + } + + const_iterator begin() const noexcept { + return cbegin(); + } + + const_iterator cbegin() const noexcept { + auto begin = m_buckets.cbegin(); + while(begin != m_buckets.cend() && begin->empty()) { + ++begin; + } + + return const_iterator(begin); + } + + iterator end() noexcept { + return iterator(m_buckets.end()); + } + + const_iterator end() const noexcept { + return cend(); + } + + const_iterator cend() const noexcept { + return const_iterator(m_buckets.cend()); + } + + + /* + * Capacity + */ + bool empty() const noexcept { + return m_nb_elements == 0; + } + + size_type size() const noexcept { + return m_nb_elements; + } + + size_type max_size() const noexcept { + return m_buckets.max_size(); + } + + /* + * Modifiers + */ + void clear() noexcept { + for(auto& bucket: m_buckets) { + bucket.clear(); + } + + m_nb_elements = 0; + m_grow_on_next_insert = false; + } + + + + template + std::pair insert(P&& value) { + return insert_impl(KeySelect()(value), std::forward

(value)); + } + + template + iterator insert(const_iterator hint, P&& value) { + if(hint != cend() && compare_keys(KeySelect()(*hint), KeySelect()(value))) { + return mutable_iterator(hint); + } + + return insert(std::forward

(value)).first; + } + + template + void insert(InputIt first, InputIt last) { + if(std::is_base_of::iterator_category>::value) + { + const auto nb_elements_insert = std::distance(first, last); + const size_type nb_free_buckets = m_load_threshold - size(); + tsl_assert(m_load_threshold >= size()); + + if(nb_elements_insert > 0 && nb_free_buckets < size_type(nb_elements_insert)) { + reserve(size() + size_type(nb_elements_insert)); + } + } + + for(; first != last; ++first) { + insert(*first); + } + } + + + + template + std::pair insert_or_assign(K&& key, M&& obj) { + auto it = try_emplace(std::forward(key), std::forward(obj)); + if(!it.second) { + it.first.value() = std::forward(obj); + } + + return it; + } + + template + iterator insert_or_assign(const_iterator hint, K&& key, M&& obj) { + if(hint != cend() && compare_keys(KeySelect()(*hint), key)) { + auto it = mutable_iterator(hint); + it.value() = std::forward(obj); + + return it; + } + + return insert_or_assign(std::forward(key), std::forward(obj)).first; + } + + + template + std::pair emplace(Args&&... args) { + return insert(value_type(std::forward(args)...)); + } + + template + iterator emplace_hint(const_iterator hint, Args&&... args) { + return insert(hint, value_type(std::forward(args)...)); + } + + + + template + std::pair try_emplace(K&& key, Args&&... args) { + return insert_impl(key, std::piecewise_construct, + std::forward_as_tuple(std::forward(key)), + std::forward_as_tuple(std::forward(args)...)); + } + + template + iterator try_emplace(const_iterator hint, K&& key, Args&&... args) { + if(hint != cend() && compare_keys(KeySelect()(*hint), key)) { + return mutable_iterator(hint); + } + + return try_emplace(std::forward(key), std::forward(args)...).first; + } + + /** + * Here to avoid `template size_type erase(const K& key)` being used when + * we use a iterator instead of a const_iterator. + */ + iterator erase(iterator pos) { + erase_from_bucket(pos); + + /** + * Erase bucket used a backward shift after clearing the bucket. + * Check if there is a new value in the bucket, if not get the next non-empty. + */ + if(pos.m_iterator->empty()) { + ++pos; + } + + return pos; + } + + iterator erase(const_iterator pos) { + return erase(mutable_iterator(pos)); + } + + iterator erase(const_iterator first, const_iterator last) { + if(first == last) { + return mutable_iterator(first); + } + + auto first_mutable = mutable_iterator(first); + auto last_mutable = mutable_iterator(last); + for(auto it = first_mutable.m_iterator; it != last_mutable.m_iterator; ++it) { + if(!it->empty()) { + it->clear(); + m_nb_elements--; + } + } + + if(last_mutable == end()) { + return end(); + } + + + /* + * Backward shift on the values which come after the deleted values. + * We try to move the values closer to their ideal bucket. + */ + std::size_t icloser_bucket = std::size_t(std::distance(m_buckets.begin(), first_mutable.m_iterator)); + std::size_t ito_move_closer_value = std::size_t(std::distance(m_buckets.begin(), last_mutable.m_iterator)); + tsl_assert(ito_move_closer_value > icloser_bucket); + + const std::size_t ireturn_bucket = ito_move_closer_value - + std::min(ito_move_closer_value - icloser_bucket, + std::size_t(m_buckets[ito_move_closer_value].dist_from_ideal_bucket())); + + while(ito_move_closer_value < m_buckets.size() && m_buckets[ito_move_closer_value].dist_from_ideal_bucket() > 0) { + icloser_bucket = ito_move_closer_value - + std::min(ito_move_closer_value - icloser_bucket, + std::size_t(m_buckets[ito_move_closer_value].dist_from_ideal_bucket())); + + + tsl_assert(m_buckets[icloser_bucket].empty()); + const distance_type new_distance = distance_type(m_buckets[ito_move_closer_value].dist_from_ideal_bucket() - + (ito_move_closer_value - icloser_bucket)); + m_buckets[icloser_bucket].set_value_of_empty_bucket(new_distance, + m_buckets[ito_move_closer_value].truncated_hash(), + std::move(m_buckets[ito_move_closer_value].value())); + m_buckets[ito_move_closer_value].clear(); + + + ++icloser_bucket; + ++ito_move_closer_value; + } + + + return iterator(m_buckets.begin() + ireturn_bucket); + } + + + template + size_type erase(const K& key) { + return erase(key, hash_key(key)); + } + + template + size_type erase(const K& key, std::size_t hash) { + auto it = find(key, hash); + if(it != end()) { + erase_from_bucket(it); + + return 1; + } + else { + return 0; + } + } + + + + + + void swap(robin_hash& other) { + using std::swap; + + swap(static_cast(*this), static_cast(other)); + swap(static_cast(*this), static_cast(other)); + swap(static_cast(*this), static_cast(other)); + swap(m_buckets, other.m_buckets); + swap(m_bucket_count, other.m_bucket_count); + swap(m_nb_elements, other.m_nb_elements); + swap(m_load_threshold, other.m_load_threshold); + swap(m_max_load_factor, other.m_max_load_factor); + swap(m_grow_on_next_insert, other.m_grow_on_next_insert); + } + + + /* + * Lookup + */ + template::value>::type* = nullptr> + typename U::value_type& at(const K& key) { + return at(key, hash_key(key)); + } + + template::value>::type* = nullptr> + typename U::value_type& at(const K& key, std::size_t hash) { + return const_cast(static_cast(this)->at(key, hash)); + } + + + template::value>::type* = nullptr> + const typename U::value_type& at(const K& key) const { + return at(key, hash_key(key)); + } + + template::value>::type* = nullptr> + const typename U::value_type& at(const K& key, std::size_t hash) const { + auto it = find(key, hash); + if(it != cend()) { + return it.value(); + } + else { + THROW(std::out_of_range, "Couldn't find key."); + } + } + + template::value>::type* = nullptr> + typename U::value_type& operator[](K&& key) { + return try_emplace(std::forward(key)).first.value(); + } + + + template + size_type count(const K& key) const { + return count(key, hash_key(key)); + } + + template + size_type count(const K& key, std::size_t hash) const { + if(find(key, hash) != cend()) { + return 1; + } + else { + return 0; + } + } + + + template + iterator find(const K& key) { + return find_impl(key, hash_key(key)); + } + + template + iterator find(const K& key, std::size_t hash) { + return find_impl(key, hash); + } + + + template + const_iterator find(const K& key) const { + return find_impl(key, hash_key(key)); + } + + template + const_iterator find(const K& key, std::size_t hash) const { + return find_impl(key, hash); + } + + + template + std::pair equal_range(const K& key) { + return equal_range(key, hash_key(key)); + } + + template + std::pair equal_range(const K& key, std::size_t hash) { + iterator it = find(key, hash); + return std::make_pair(it, (it == end())?it:std::next(it)); + } + + + template + std::pair equal_range(const K& key) const { + return equal_range(key, hash_key(key)); + } + + template + std::pair equal_range(const K& key, std::size_t hash) const { + const_iterator it = find(key, hash); + return std::make_pair(it, (it == cend())?it:std::next(it)); + } + + /* + * Bucket interface + */ + size_type bucket_count() const { + return m_bucket_count; + } + + size_type max_bucket_count() const { + return std::min(GrowthPolicy::max_bucket_count(), m_buckets.max_size()); + } + + /* + * Hash policy + */ + float load_factor() const { + return float(m_nb_elements)/float(bucket_count()); + } + + float max_load_factor() const { + return m_max_load_factor; + } + + void max_load_factor(float ml) { + m_max_load_factor = std::max(0.1f, std::min(ml, 0.95f)); + m_load_threshold = size_type(float(bucket_count())*m_max_load_factor); + } + + void rehash(size_type count) { + count = std::max(count, size_type(std::ceil(float(size())/max_load_factor()))); + rehash_impl(count); + } + + void reserve(size_type count) { + rehash(size_type(std::ceil(float(count)/max_load_factor()))); + } + + /* + * Observers + */ + hasher hash_function() const { + return static_cast(*this); + } + + key_equal key_eq() const { + return static_cast(*this); + } + + + /* + * Other + */ + iterator mutable_iterator(const_iterator pos) { + return iterator(m_buckets.begin() + std::distance(m_buckets.cbegin(), pos.m_iterator)); + } + +private: + template + std::size_t hash_key(const K& key) const { + return Hash::operator()(key); + } + + template + bool compare_keys(const K1& key1, const K2& key2) const { + return KeyEqual::operator()(key1, key2); + } + + std::size_t bucket_for_hash(std::size_t hash) const { + return GrowthPolicy::bucket_for_hash(hash); + } + + template::value>::type* = nullptr> + std::size_t next_bucket(std::size_t index) const noexcept { + tsl_assert(index < bucket_count()); + + return (index + 1) & this->m_mask; + } + + template::value>::type* = nullptr> + std::size_t next_bucket(std::size_t index) const noexcept { + tsl_assert(index < bucket_count()); + + index++; + return (index != bucket_count())?index:0; + } + + + + template + iterator find_impl(const K& key, std::size_t hash) { + return mutable_iterator(static_cast(this)->find(key, hash)); + } + + template + const_iterator find_impl(const K& key, std::size_t hash) const { + std::size_t ibucket = bucket_for_hash(hash); + distance_type dist_from_ideal_bucket = 0; + + while(dist_from_ideal_bucket <= m_buckets[ibucket].dist_from_ideal_bucket()) { + if (TSL_LIKELY((!USE_STORED_HASH_ON_LOOKUP || m_buckets[ibucket].bucket_hash_equal(hash)) && + compare_keys(KeySelect()(m_buckets[ibucket].value()), key))) + { + return const_iterator(m_buckets.begin() + ibucket); + } + + ibucket = next_bucket(ibucket); + dist_from_ideal_bucket++; + } + + return cend(); + } + + void erase_from_bucket(iterator pos) { + pos.m_iterator->clear(); + m_nb_elements--; + + /** + * Backward shift, swap the empty bucket, previous_ibucket, with the values on its right, ibucket, + * until we cross another empty bucket or if the other bucket has a distance_from_ideal_bucket == 0. + * + * We try to move the values closer to their ideal bucket. + */ + std::size_t previous_ibucket = std::size_t(std::distance(m_buckets.begin(), pos.m_iterator)); + std::size_t ibucket = next_bucket(previous_ibucket); + + while(m_buckets[ibucket].dist_from_ideal_bucket() > 0) { + tsl_assert(m_buckets[previous_ibucket].empty()); + + const distance_type new_distance = distance_type(m_buckets[ibucket].dist_from_ideal_bucket() - 1); + m_buckets[previous_ibucket].set_value_of_empty_bucket(new_distance, m_buckets[ibucket].truncated_hash(), + std::move(m_buckets[ibucket].value())); + m_buckets[ibucket].clear(); + + previous_ibucket = ibucket; + ibucket = next_bucket(ibucket); + } + } + + template + std::pair insert_impl(const K& key, Args&&... value_type_args) { + const std::size_t hash = hash_key(key); + + std::size_t ibucket = bucket_for_hash(hash); + distance_type dist_from_ideal_bucket = 0; + + while(dist_from_ideal_bucket <= m_buckets[ibucket].dist_from_ideal_bucket()) { + if((!USE_STORED_HASH_ON_LOOKUP || m_buckets[ibucket].bucket_hash_equal(hash)) && + compare_keys(KeySelect()(m_buckets[ibucket].value()), key)) + { + return std::make_pair(iterator(m_buckets.begin() + ibucket), false); + } + + ibucket = next_bucket(ibucket); + dist_from_ideal_bucket++; + } + + if(grow_on_high_load()) { + ibucket = bucket_for_hash(hash); + dist_from_ideal_bucket = 0; + + while(dist_from_ideal_bucket <= m_buckets[ibucket].dist_from_ideal_bucket()) { + ibucket = next_bucket(ibucket); + dist_from_ideal_bucket++; + } + } + + + if(m_buckets[ibucket].empty()) { + m_buckets[ibucket].set_value_of_empty_bucket(dist_from_ideal_bucket, bucket_entry::truncate_hash(hash), + std::forward(value_type_args)...); + } + else { + insert_value(ibucket, dist_from_ideal_bucket, bucket_entry::truncate_hash(hash), + std::forward(value_type_args)...); + } + + + m_nb_elements++; + /* + * The value will be inserted in ibucket in any case, either because it was + * empty or by stealing the bucket (robin hood). + */ + return std::make_pair(iterator(m_buckets.begin() + ibucket), true); + } + + + template + void insert_value(std::size_t ibucket, distance_type dist_from_ideal_bucket, + truncated_hash_type hash, Args&&... value_type_args) + { + value_type value(std::forward(value_type_args)...); + insert_value_impl(ibucket, dist_from_ideal_bucket, hash, value); + } + + // fix issue #6 (see https://github.com/Tessil/robin-map/commit/965dacd191502d310f053cc00551ea8fc2f6c7f0) + void insert_value(std::size_t ibucket, distance_type dist_from_ideal_bucket, + truncated_hash_type hash, value_type&& value) + { + insert_value_impl(ibucket, dist_from_ideal_bucket, hash, value); + } + + /* + * We don't use `value_type&& value` as last argument due to a bug in MSVC when `value_type` is a pointer, + * The compiler is not able to see the difference between `std::string*` and `std::string*&&` resulting in + * compile error. + * + * The `value` will be in a moved state at the end of the function. + */ + void insert_value_impl(std::size_t ibucket, distance_type dist_from_ideal_bucket, + truncated_hash_type hash, value_type& value) + { + m_buckets[ibucket].swap_with_value_in_bucket(dist_from_ideal_bucket, hash, value); + ibucket = next_bucket(ibucket); + dist_from_ideal_bucket++; + + while(!m_buckets[ibucket].empty()) { + if(dist_from_ideal_bucket > m_buckets[ibucket].dist_from_ideal_bucket()) { + if(dist_from_ideal_bucket >= REHASH_ON_HIGH_NB_PROBES__NPROBES && + load_factor() >= REHASH_ON_HIGH_NB_PROBES__MIN_LOAD_FACTOR) + { + /** + * The number of probes is really high, rehash the map on the next insert. + * Difficult to do now as rehash may throw. + */ + m_grow_on_next_insert = true; + } + + m_buckets[ibucket].swap_with_value_in_bucket(dist_from_ideal_bucket, hash, value); + } + + ibucket = next_bucket(ibucket); + dist_from_ideal_bucket++; + } + + m_buckets[ibucket].set_value_of_empty_bucket(dist_from_ideal_bucket, hash, std::move(value)); + } + + + void rehash_impl(size_type count) { + robin_hash new_table(count, static_cast(*this), static_cast(*this), + get_allocator(), m_max_load_factor); + + const bool use_stored_hash = USE_STORED_HASH_ON_REHASH(new_table.bucket_count()); + for(auto& bucket: m_buckets) { + if(bucket.empty()) { + continue; + } + + const std::size_t hash = use_stored_hash?bucket.truncated_hash(): + new_table.hash_key(KeySelect()(bucket.value())); + + new_table.insert_value_on_rehash(new_table.bucket_for_hash(hash), 0, + bucket_entry::truncate_hash(hash), std::move(bucket.value())); + } + + new_table.m_nb_elements = m_nb_elements; + new_table.swap(*this); + } + + void insert_value_on_rehash(std::size_t ibucket, distance_type dist_from_ideal_bucket, + truncated_hash_type hash, value_type&& value) + { + while(true) { + if(dist_from_ideal_bucket > m_buckets[ibucket].dist_from_ideal_bucket()) { + if(m_buckets[ibucket].empty()) { + m_buckets[ibucket].set_value_of_empty_bucket(dist_from_ideal_bucket, hash, std::move(value)); + return; + } + else { + m_buckets[ibucket].swap_with_value_in_bucket(dist_from_ideal_bucket, hash, value); + } + } + + dist_from_ideal_bucket++; + ibucket = next_bucket(ibucket); + } + } + + + + /** + * Return true if the map has been rehashed. + */ + bool grow_on_high_load() { + if(m_grow_on_next_insert || size() >= m_load_threshold) { + rehash_impl(GrowthPolicy::next_bucket_count()); + m_grow_on_next_insert = false; + + return true; + } + + return false; + } + + +public: + static const size_type DEFAULT_INIT_BUCKETS_SIZE = 16; + static constexpr float DEFAULT_MAX_LOAD_FACTOR = 0.5f; + +private: + static const distance_type REHASH_ON_HIGH_NB_PROBES__NPROBES = 128; + static constexpr float REHASH_ON_HIGH_NB_PROBES__MIN_LOAD_FACTOR = 0.15f; + +private: + buckets_container_type m_buckets; + + /** + * Used a lot in find, avoid the call to m_buckets.size() which is a bit slower. + */ + size_type m_bucket_count; + + size_type m_nb_elements; + + size_type m_load_threshold; + float m_max_load_factor; + + bool m_grow_on_next_insert; +}; + +} + +} + +#endif diff --git a/ios/include/tsl/robin_map.h b/ios/include/tsl/robin_map.h new file mode 100644 index 00000000..b0cd8748 --- /dev/null +++ b/ios/include/tsl/robin_map.h @@ -0,0 +1,668 @@ +/** + * MIT License + * + * Copyright (c) 2017 Tessil + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef TSL_ROBIN_MAP_H +#define TSL_ROBIN_MAP_H + + +#include +#include +#include +#include +#include +#include +#include "robin_hash.h" + + +namespace tsl { + + +/** + * Implementation of a hash map using open-adressing and the robin hood hashing algorithm with backward shift deletion. + * + * For operations modifying the hash map (insert, erase, rehash, ...), the strong exception guarantee + * is only guaranteed when the expression `std::is_nothrow_swappable>::value && + * std::is_nothrow_move_constructible>::value` is true, otherwise if an exception + * is thrown during the swap or the move, the hash map may end up in a undefined state. Per the standard + * a `Key` or `T` with a noexcept copy constructor and no move constructor also satisfies the + * `std::is_nothrow_move_constructible>::value` criterion (and will thus guarantee the + * strong exception for the map). + * + * When `StoreHash` is true, 32 bits of the hash are stored alongside the values. It can improve + * the performance during lookups if the `KeyEqual` function takes time (if it engenders a cache-miss for example) + * as we then compare the stored hashes before comparing the keys. When `tsl::rh::power_of_two_growth_policy` is used + * as `GrowthPolicy`, it may also speed-up the rehash process as we can avoid to recalculate the hash. + * When it is detected that storing the hash will not incur any memory penality due to alignement (i.e. + * `sizeof(tsl::detail_robin_hash::bucket_entry) == + * sizeof(tsl::detail_robin_hash::bucket_entry)`) and `tsl::rh::power_of_two_growth_policy` is + * used, the hash will be stored even if `StoreHash` is false so that we can speed-up the rehash (but it will + * not be used on lookups unless `StoreHash` is true). + * + * `GrowthPolicy` defines how the map grows and consequently how a hash value is mapped to a bucket. + * By default the map uses `tsl::rh::power_of_two_growth_policy`. This policy keeps the number of buckets + * to a power of two and uses a mask to map the hash to a bucket instead of the slow modulo. + * Other growth policies are available and you may define your own growth policy, + * check `tsl::rh::power_of_two_growth_policy` for the interface. + * + * If the destructor of `Key` or `T` throws an exception, the behaviour of the class is undefined. + * + * Iterators invalidation: + * - clear, operator=, reserve, rehash: always invalidate the iterators. + * - insert, emplace, emplace_hint, operator[]: if there is an effective insert, invalidate the iterators. + * - erase: always invalidate the iterators. + */ +template, + class KeyEqual = std::equal_to, + class Allocator = std::allocator>, + bool StoreHash = false, + class GrowthPolicy = tsl::rh::power_of_two_growth_policy<2>> +class robin_map { +private: + template + using has_is_transparent = tsl::detail_robin_hash::has_is_transparent; + + class KeySelect { + public: + using key_type = Key; + + const key_type& operator()(const std::pair& key_value) const noexcept { + return key_value.first; + } + + key_type& operator()(std::pair& key_value) noexcept { + return key_value.first; + } + }; + + class ValueSelect { + public: + using value_type = T; + + const value_type& operator()(const std::pair& key_value) const noexcept { + return key_value.second; + } + + value_type& operator()(std::pair& key_value) noexcept { + return key_value.second; + } + }; + + using ht = detail_robin_hash::robin_hash, KeySelect, ValueSelect, + Hash, KeyEqual, Allocator, StoreHash, GrowthPolicy>; + +public: + using key_type = typename ht::key_type; + using mapped_type = T; + using value_type = typename ht::value_type; + using size_type = typename ht::size_type; + using difference_type = typename ht::difference_type; + using hasher = typename ht::hasher; + using key_equal = typename ht::key_equal; + using allocator_type = typename ht::allocator_type; + using reference = typename ht::reference; + using const_reference = typename ht::const_reference; + using pointer = typename ht::pointer; + using const_pointer = typename ht::const_pointer; + using iterator = typename ht::iterator; + using const_iterator = typename ht::const_iterator; + + +public: + /* + * Constructors + */ + robin_map(): robin_map(ht::DEFAULT_INIT_BUCKETS_SIZE) { + } + + explicit robin_map(size_type bucket_count, + const Hash& hash = Hash(), + const KeyEqual& equal = KeyEqual(), + const Allocator& alloc = Allocator()): + m_ht(bucket_count, hash, equal, alloc, ht::DEFAULT_MAX_LOAD_FACTOR) + { + } + + robin_map(size_type bucket_count, + const Allocator& alloc): robin_map(bucket_count, Hash(), KeyEqual(), alloc) + { + } + + robin_map(size_type bucket_count, + const Hash& hash, + const Allocator& alloc): robin_map(bucket_count, hash, KeyEqual(), alloc) + { + } + + explicit robin_map(const Allocator& alloc): robin_map(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) { + } + + template + robin_map(InputIt first, InputIt last, + size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE, + const Hash& hash = Hash(), + const KeyEqual& equal = KeyEqual(), + const Allocator& alloc = Allocator()): robin_map(bucket_count, hash, equal, alloc) + { + insert(first, last); + } + + template + robin_map(InputIt first, InputIt last, + size_type bucket_count, + const Allocator& alloc): robin_map(first, last, bucket_count, Hash(), KeyEqual(), alloc) + { + } + + template + robin_map(InputIt first, InputIt last, + size_type bucket_count, + const Hash& hash, + const Allocator& alloc): robin_map(first, last, bucket_count, hash, KeyEqual(), alloc) + { + } + + robin_map(std::initializer_list init, + size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE, + const Hash& hash = Hash(), + const KeyEqual& equal = KeyEqual(), + const Allocator& alloc = Allocator()): + robin_map(init.begin(), init.end(), bucket_count, hash, equal, alloc) + { + } + + robin_map(std::initializer_list init, + size_type bucket_count, + const Allocator& alloc): + robin_map(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(), alloc) + { + } + + robin_map(std::initializer_list init, + size_type bucket_count, + const Hash& hash, + const Allocator& alloc): + robin_map(init.begin(), init.end(), bucket_count, hash, KeyEqual(), alloc) + { + } + + robin_map& operator=(std::initializer_list ilist) { + m_ht.clear(); + + m_ht.reserve(ilist.size()); + m_ht.insert(ilist.begin(), ilist.end()); + + return *this; + } + + allocator_type get_allocator() const { return m_ht.get_allocator(); } + + + /* + * Iterators + */ + iterator begin() noexcept { return m_ht.begin(); } + const_iterator begin() const noexcept { return m_ht.begin(); } + const_iterator cbegin() const noexcept { return m_ht.cbegin(); } + + iterator end() noexcept { return m_ht.end(); } + const_iterator end() const noexcept { return m_ht.end(); } + const_iterator cend() const noexcept { return m_ht.cend(); } + + + /* + * Capacity + */ + bool empty() const noexcept { return m_ht.empty(); } + size_type size() const noexcept { return m_ht.size(); } + size_type max_size() const noexcept { return m_ht.max_size(); } + + /* + * Modifiers + */ + void clear() noexcept { m_ht.clear(); } + + + + std::pair insert(const value_type& value) { + return m_ht.insert(value); + } + + template::value>::type* = nullptr> + std::pair insert(P&& value) { + return m_ht.emplace(std::forward

(value)); + } + + std::pair insert(value_type&& value) { + return m_ht.insert(std::move(value)); + } + + + iterator insert(const_iterator hint, const value_type& value) { + return m_ht.insert(hint, value); + } + + template::value>::type* = nullptr> + iterator insert(const_iterator hint, P&& value) { + return m_ht.emplace_hint(hint, std::forward

(value)); + } + + iterator insert(const_iterator hint, value_type&& value) { + return m_ht.insert(hint, std::move(value)); + } + + + template + void insert(InputIt first, InputIt last) { + m_ht.insert(first, last); + } + + void insert(std::initializer_list ilist) { + m_ht.insert(ilist.begin(), ilist.end()); + } + + + + + template + std::pair insert_or_assign(const key_type& k, M&& obj) { + return m_ht.insert_or_assign(k, std::forward(obj)); + } + + template + std::pair insert_or_assign(key_type&& k, M&& obj) { + return m_ht.insert_or_assign(std::move(k), std::forward(obj)); + } + + template + iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj) { + return m_ht.insert_or_assign(hint, k, std::forward(obj)); + } + + template + iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj) { + return m_ht.insert_or_assign(hint, std::move(k), std::forward(obj)); + } + + + + /** + * Due to the way elements are stored, emplace will need to move or copy the key-value once. + * The method is equivalent to insert(value_type(std::forward(args)...)); + * + * Mainly here for compatibility with the std::unordered_map interface. + */ + template + std::pair emplace(Args&&... args) { + return m_ht.emplace(std::forward(args)...); + } + + + + /** + * Due to the way elements are stored, emplace_hint will need to move or copy the key-value once. + * The method is equivalent to insert(hint, value_type(std::forward(args)...)); + * + * Mainly here for compatibility with the std::unordered_map interface. + */ + template + iterator emplace_hint(const_iterator hint, Args&&... args) { + return m_ht.emplace_hint(hint, std::forward(args)...); + } + + + + + template + std::pair try_emplace(const key_type& k, Args&&... args) { + return m_ht.try_emplace(k, std::forward(args)...); + } + + template + std::pair try_emplace(key_type&& k, Args&&... args) { + return m_ht.try_emplace(std::move(k), std::forward(args)...); + } + + template + iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args) { + return m_ht.try_emplace(hint, k, std::forward(args)...); + } + + template + iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args) { + return m_ht.try_emplace(hint, std::move(k), std::forward(args)...); + } + + + + + iterator erase(iterator pos) { return m_ht.erase(pos); } + iterator erase(const_iterator pos) { return m_ht.erase(pos); } + iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); } + size_type erase(const key_type& key) { return m_ht.erase(key); } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash. + */ + size_type erase(const key_type& key, std::size_t precalculated_hash) { + return m_ht.erase(key, precalculated_hash); + } + + /** + * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. + * If so, K must be hashable and comparable to Key. + */ + template::value>::type* = nullptr> + size_type erase(const K& key) { return m_ht.erase(key); } + + /** + * @copydoc erase(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash. + */ + template::value>::type* = nullptr> + size_type erase(const K& key, std::size_t precalculated_hash) { + return m_ht.erase(key, precalculated_hash); + } + + + + void swap(robin_map& other) { other.m_ht.swap(m_ht); } + + + + /* + * Lookup + */ + T& at(const Key& key) { return m_ht.at(key); } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Usefull to speed-up the lookup if you already have the hash. + */ + T& at(const Key& key, std::size_t precalculated_hash) { return m_ht.at(key, precalculated_hash); } + + + const T& at(const Key& key) const { return m_ht.at(key); } + + /** + * @copydoc at(const Key& key, std::size_t precalculated_hash) + */ + const T& at(const Key& key, std::size_t precalculated_hash) const { return m_ht.at(key, precalculated_hash); } + + + /** + * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. + * If so, K must be hashable and comparable to Key. + */ + template::value>::type* = nullptr> + T& at(const K& key) { return m_ht.at(key); } + + /** + * @copydoc at(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Usefull to speed-up the lookup if you already have the hash. + */ + template::value>::type* = nullptr> + T& at(const K& key, std::size_t precalculated_hash) { return m_ht.at(key, precalculated_hash); } + + + /** + * @copydoc at(const K& key) + */ + template::value>::type* = nullptr> + const T& at(const K& key) const { return m_ht.at(key); } + + /** + * @copydoc at(const K& key, std::size_t precalculated_hash) + */ + template::value>::type* = nullptr> + const T& at(const K& key, std::size_t precalculated_hash) const { return m_ht.at(key, precalculated_hash); } + + + + + T& operator[](const Key& key) { return m_ht[key]; } + T& operator[](Key&& key) { return m_ht[std::move(key)]; } + + + + + size_type count(const Key& key) const { return m_ht.count(key); } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Usefull to speed-up the lookup if you already have the hash. + */ + size_type count(const Key& key, std::size_t precalculated_hash) const { + return m_ht.count(key, precalculated_hash); + } + + /** + * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. + * If so, K must be hashable and comparable to Key. + */ + template::value>::type* = nullptr> + size_type count(const K& key) const { return m_ht.count(key); } + + /** + * @copydoc count(const K& key) const + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Usefull to speed-up the lookup if you already have the hash. + */ + template::value>::type* = nullptr> + size_type count(const K& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); } + + + + + iterator find(const Key& key) { return m_ht.find(key); } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Usefull to speed-up the lookup if you already have the hash. + */ + iterator find(const Key& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); } + + const_iterator find(const Key& key) const { return m_ht.find(key); } + + /** + * @copydoc find(const Key& key, std::size_t precalculated_hash) + */ + const_iterator find(const Key& key, std::size_t precalculated_hash) const { + return m_ht.find(key, precalculated_hash); + } + + /** + * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. + * If so, K must be hashable and comparable to Key. + */ + template::value>::type* = nullptr> + iterator find(const K& key) { return m_ht.find(key); } + + /** + * @copydoc find(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Usefull to speed-up the lookup if you already have the hash. + */ + template::value>::type* = nullptr> + iterator find(const K& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); } + + /** + * @copydoc find(const K& key) + */ + template::value>::type* = nullptr> + const_iterator find(const K& key) const { return m_ht.find(key); } + + /** + * @copydoc find(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Usefull to speed-up the lookup if you already have the hash. + */ + template::value>::type* = nullptr> + const_iterator find(const K& key, std::size_t precalculated_hash) const { + return m_ht.find(key, precalculated_hash); + } + + + + + std::pair equal_range(const Key& key) { return m_ht.equal_range(key); } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Usefull to speed-up the lookup if you already have the hash. + */ + std::pair equal_range(const Key& key, std::size_t precalculated_hash) { + return m_ht.equal_range(key, precalculated_hash); + } + + std::pair equal_range(const Key& key) const { return m_ht.equal_range(key); } + + /** + * @copydoc equal_range(const Key& key, std::size_t precalculated_hash) + */ + std::pair equal_range(const Key& key, std::size_t precalculated_hash) const { + return m_ht.equal_range(key, precalculated_hash); + } + + /** + * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. + * If so, K must be hashable and comparable to Key. + */ + template::value>::type* = nullptr> + std::pair equal_range(const K& key) { return m_ht.equal_range(key); } + + + /** + * @copydoc equal_range(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Usefull to speed-up the lookup if you already have the hash. + */ + template::value>::type* = nullptr> + std::pair equal_range(const K& key, std::size_t precalculated_hash) { + return m_ht.equal_range(key, precalculated_hash); + } + + /** + * @copydoc equal_range(const K& key) + */ + template::value>::type* = nullptr> + std::pair equal_range(const K& key) const { return m_ht.equal_range(key); } + + /** + * @copydoc equal_range(const K& key, std::size_t precalculated_hash) + */ + template::value>::type* = nullptr> + std::pair equal_range(const K& key, std::size_t precalculated_hash) const { + return m_ht.equal_range(key, precalculated_hash); + } + + + + + /* + * Bucket interface + */ + size_type bucket_count() const { return m_ht.bucket_count(); } + size_type max_bucket_count() const { return m_ht.max_bucket_count(); } + + + /* + * Hash policy + */ + float load_factor() const { return m_ht.load_factor(); } + float max_load_factor() const { return m_ht.max_load_factor(); } + void max_load_factor(float ml) { m_ht.max_load_factor(ml); } + + void rehash(size_type count) { m_ht.rehash(count); } + void reserve(size_type count) { m_ht.reserve(count); } + + + /* + * Observers + */ + hasher hash_function() const { return m_ht.hash_function(); } + key_equal key_eq() const { return m_ht.key_eq(); } + + /* + * Other + */ + + /** + * Convert a const_iterator to an iterator. + */ + iterator mutable_iterator(const_iterator pos) { + return m_ht.mutable_iterator(pos); + } + + friend bool operator==(const robin_map& lhs, const robin_map& rhs) { + if(lhs.size() != rhs.size()) { + return false; + } + + for(const auto& element_lhs: lhs) { + const auto it_element_rhs = rhs.find(element_lhs.first); + if(it_element_rhs == rhs.cend() || element_lhs.second != it_element_rhs->second) { + return false; + } + } + + return true; + } + + friend bool operator!=(const robin_map& lhs, const robin_map& rhs) { + return !operator==(lhs, rhs); + } + + friend void swap(robin_map& lhs, robin_map& rhs) { + lhs.swap(rhs); + } + +private: + ht m_ht; +}; + + +/** + * Same as `tsl::robin_map`. + */ +template, + class KeyEqual = std::equal_to, + class Allocator = std::allocator>, + bool StoreHash = false> +using robin_pg_map = robin_map; + +} // end namespace tsl + +#endif diff --git a/ios/include/tsl/robin_set.h b/ios/include/tsl/robin_set.h new file mode 100644 index 00000000..da47b293 --- /dev/null +++ b/ios/include/tsl/robin_set.h @@ -0,0 +1,535 @@ +/** + * MIT License + * + * Copyright (c) 2017 Tessil + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef TSL_ROBIN_SET_H +#define TSL_ROBIN_SET_H + + +#include +#include +#include +#include +#include +#include +#include "robin_hash.h" + + +namespace tsl { + + +/** + * Implementation of a hash set using open-adressing and the robin hood hashing algorithm with backward shift deletion. + * + * For operations modifying the hash set (insert, erase, rehash, ...), the strong exception guarantee + * is only guaranteed when the expression `std::is_nothrow_swappable::value && + * std::is_nothrow_move_constructible::value` is true, otherwise if an exception + * is thrown during the swap or the move, the hash set may end up in a undefined state. Per the standard + * a `Key` with a noexcept copy constructor and no move constructor also satisfies the + * `std::is_nothrow_move_constructible::value` criterion (and will thus guarantee the + * strong exception for the set). + * + * When `StoreHash` is true, 32 bits of the hash are stored alongside the values. It can improve + * the performance during lookups if the `KeyEqual` function takes time (or engenders a cache-miss for example) + * as we then compare the stored hashes before comparing the keys. When `tsl::rh::power_of_two_growth_policy` is used + * as `GrowthPolicy`, it may also speed-up the rehash process as we can avoid to recalculate the hash. + * When it is detected that storing the hash will not incur any memory penality due to alignement (i.e. + * `sizeof(tsl::detail_robin_hash::bucket_entry) == + * sizeof(tsl::detail_robin_hash::bucket_entry)`) and `tsl::rh::power_of_two_growth_policy` is + * used, the hash will be stored even if `StoreHash` is false so that we can speed-up the rehash (but it will + * not be used on lookups unless `StoreHash` is true). + * + * `GrowthPolicy` defines how the set grows and consequently how a hash value is mapped to a bucket. + * By default the set uses `tsl::rh::power_of_two_growth_policy`. This policy keeps the number of buckets + * to a power of two and uses a mask to set the hash to a bucket instead of the slow modulo. + * Other growth policies are available and you may define your own growth policy, + * check `tsl::rh::power_of_two_growth_policy` for the interface. + * + * If the destructor of `Key` throws an exception, the behaviour of the class is undefined. + * + * Iterators invalidation: + * - clear, operator=, reserve, rehash: always invalidate the iterators. + * - insert, emplace, emplace_hint, operator[]: if there is an effective insert, invalidate the iterators. + * - erase: always invalidate the iterators. + */ +template, + class KeyEqual = std::equal_to, + class Allocator = std::allocator, + bool StoreHash = false, + class GrowthPolicy = tsl::rh::power_of_two_growth_policy<2>> +class robin_set { +private: + template + using has_is_transparent = tsl::detail_robin_hash::has_is_transparent; + + class KeySelect { + public: + using key_type = Key; + + const key_type& operator()(const Key& key) const noexcept { + return key; + } + + key_type& operator()(Key& key) noexcept { + return key; + } + }; + + using ht = detail_robin_hash::robin_hash; + +public: + using key_type = typename ht::key_type; + using value_type = typename ht::value_type; + using size_type = typename ht::size_type; + using difference_type = typename ht::difference_type; + using hasher = typename ht::hasher; + using key_equal = typename ht::key_equal; + using allocator_type = typename ht::allocator_type; + using reference = typename ht::reference; + using const_reference = typename ht::const_reference; + using pointer = typename ht::pointer; + using const_pointer = typename ht::const_pointer; + using iterator = typename ht::iterator; + using const_iterator = typename ht::const_iterator; + + + /* + * Constructors + */ + robin_set(): robin_set(ht::DEFAULT_INIT_BUCKETS_SIZE) { + } + + explicit robin_set(size_type bucket_count, + const Hash& hash = Hash(), + const KeyEqual& equal = KeyEqual(), + const Allocator& alloc = Allocator()): + m_ht(bucket_count, hash, equal, alloc, ht::DEFAULT_MAX_LOAD_FACTOR) + { + } + + robin_set(size_type bucket_count, + const Allocator& alloc): robin_set(bucket_count, Hash(), KeyEqual(), alloc) + { + } + + robin_set(size_type bucket_count, + const Hash& hash, + const Allocator& alloc): robin_set(bucket_count, hash, KeyEqual(), alloc) + { + } + + explicit robin_set(const Allocator& alloc): robin_set(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) { + } + + template + robin_set(InputIt first, InputIt last, + size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE, + const Hash& hash = Hash(), + const KeyEqual& equal = KeyEqual(), + const Allocator& alloc = Allocator()): robin_set(bucket_count, hash, equal, alloc) + { + insert(first, last); + } + + template + robin_set(InputIt first, InputIt last, + size_type bucket_count, + const Allocator& alloc): robin_set(first, last, bucket_count, Hash(), KeyEqual(), alloc) + { + } + + template + robin_set(InputIt first, InputIt last, + size_type bucket_count, + const Hash& hash, + const Allocator& alloc): robin_set(first, last, bucket_count, hash, KeyEqual(), alloc) + { + } + + robin_set(std::initializer_list init, + size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE, + const Hash& hash = Hash(), + const KeyEqual& equal = KeyEqual(), + const Allocator& alloc = Allocator()): + robin_set(init.begin(), init.end(), bucket_count, hash, equal, alloc) + { + } + + robin_set(std::initializer_list init, + size_type bucket_count, + const Allocator& alloc): + robin_set(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(), alloc) + { + } + + robin_set(std::initializer_list init, + size_type bucket_count, + const Hash& hash, + const Allocator& alloc): + robin_set(init.begin(), init.end(), bucket_count, hash, KeyEqual(), alloc) + { + } + + + robin_set& operator=(std::initializer_list ilist) { + m_ht.clear(); + + m_ht.reserve(ilist.size()); + m_ht.insert(ilist.begin(), ilist.end()); + + return *this; + } + + allocator_type get_allocator() const { return m_ht.get_allocator(); } + + + /* + * Iterators + */ + iterator begin() noexcept { return m_ht.begin(); } + const_iterator begin() const noexcept { return m_ht.begin(); } + const_iterator cbegin() const noexcept { return m_ht.cbegin(); } + + iterator end() noexcept { return m_ht.end(); } + const_iterator end() const noexcept { return m_ht.end(); } + const_iterator cend() const noexcept { return m_ht.cend(); } + + + /* + * Capacity + */ + bool empty() const noexcept { return m_ht.empty(); } + size_type size() const noexcept { return m_ht.size(); } + size_type max_size() const noexcept { return m_ht.max_size(); } + + /* + * Modifiers + */ + void clear() noexcept { m_ht.clear(); } + + + + + std::pair insert(const value_type& value) { + return m_ht.insert(value); + } + + std::pair insert(value_type&& value) { + return m_ht.insert(std::move(value)); + } + + iterator insert(const_iterator hint, const value_type& value) { + return m_ht.insert(hint, value); + } + + iterator insert(const_iterator hint, value_type&& value) { + return m_ht.insert(hint, std::move(value)); + } + + template + void insert(InputIt first, InputIt last) { + m_ht.insert(first, last); + } + + void insert(std::initializer_list ilist) { + m_ht.insert(ilist.begin(), ilist.end()); + } + + + + + /** + * Due to the way elements are stored, emplace will need to move or copy the key-value once. + * The method is equivalent to insert(value_type(std::forward(args)...)); + * + * Mainly here for compatibility with the std::unordered_map interface. + */ + template + std::pair emplace(Args&&... args) { + return m_ht.emplace(std::forward(args)...); + } + + + + /** + * Due to the way elements are stored, emplace_hint will need to move or copy the key-value once. + * The method is equivalent to insert(hint, value_type(std::forward(args)...)); + * + * Mainly here for compatibility with the std::unordered_map interface. + */ + template + iterator emplace_hint(const_iterator hint, Args&&... args) { + return m_ht.emplace_hint(hint, std::forward(args)...); + } + + + + iterator erase(iterator pos) { return m_ht.erase(pos); } + iterator erase(const_iterator pos) { return m_ht.erase(pos); } + iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); } + size_type erase(const key_type& key) { return m_ht.erase(key); } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash. + */ + size_type erase(const key_type& key, std::size_t precalculated_hash) { + return m_ht.erase(key, precalculated_hash); + } + + /** + * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. + * If so, K must be hashable and comparable to Key. + */ + template::value>::type* = nullptr> + size_type erase(const K& key) { return m_ht.erase(key); } + + /** + * @copydoc erase(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Usefull to speed-up the lookup to the value if you already have the hash. + */ + template::value>::type* = nullptr> + size_type erase(const K& key, std::size_t precalculated_hash) { + return m_ht.erase(key, precalculated_hash); + } + + + + void swap(robin_set& other) { other.m_ht.swap(m_ht); } + + + + /* + * Lookup + */ + size_type count(const Key& key) const { return m_ht.count(key); } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Usefull to speed-up the lookup if you already have the hash. + */ + size_type count(const Key& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); } + + /** + * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. + * If so, K must be hashable and comparable to Key. + */ + template::value>::type* = nullptr> + size_type count(const K& key) const { return m_ht.count(key); } + + /** + * @copydoc count(const K& key) const + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Usefull to speed-up the lookup if you already have the hash. + */ + template::value>::type* = nullptr> + size_type count(const K& key, std::size_t precalculated_hash) const { return m_ht.count(key, precalculated_hash); } + + + + + iterator find(const Key& key) { return m_ht.find(key); } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Usefull to speed-up the lookup if you already have the hash. + */ + iterator find(const Key& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); } + + const_iterator find(const Key& key) const { return m_ht.find(key); } + + /** + * @copydoc find(const Key& key, std::size_t precalculated_hash) + */ + const_iterator find(const Key& key, std::size_t precalculated_hash) const { return m_ht.find(key, precalculated_hash); } + + /** + * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. + * If so, K must be hashable and comparable to Key. + */ + template::value>::type* = nullptr> + iterator find(const K& key) { return m_ht.find(key); } + + /** + * @copydoc find(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Usefull to speed-up the lookup if you already have the hash. + */ + template::value>::type* = nullptr> + iterator find(const K& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); } + + /** + * @copydoc find(const K& key) + */ + template::value>::type* = nullptr> + const_iterator find(const K& key) const { return m_ht.find(key); } + + /** + * @copydoc find(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Usefull to speed-up the lookup if you already have the hash. + */ + template::value>::type* = nullptr> + const_iterator find(const K& key, std::size_t precalculated_hash) const { return m_ht.find(key, precalculated_hash); } + + + + + std::pair equal_range(const Key& key) { return m_ht.equal_range(key); } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Usefull to speed-up the lookup if you already have the hash. + */ + std::pair equal_range(const Key& key, std::size_t precalculated_hash) { + return m_ht.equal_range(key, precalculated_hash); + } + + std::pair equal_range(const Key& key) const { return m_ht.equal_range(key); } + + /** + * @copydoc equal_range(const Key& key, std::size_t precalculated_hash) + */ + std::pair equal_range(const Key& key, std::size_t precalculated_hash) const { + return m_ht.equal_range(key, precalculated_hash); + } + + /** + * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. + * If so, K must be hashable and comparable to Key. + */ + template::value>::type* = nullptr> + std::pair equal_range(const K& key) { return m_ht.equal_range(key); } + + /** + * @copydoc equal_range(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Usefull to speed-up the lookup if you already have the hash. + */ + template::value>::type* = nullptr> + std::pair equal_range(const K& key, std::size_t precalculated_hash) { + return m_ht.equal_range(key, precalculated_hash); + } + + /** + * @copydoc equal_range(const K& key) + */ + template::value>::type* = nullptr> + std::pair equal_range(const K& key) const { return m_ht.equal_range(key); } + + /** + * @copydoc equal_range(const K& key, std::size_t precalculated_hash) + */ + template::value>::type* = nullptr> + std::pair equal_range(const K& key, std::size_t precalculated_hash) const { + return m_ht.equal_range(key, precalculated_hash); + } + + + + + /* + * Bucket interface + */ + size_type bucket_count() const { return m_ht.bucket_count(); } + size_type max_bucket_count() const { return m_ht.max_bucket_count(); } + + + /* + * Hash policy + */ + float load_factor() const { return m_ht.load_factor(); } + float max_load_factor() const { return m_ht.max_load_factor(); } + void max_load_factor(float ml) { m_ht.max_load_factor(ml); } + + void rehash(size_type count) { m_ht.rehash(count); } + void reserve(size_type count) { m_ht.reserve(count); } + + + /* + * Observers + */ + hasher hash_function() const { return m_ht.hash_function(); } + key_equal key_eq() const { return m_ht.key_eq(); } + + + /* + * Other + */ + + /** + * Convert a const_iterator to an iterator. + */ + iterator mutable_iterator(const_iterator pos) { + return m_ht.mutable_iterator(pos); + } + + friend bool operator==(const robin_set& lhs, const robin_set& rhs) { + if(lhs.size() != rhs.size()) { + return false; + } + + for(const auto& element_lhs: lhs) { + const auto it_element_rhs = rhs.find(element_lhs); + if(it_element_rhs == rhs.cend()) { + return false; + } + } + + return true; + } + + friend bool operator!=(const robin_set& lhs, const robin_set& rhs) { + return !operator==(lhs, rhs); + } + + friend void swap(robin_set& lhs, robin_set& rhs) { + lhs.swap(rhs); + } + +private: + ht m_ht; +}; + + +/** + * Same as `tsl::robin_set`. + */ +template, + class KeyEqual = std::equal_to, + class Allocator = std::allocator, + bool StoreHash = false> +using robin_pg_set = robin_set; + +} // end namespace tsl + +#endif + diff --git a/ios/include/utils/Allocator.h b/ios/include/utils/Allocator.h new file mode 100644 index 00000000..23dc7211 --- /dev/null +++ b/ios/include/utils/Allocator.h @@ -0,0 +1,824 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_UTILS_ALLOCATOR_H +#define TNT_UTILS_ALLOCATOR_H + + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +namespace utils { + +namespace pointermath { + +template +static inline P* add(P* a, T b) noexcept { + return (P*)(uintptr_t(a) + uintptr_t(b)); +} + +template +static inline P* align(P* p, size_t alignment) noexcept { + // alignment must be a power-of-two + assert(alignment && !(alignment & alignment-1)); + return (P*)((uintptr_t(p) + alignment - 1) & ~(alignment - 1)); +} + +template +static inline P* align(P* p, size_t alignment, size_t offset) noexcept { + P* const r = align(add(p, offset), alignment); + assert(pointermath::add(r, -offset) >= p); + return r; +} + +} + +/* ------------------------------------------------------------------------------------------------ + * LinearAllocator + * + * + Allocates blocks linearly + * + Cannot free individual blocks + * + Can free top of memory back up to a specified point + * + Doesn't call destructors + * ------------------------------------------------------------------------------------------------ + */ + +class LinearAllocator { +public: + // use memory area provided + LinearAllocator(void* begin, void* end) noexcept; + + template + explicit LinearAllocator(const AREA& area) : LinearAllocator(area.begin(), area.end()) { } + + // Allocators can't be copied + LinearAllocator(const LinearAllocator& rhs) = delete; + LinearAllocator& operator=(const LinearAllocator& rhs) = delete; + + // Allocators can be moved + LinearAllocator(LinearAllocator&& rhs) noexcept; + LinearAllocator& operator=(LinearAllocator&& rhs) noexcept; + + ~LinearAllocator() noexcept = default; + + // our allocator concept + void* alloc(size_t size, size_t alignment = alignof(std::max_align_t), size_t extra = 0) UTILS_RESTRICT { + // branch-less allocation + void* const p = pointermath::align(current(), alignment, extra); + void* const c = pointermath::add(p, size); + bool success = c <= end(); + set_current(success ? c : current()); + return success ? p : nullptr; + } + + // API specific to this allocator + + void *getCurrent() UTILS_RESTRICT noexcept { + return current(); + } + + // free memory back to the specified point + void rewind(void* p) UTILS_RESTRICT noexcept { + assert(p>=mBegin && p + explicit HeapAllocator(const AREA&) { } + + // our allocator concept + void* alloc(size_t size, size_t alignment = alignof(std::max_align_t), size_t extra = 0) { + // this allocator doesn't support 'extra' + assert(extra == 0); + return aligned_alloc(size, alignment); + } + + void free(void* p) noexcept { + aligned_free(p); + } + + void free(void* p, size_t) noexcept { + free(p); + } + + ~HeapAllocator() noexcept = default; + + void swap(HeapAllocator& rhs) noexcept { } +}; + +// ------------------------------------------------------------------------------------------------ + +class FreeList { +public: + FreeList() noexcept = default; + FreeList(void* begin, void* end, size_t elementSize, size_t alignment, size_t extra) noexcept; + FreeList(const FreeList& rhs) = delete; + FreeList& operator=(const FreeList& rhs) = delete; + FreeList(FreeList&& rhs) noexcept = default; + FreeList& operator=(FreeList&& rhs) noexcept = default; + + void* pop() noexcept { + Node* const head = mHead; + mHead = head ? head->next : nullptr; + // this could indicate a use after free + assert(!mHead || mHead >= mBegin && mHead < mEnd); + return head; + } + + void push(void* p) noexcept { + assert(p); + assert(p >= mBegin && p < mEnd); + // TODO: assert this is one of our pointer (i.e.: it's address match one of ours) + Node* const head = static_cast(p); + head->next = mHead; + mHead = head; + } + + void *getFirst() noexcept { + return mHead; + } + +private: + struct Node { + Node* next; + }; + + static Node* init(void* begin, void* end, + size_t elementSize, size_t alignment, size_t extra) noexcept; + + Node* mHead = nullptr; + +#ifndef NDEBUG + // These are needed only for debugging... + void* mBegin = nullptr; + void* mEnd = nullptr; +#endif +}; + +class AtomicFreeList { +public: + AtomicFreeList() noexcept = default; + AtomicFreeList(void* begin, void* end, + size_t elementSize, size_t alignment, size_t extra) noexcept; + AtomicFreeList(const FreeList& rhs) = delete; + AtomicFreeList& operator=(const FreeList& rhs) = delete; + + void* pop() noexcept { + Node* const storage = mStorage; + + HeadPtr currentHead = mHead.load(); + while (currentHead.offset >= 0) { + // The value of "next" we load here might already contain application data if another + // thread raced ahead of us. But in that case, the computed "newHead" will be discarded + // since compare_exchange_weak fails. Then this thread will loop with the updated + // value of currentHead, and try again. + Node* const next = storage[currentHead.offset].next.load(std::memory_order_relaxed); + const HeadPtr newHead{ next ? int32_t(next - storage) : -1, currentHead.tag + 1 }; + // In the rare case that the other thread that raced ahead of us already returned the + // same mHead we just loaded, but it now has a different "next" value, the tag field will not + // match, and compare_exchange_weak will fail and prevent that particular race condition. + if (mHead.compare_exchange_weak(currentHead, newHead)) { + // This assert needs to occur after we have validated that there was no race condition + // Otherwise, next might already contain application data, if another thread + // raced ahead of us after we loaded mHead, but before we loaded mHead->next. + assert(!next || next >= storage); + break; + } + } + void* p = (currentHead.offset >= 0) ? (storage + currentHead.offset) : nullptr; + assert(!p || p >= storage); + return p; + } + + void push(void* p) noexcept { + Node* const storage = mStorage; + assert(p && p >= storage); + Node* const node = static_cast(p); + HeadPtr currentHead = mHead.load(); + HeadPtr newHead = { int32_t(node - storage), currentHead.tag + 1 }; + do { + newHead.tag = currentHead.tag + 1; + Node* const n = (currentHead.offset >= 0) ? (storage + currentHead.offset) : nullptr; + node->next.store(n, std::memory_order_relaxed); + } while(!mHead.compare_exchange_weak(currentHead, newHead)); + } + + void* getFirst() noexcept { + return mStorage + mHead.load(std::memory_order_relaxed).offset; + } + +private: + struct Node { + // This should be a regular (non-atomic) pointer, but this causes TSAN to complain + // about a data-race that exists but is benin. We always use this atomic<> in + // relaxed mode. + // The data race TSAN complains about is when a pop() is interrupted by a + // pop() + push() just after mHead->next is read -- it appears as though it is written + // without synchronization (by the push), however in that case, the pop's CAS will fail + // and things will auto-correct. + // + // Pop() | + // | | + // read head->next | + // | pop() + // | | + // | read head->next + // | CAS, tag++ + // | | + // | push() + // | | + // [TSAN: data-race here] write head->next + // | CAS, tag++ + // CAS fails + // | + // read head->next + // | + // CAS, tag++ + // + std::atomic next; + }; + + // This struct is using a 32-bit offset into the arena rather than + // a direct pointer, because together with the 32-bit tag, it needs to + // fit into 8 bytes. If it was any larger, it would not be possible to + // access it atomically. + struct alignas(8) HeadPtr { + int32_t offset; + uint32_t tag; + }; + + std::atomic mHead{}; + + Node* mStorage = nullptr; +}; + +// ------------------------------------------------------------------------------------------------ + +template < + size_t ELEMENT_SIZE, + size_t ALIGNMENT = alignof(std::max_align_t), + size_t OFFSET = 0, + typename FREELIST = FreeList> +class PoolAllocator { + static_assert(ELEMENT_SIZE >= sizeof(void*), "ELEMENT_SIZE must accommodate at least a pointer"); +public: + // our allocator concept + void* alloc(size_t size = ELEMENT_SIZE, + size_t alignment = ALIGNMENT, size_t offset = OFFSET) noexcept { + assert(size <= ELEMENT_SIZE); + assert(alignment <= ALIGNMENT); + assert(offset == OFFSET); + return mFreeList.pop(); + } + + void free(void* p, size_t = ELEMENT_SIZE) noexcept { + mFreeList.push(p); + } + + constexpr size_t getSize() const noexcept { return ELEMENT_SIZE; } + + PoolAllocator(void* begin, void* end) noexcept + : mFreeList(begin, end, ELEMENT_SIZE, ALIGNMENT, OFFSET) { + } + + template + explicit PoolAllocator(const AREA& area) noexcept + : PoolAllocator(area.begin(), area.end()) { + } + + // Allocators can't be copied + PoolAllocator(const PoolAllocator& rhs) = delete; + PoolAllocator& operator=(const PoolAllocator& rhs) = delete; + + // Allocators can be moved + PoolAllocator(PoolAllocator&& rhs) = default; + PoolAllocator& operator=(PoolAllocator&& rhs) = default; + + PoolAllocator() noexcept = default; + ~PoolAllocator() noexcept = default; + + // API specific to this allocator + + void *getCurrent() noexcept { + return mFreeList.getFirst(); + } + +private: + FREELIST mFreeList; +}; + +#define UTILS_MAX(a,b) ((a) > (b) ? (a) : (b)) + +template +using ObjectPoolAllocator = PoolAllocator; + +template +using ThreadSafeObjectPoolAllocator = PoolAllocator; + + +// ------------------------------------------------------------------------------------------------ +// Areas +// ------------------------------------------------------------------------------------------------ + +namespace AreaPolicy { + +class StaticArea { +public: + StaticArea() noexcept = default; + + StaticArea(void* b, void* e) noexcept + : mBegin(b), mEnd(e) { + } + + ~StaticArea() noexcept = default; + + StaticArea(const StaticArea& rhs) = default; + StaticArea& operator=(const StaticArea& rhs) = default; + StaticArea(StaticArea&& rhs) noexcept = default; + StaticArea& operator=(StaticArea&& rhs) noexcept = default; + + void* data() const noexcept { return mBegin; } + void* begin() const noexcept { return mBegin; } + void* end() const noexcept { return mEnd; } + size_t size() const noexcept { return uintptr_t(mEnd) - uintptr_t(mBegin); } + + friend void swap(StaticArea& lhs, StaticArea& rhs) noexcept { + using std::swap; + swap(lhs.mBegin, rhs.mBegin); + swap(lhs.mEnd, rhs.mEnd); + } + +private: + void* mBegin = nullptr; + void* mEnd = nullptr; +}; + +class HeapArea { +public: + HeapArea() noexcept = default; + + explicit HeapArea(size_t size) { + if (size) { + // TODO: policy committing memory + mBegin = malloc(size); + mEnd = pointermath::add(mBegin, size); + } + } + + ~HeapArea() noexcept { + // TODO: policy for returning memory to system + free(mBegin); + } + + HeapArea(const HeapArea& rhs) = delete; + HeapArea& operator=(const HeapArea& rhs) = delete; + HeapArea(HeapArea&& rhs) noexcept = delete; + HeapArea& operator=(HeapArea&& rhs) noexcept = delete; + + void* data() const noexcept { return mBegin; } + void* begin() const noexcept { return mBegin; } + void* end() const noexcept { return mEnd; } + size_t size() const noexcept { return uintptr_t(mEnd) - uintptr_t(mBegin); } + + friend void swap(HeapArea& lhs, HeapArea& rhs) noexcept { + using std::swap; + swap(lhs.mBegin, rhs.mBegin); + swap(lhs.mEnd, rhs.mEnd); + } + +private: + void* mBegin = nullptr; + void* mEnd = nullptr; +}; + +} // namespace AreaPolicy + +// ------------------------------------------------------------------------------------------------ +// Policies +// ------------------------------------------------------------------------------------------------ + +namespace LockingPolicy { + +struct NoLock { + void lock() noexcept { } + void unlock() noexcept { } +}; + +using SpinLock = utils::SpinLock; +using Mutex = utils::Mutex; + +} // namespace LockingPolicy + + +namespace TrackingPolicy { + +// default no-op tracker +struct Untracked { + Untracked() noexcept = default; + Untracked(const char* name, void* base, size_t size) noexcept { } + void onAlloc(void* p, size_t size, size_t alignment, size_t extra) noexcept { } + void onFree(void* p, size_t = 0) noexcept { } + void onReset() noexcept { } + void onRewind(void* addr) noexcept { } +}; + +// This just track the max memory usage and logs it in the destructor +struct HighWatermark { + HighWatermark() noexcept = default; + HighWatermark(const char* name, void* base, size_t size) noexcept + : mName(name), mBase(base), mSize(uint32_t(size)) { } + ~HighWatermark() noexcept; + void onAlloc(void* p, size_t size, size_t alignment, size_t extra) noexcept; + void onFree(void* p, size_t size) noexcept; + void onReset() noexcept; + void onRewind(void const* addr) noexcept; +protected: + const char* mName = nullptr; + void* mBase = nullptr; + uint32_t mSize = 0; + uint32_t mCurrent = 0; + uint32_t mHighWaterMark = 0; +}; + +// This just fills buffers with known values to help catch uninitialized access and use after free. +struct Debug { + Debug() noexcept = default; + Debug(const char* name, void* base, size_t size) noexcept + : mName(name), mBase(base), mSize(uint32_t(size)) { } + void onAlloc(void* p, size_t size, size_t alignment, size_t extra) noexcept; + void onFree(void* p, size_t size) noexcept; + void onReset() noexcept; + void onRewind(void* addr) noexcept; +protected: + const char* mName = nullptr; + void* mBase = nullptr; + uint32_t mSize = 0; +}; + +struct DebugAndHighWatermark : protected HighWatermark, protected Debug { + DebugAndHighWatermark() noexcept = default; + DebugAndHighWatermark(const char* name, void* base, size_t size) noexcept + : HighWatermark(name, base, size), Debug(name, base, size) { } + void onAlloc(void* p, size_t size, size_t alignment, size_t extra) noexcept { + HighWatermark::onAlloc(p, size, alignment, extra); + Debug::onAlloc(p, size, alignment, extra); + } + void onFree(void* p, size_t size) noexcept { + HighWatermark::onFree(p, size); + Debug::onFree(p, size); + } + void onReset() noexcept { + HighWatermark::onReset(); + Debug::onReset(); + } + void onRewind(void* addr) noexcept { + HighWatermark::onRewind(addr); + Debug::onRewind(addr); + } +}; + +} // namespace TrackingPolicy + +// ------------------------------------------------------------------------------------------------ +// Arenas +// ------------------------------------------------------------------------------------------------ + +template +class Arena { +public: + + Arena() = default; + + // construct an arena with a name and forward argument to its allocator + template + Arena(const char* name, size_t size, ARGS&& ... args) + : mArenaName(name), + mArea(size), + mAllocator(mArea, std::forward(args) ... ), + mListener(name, mArea.data(), mArea.size()) { + } + + template + Arena(const char* name, AreaPolicy&& area, ARGS&& ... args) + : mArenaName(name), + mArea(std::forward(area)), + mAllocator(mArea, std::forward(args) ... ), + mListener(name, mArea.data(), mArea.size()) { + } + + // allocate memory from arena with given size and alignment + // (acceptable size/alignment may depend on the allocator provided) + void* alloc(size_t size, size_t alignment = alignof(std::max_align_t), size_t extra = 0) noexcept { + std::lock_guard guard(mLock); + void* p = mAllocator.alloc(size, alignment, extra); + mListener.onAlloc(p, size, alignment, extra); + return p; + } + + // Allocate an array of trivially destructible objects + // for safety, we disable the object-based alloc method if the object type is not + // trivially destructible, since free() won't call the destructor and this is allocating + // an array. + template ::value>::type> + T* alloc(size_t count, size_t alignment = alignof(T), size_t extra = 0) noexcept { + return (T*)alloc(count * sizeof(T), alignment, extra); + } + + // return memory pointed by p to the arena + // (actual behaviour may depend on allocator provided) + void free(void* p) noexcept { + if (p) { + std::lock_guard guard(mLock); + mListener.onFree(p); + mAllocator.free(p); + } + } + + // some allocators require the size of the allocation for free + void free(void* p, size_t size) noexcept { + if (p) { + std::lock_guard guard(mLock); + mListener.onFree(p, size); + mAllocator.free(p, size); + } + } + + // some allocators don't have a free() call, but a single reset() or rewind() instead + void reset() noexcept { + std::lock_guard guard(mLock); + mListener.onReset(); + mAllocator.reset(); + } + + void* getCurrent() noexcept { return mAllocator.getCurrent(); } + + void rewind(void *addr) noexcept { + std::lock_guard guard(mLock); + mListener.onRewind(addr); + mAllocator.rewind(addr); + } + + // Allocate and construct an object + template + T* make(ARGS&& ... args) noexcept { + void* const p = this->alloc(sizeof(T), ALIGN); + return p ? new(p) T(std::forward(args)...) : nullptr; + } + + // destroys an object created with make() above, and frees associated memory + template + void destroy(T* p) noexcept { + if (p) { + p->~T(); + this->free((void*)p, sizeof(T)); + } + } + + char const* getName() const noexcept { return mArenaName; } + + AllocatorPolicy& getAllocator() noexcept { return mAllocator; } + AllocatorPolicy const& getAllocator() const noexcept { return mAllocator; } + + TrackingPolicy& getListener() noexcept { return mListener; } + TrackingPolicy const& getListener() const noexcept { return mListener; } + + AreaPolicy& getArea() noexcept { return mArea; } + AreaPolicy const& getArea() const noexcept { return mArea; } + + void setListener(TrackingPolicy listener) noexcept { + std::swap(mListener, listener); + } + + template + void emplaceListener(ARGS&& ... args) noexcept { + mListener.~TrackingPolicy(); + new (&mListener) TrackingPolicy(std::forward(args)...); + } + + // An arena can't be copied + Arena(Arena const& rhs) noexcept = delete; + Arena& operator=(Arena const& rhs) noexcept = delete; + + friend void swap(Arena& lhs, Arena& rhs) noexcept { + using std::swap; + swap(lhs.mArea, rhs.mArea); + swap(lhs.mAllocator, rhs.mAllocator); + swap(lhs.mLock, rhs.mLock); + swap(lhs.mListener, rhs.mListener); + swap(lhs.mArenaName, rhs.mArenaName); + } + +private: + char const* mArenaName = nullptr; + AreaPolicy mArea; + // note: we should use something like compressed_pair for the members below + AllocatorPolicy mAllocator; + LockingPolicy mLock; + TrackingPolicy mListener; +}; + +// ------------------------------------------------------------------------------------------------ + +template +using HeapArena = Arena; + +// ------------------------------------------------------------------------------------------------ + +// This doesn't implement our allocator concept, because it's too risky to use this as an allocator +// in particular, doing ArenaScope. +template +class ArenaScope { + + struct Finalizer { + void (*finalizer)(void* p) = nullptr; + Finalizer* next = nullptr; + }; + + template + static void destruct(void* p) noexcept { + static_cast(p)->~T(); + } + +public: + explicit ArenaScope(ARENA& allocator) + : mArena(allocator), mRewind(allocator.getCurrent()) { + } + + ArenaScope& operator=(const ArenaScope& rhs) = delete; + ArenaScope(ArenaScope&& rhs) noexcept = delete; + ArenaScope& operator=(ArenaScope&& rhs) noexcept = delete; + + ~ArenaScope() { + // run the finalizer chain + Finalizer* head = mFinalizerHead; + while (head) { + void* p = pointermath::add(head, sizeof(Finalizer)); + head->finalizer(p); + head = head->next; + } + // ArenaScope works only with Arena that implements rewind() + mArena.rewind(mRewind); + } + + template + T* make(ARGS&& ... args) noexcept { + T* o = nullptr; + if (std::is_trivially_destructible::value) { + o = mArena.template make(std::forward(args)...); + } else { + void* const p = (Finalizer*)mArena.alloc(sizeof(T), ALIGN, sizeof(Finalizer)); + if (p != nullptr) { + Finalizer* const f = static_cast(p) - 1; + // constructor must be called before adding the dtor to the list + // so that the ctor can allocate objects in a nested scope and have the + // finalizers called in reverse order. + o = new(p) T(std::forward(args)...); + f->finalizer = &destruct; + f->next = mFinalizerHead; + mFinalizerHead = f; + } + } + return o; + } + + void* allocate(size_t size, size_t alignment = 1) noexcept { + return mArena.template alloc(size, alignment, 0); + } + + template + T* allocate(size_t size, size_t alignment = alignof(T), size_t extra = 0) noexcept { + return mArena.template alloc(size, alignment, extra); + } + + // use with caution + ARENA& getAllocator() noexcept { return mArena; } + +private: + ARENA& mArena; + void* mRewind = nullptr; + Finalizer* mFinalizerHead = nullptr; +}; + + +template +class STLAllocator { +public: + using value_type = TYPE; + using pointer = TYPE*; + using const_pointer = const TYPE*; + using reference = TYPE&; + using const_reference = const TYPE&; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using propagate_on_container_move_assignment = std::true_type; + using is_always_equal = std::true_type; + + template + struct rebind { using other = STLAllocator; }; + +public: + // we don't make this explicit, so that we can initialize a vector using a STLAllocator + // from an Arena, avoiding to have to repeat the vector type. + STLAllocator(ARENA& arena) : mArena(arena) { } // NOLINT(google-explicit-constructor) + + template + explicit STLAllocator(STLAllocator const& rhs) : mArena(rhs.mArena) { } + + TYPE* allocate(std::size_t n) { + return static_cast(mArena.alloc(n * sizeof(TYPE), alignof(TYPE))); + } + + void deallocate(TYPE* p, std::size_t n) { + mArena.free(p, n * sizeof(TYPE)); + } + + // these should be out-of-class friends, but this doesn't seem to work with some compilers + // which complain about multiple definition each time a STLAllocator<> is instantiated. + template + bool operator==(const STLAllocator& rhs) const noexcept { + return std::addressof(mArena) == std::addressof(rhs.mArena); + } + + template + bool operator!=(const STLAllocator& rhs) const noexcept { + return !operator==(rhs); + } + +private: + template + friend class STLAllocator; + + ARENA& mArena; +}; + +} // namespace utils + +#endif // TNT_UTILS_ALLOCATOR_H diff --git a/ios/include/utils/BinaryTreeArray.h b/ios/include/utils/BinaryTreeArray.h new file mode 100644 index 00000000..50a1d6c8 --- /dev/null +++ b/ios/include/utils/BinaryTreeArray.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_UTILS_BINARYTREEARRAY_H +#define TNT_UTILS_BINARYTREEARRAY_H + +#include + +#include + +#include +#include +#include + +namespace utils { + +class BinaryTreeArray { + + // Simple fixed capacity stack + template::value>::type> + class stack { + TYPE mElements[CAPACITY]; + size_t mSize = 0; + public: + bool empty() const noexcept { return mSize == 0; } + void push(TYPE const& v) noexcept { + assert(mSize < CAPACITY); + mElements[mSize++] = v; + } + void pop() noexcept { + assert(mSize > 0); + --mSize; + } + const TYPE& back() const noexcept { + return mElements[mSize - 1]; + } + }; + +public: + static size_t count(size_t height) noexcept { return (1u << height) - 1; } + static size_t left(size_t i, size_t height) noexcept { return i + 1; } + static size_t right(size_t i, size_t height) noexcept { return i + (1u << (height - 1)); } + + // this builds the depth-first binary tree array top down (post-order) + template + static void traverse(size_t height, Leaf leaf, Node node) noexcept { + + struct TNode { + uint32_t index; + uint32_t col; + uint32_t height; + uint32_t next; + + bool isLeaf() const noexcept { return height == 1; } + size_t left() const noexcept { return BinaryTreeArray::left(index, height); } + size_t right() const noexcept { return BinaryTreeArray::right(index, height); } + }; + + stack stack; + stack.push(TNode{ 0, 0, (uint32_t)height, (uint32_t)count(height) }); + + uint32_t prevLeft = 0; + uint32_t prevRight = 0; + uint32_t prevIndex = 0; + while (!stack.empty()) { + TNode const* const UTILS_RESTRICT curr = &stack.back(); + const bool isLeaf = curr->isLeaf(); + const uint32_t index = curr->index; + const uint32_t l = (uint32_t)curr->left(); + const uint32_t r = (uint32_t)curr->right(); + + if (prevLeft == index || prevRight == index) { + if (!isLeaf) { + // the 'next' node of our left node's right descendants is our right child + stack.push({ l, 2 * curr->col, curr->height - 1, r }); + } + } else if (l == prevIndex) { + if (!isLeaf) { + // the 'next' node of our right child is our own 'next' sibling + stack.push({ r, 2 * curr->col + 1, curr->height - 1, curr->next }); + } + } else { + if (!isLeaf) { + node(index, l, r, curr->next); + } else { + leaf(index, curr->col, curr->next); + } + stack.pop(); + } + + prevLeft = l; + prevRight = r; + prevIndex = index; + } + } +}; + +} // namespace utils + +#endif //TNT_UTILS_BINARYTREEARRAY_H diff --git a/ios/include/utils/BitmaskEnum.h b/ios/include/utils/BitmaskEnum.h new file mode 100644 index 00000000..ff4fc0d9 --- /dev/null +++ b/ios/include/utils/BitmaskEnum.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_UTILS_BITMASKENUM_H +#define TNT_UTILS_BITMASKENUM_H + +#include + +#include // for std::false_type + +#include +#include +#include + +namespace utils { +template +struct EnableBitMaskOperators : public std::false_type { }; +} // namespace utils + +template::value && utils::EnableBitMaskOperators::value, int> = 0> +inline constexpr bool operator!(Enum rhs) noexcept { + using underlying = std::underlying_type_t; + return underlying(rhs) == 0; +} + +template::value && utils::EnableBitMaskOperators::value, int> = 0> +inline constexpr Enum operator~(Enum rhs) noexcept { + using underlying = std::underlying_type_t; + return Enum(~underlying(rhs)); +} + +template::value && utils::EnableBitMaskOperators::value, int> = 0> +inline constexpr Enum operator|(Enum lhs, Enum rhs) noexcept { + using underlying = std::underlying_type_t; + return Enum(underlying(lhs) | underlying(rhs)); +} + +template::value && utils::EnableBitMaskOperators::value, int> = 0> +inline constexpr Enum operator&(Enum lhs, Enum rhs) noexcept { + using underlying = std::underlying_type_t; + return Enum(underlying(lhs) & underlying(rhs)); +} + +template::value && utils::EnableBitMaskOperators::value, int> = 0> +inline constexpr Enum operator^(Enum lhs, Enum rhs) noexcept { + using underlying = std::underlying_type_t; + return Enum(underlying(lhs) ^ underlying(rhs)); +} + +template::value && utils::EnableBitMaskOperators::value, int> = 0> +inline constexpr Enum operator|=(Enum& lhs, Enum rhs) noexcept { + return lhs = lhs | rhs; +} + +template::value && utils::EnableBitMaskOperators::value, int> = 0> +inline constexpr Enum operator&=(Enum& lhs, Enum rhs) noexcept { + return lhs = lhs & rhs; +} + +template::value && utils::EnableBitMaskOperators::value, int> = 0> +inline constexpr Enum operator^=(Enum& lhs, Enum rhs) noexcept { + return lhs = lhs ^ rhs; +} + +template::value && utils::EnableBitMaskOperators::value, int> = 0> +inline constexpr bool none(Enum lhs) noexcept { + return !lhs; +} + +template::value && utils::EnableBitMaskOperators::value, int> = 0> +inline constexpr bool any(Enum lhs) noexcept { + return !none(lhs); +} + + +#endif // TNT_UTILS_BITMASKENUM_H diff --git a/ios/include/utils/CString.h b/ios/include/utils/CString.h new file mode 100644 index 00000000..fb9af1aa --- /dev/null +++ b/ios/include/utils/CString.h @@ -0,0 +1,393 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_CSTRING_H +#define TNT_FILAMENT_CSTRING_H + +// NOTE: this header should not include STL headers + +#include + +#include +#include +#include +#include +#include + +namespace utils { + +//! \privatesection +struct hashCStrings { + typedef const char* argument_type; + typedef size_t result_type; + result_type operator()(argument_type cstr) const noexcept { + size_t hash = 5381; + while (int c = *cstr++) { + hash = (hash * 33u) ^ size_t(c); + } + return hash; + } +}; + +//! \privatesection +struct equalCStrings { + typedef const char* first_argument_type; + typedef const char* second_argument_type; + typedef bool result_type; + bool operator()(const char* lhs, const char* rhs) const noexcept { + return !strcmp(lhs, rhs); + } +}; + +//! \privatesection +struct lessCStrings { + typedef const char* first_argument_type; + typedef const char* second_argument_type; + typedef bool result_type; + result_type operator()(first_argument_type lhs, second_argument_type rhs) const noexcept { + return strcmp(lhs, rhs) < 0; + } +}; + +// This can be used to creates a string from a string literal -- w/o underlying allocations. +// e.g.: +// StaticString s("Hello World!"); +// +template +using StringLiteral = const char[N]; + +//! \publicsection +class UTILS_PUBLIC StaticString { +public: + using value_type = char; + using size_type = uint32_t; + using difference_type = int32_t; + using const_reference = const value_type&; + using const_pointer = const value_type*; + using const_iterator = const value_type*; + + constexpr StaticString() noexcept = default; + + // initialization from a string literal + template + constexpr StaticString(StringLiteral const& other) noexcept // NOLINT(google-explicit-constructor) + : mString(other), + mLength(size_type(N - 1)), + mHash(computeHash(other)) { + } + + // assignment from a string literal + template + StaticString& operator=(StringLiteral const& other) noexcept { + mString = other; + mLength = size_type(N - 1); + mHash = computeHash(other); + return *this; + } + + // helper to make a StaticString from a C string that is known to be a string literal + static constexpr StaticString make(const_pointer literal, size_t length) noexcept { + StaticString r; + r.mString = literal; + r.mLength = size_type(length); + size_type hash = 5381; + while (int c = *literal++) { + hash = (hash * 33u) ^ size_type(c); + } + r.mHash = hash; + return r; + } + + static StaticString make(const_pointer literal) noexcept { + return make(literal, strlen(literal)); + } + + const_pointer c_str() const noexcept { return mString; } + const_pointer data() const noexcept { return mString; } + size_type size() const noexcept { return mLength; } + size_type length() const noexcept { return mLength; } + bool empty() const noexcept { return size() == 0; } + void clear() noexcept { mString = nullptr; mLength = 0; } + + const_iterator begin() const noexcept { return mString; } + const_iterator end() const noexcept { return mString + mLength; } + const_iterator cbegin() const noexcept { return begin(); } + const_iterator cend() const noexcept { return end(); } + + const_reference operator[](size_type pos) const noexcept { + assert(pos < size()); + return begin()[pos]; + } + + const_reference at(size_type pos) const noexcept { + assert(pos < size()); + return begin()[pos]; + } + + const_reference front() const noexcept { + assert(size()); + return begin()[0]; + } + + const_reference back() const noexcept { + assert(size()); + return begin()[size() - 1]; + } + + size_type getHash() const noexcept { return mHash; } + +private: + const_pointer mString = nullptr; + size_type mLength = 0; + size_type mHash = 0; + + template + static constexpr size_type computeHash(StringLiteral const& s) noexcept { + size_type hash = 5381; + for (size_t i = 0; i < N - 1; i++) { + hash = (hash * 33u) ^ size_type(s[i]); + } + return hash; + } + + int compare(const StaticString& rhs) const noexcept; + + friend bool operator==(StaticString const& lhs, StaticString const& rhs) noexcept { + return (lhs.data() == rhs.data()) || + ((lhs.size() == rhs.size()) && !strncmp(lhs.data(), rhs.data(), lhs.size())); + } + friend bool operator!=(StaticString const& lhs, StaticString const& rhs) noexcept { + return !(lhs == rhs); + } + friend bool operator<(StaticString const& lhs, StaticString const& rhs) noexcept { + return lhs.compare(rhs) < 0; + } + friend bool operator>(StaticString const& lhs, StaticString const& rhs) noexcept { + return lhs.compare(rhs) > 0; + } + friend bool operator>=(StaticString const& lhs, StaticString const& rhs) noexcept { + return !(lhs < rhs); + } + friend bool operator<=(StaticString const& lhs, StaticString const& rhs) noexcept { + return !(lhs > rhs); + } +}; + +// ------------------------------------------------------------------------------------------------ + +class UTILS_PUBLIC CString { +public: + using value_type = char; + using size_type = uint32_t; + using difference_type = int32_t; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + using iterator = value_type*; + using const_iterator = const value_type*; + + CString() noexcept = default; + + // Allocates memory and appends a null. This constructor can be used to hold arbitrary data + // inside the string (i.e. it can contain nulls or non-ASCII encodings). + CString(const char* cstr, size_t length); + + // Allocates memory and copies traditional C string content. Unlike the above constructor, this + // does not alllow embedded nulls. This is explicit because this operation is costly. + explicit CString(const char* cstr); + + template + explicit CString(StringLiteral const& other) noexcept // NOLINT(google-explicit-constructor) + : CString(other, N - 1) { + } + + CString(StaticString const& s) : CString(s.c_str(), s.size()) {} + + CString(const CString& rhs); + + CString(CString&& rhs) noexcept { + this->swap(rhs); + } + + + CString& operator=(const CString& rhs); + + CString& operator=(CString&& rhs) noexcept { + this->swap(rhs); + return *this; + } + + ~CString() noexcept { + if (mData) { + free(mData - 1); + } + } + + void swap(CString& other) noexcept { + // don't use std::swap(), we don't want an STL dependency in this file + auto *temp = mCStr; + mCStr = other.mCStr; + other.mCStr = temp; + } + + const_pointer c_str() const noexcept { return mCStr; } + pointer c_str() noexcept { return mCStr; } + const_pointer c_str_safe() const noexcept { return mData ? c_str() : ""; } + const_pointer data() const noexcept { return c_str(); } + pointer data() noexcept { return c_str(); } + size_type size() const noexcept { return mData ? mData[-1].length : 0; } + size_type length() const noexcept { return size(); } + bool empty() const noexcept { return size() == 0; } + + iterator begin() noexcept { return mCStr; } + iterator end() noexcept { return begin() + length(); } + const_iterator begin() const noexcept { return data(); } + const_iterator end() const noexcept { return begin() + length(); } + const_iterator cbegin() const noexcept { return begin(); } + const_iterator cend() const noexcept { return end(); } + + CString& replace(size_type pos, size_type len, const CString& str) noexcept; + CString& insert(size_type pos, const CString& str) noexcept { return replace(pos, 0, str); } + + const_reference operator[](size_type pos) const noexcept { + assert(pos < size()); + return begin()[pos]; + } + + reference operator[](size_type pos) noexcept { + assert(pos < size()); + return begin()[pos]; + } + + const_reference at(size_type pos) const noexcept { + assert(pos < size()); + return begin()[pos]; + } + + reference at(size_type pos) noexcept { + assert(pos < size()); + return begin()[pos]; + } + + reference front() noexcept { + assert(size()); + return begin()[0]; + } + + const_reference front() const noexcept { + assert(size()); + return begin()[0]; + } + + reference back() noexcept { + assert(size()); + return begin()[size() - 1]; + } + + const_reference back() const noexcept { + assert(size()); + return begin()[size() - 1]; + } + + // placement new declared as "throw" to avoid the compiler's null-check + inline void* operator new(size_t size, void* ptr) { + assert(ptr); + return ptr; + } + +private: + struct Data { + size_type length; + }; + + // mCStr points to the C-string or nullptr. if non-null, mCStr is preceded by the string's size + union { + value_type *mCStr = nullptr; + Data* mData; // Data is stored at mData[-1] + }; + + int compare(const CString& rhs) const noexcept { + size_type lhs_size = size(); + size_type rhs_size = rhs.size(); + if (lhs_size < rhs_size) return -1; + if (lhs_size > rhs_size) return 1; + return strncmp(data(), rhs.data(), size()); + } + + friend bool operator==(CString const& lhs, StaticString const& rhs) noexcept { + return (lhs.data() == rhs.data()) || + ((lhs.size() == rhs.size()) && !strncmp(lhs.data(), rhs.data(), lhs.size())); + } + friend bool operator==(CString const& lhs, CString const& rhs) noexcept { + return (lhs.data() == rhs.data()) || + ((lhs.size() == rhs.size()) && !strncmp(lhs.data(), rhs.data(), lhs.size())); + } + friend bool operator!=(CString const& lhs, CString const& rhs) noexcept { + return !(lhs == rhs); + } + friend bool operator<(CString const& lhs, CString const& rhs) noexcept { + return lhs.compare(rhs) < 0; + } + friend bool operator>(CString const& lhs, CString const& rhs) noexcept { + return lhs.compare(rhs) > 0; + } + friend bool operator>=(CString const& lhs, CString const& rhs) noexcept { + return !(lhs < rhs); + } + friend bool operator<=(CString const& lhs, CString const& rhs) noexcept { + return !(lhs > rhs); + } +}; + +// implement this for your type for automatic conversion to CString. Failing to do so leads +// to a compile time failure. +template +CString to_string(T value) noexcept; + +} // namespace utils + +// FIXME: how could we not include this one? +// needed for std::hash, since implementation is inline, this would not cause +// binaries incompatibilities if another STL version was used. +#include + +namespace std { + +//! \privatesection +template<> +struct hash { + typedef utils::CString argument_type; + typedef size_t result_type; + utils::hashCStrings hasher; + size_t operator()(const utils::CString& s) const noexcept { + return hasher(s.c_str()); + } +}; + +//! \privatesection +template<> +struct hash { + typedef utils::StaticString argument_type; + typedef size_t result_type; + size_t operator()(const utils::StaticString& s) const noexcept { + return s.getHash(); + } +}; + +} // namespace std + +#endif // TNT_FILAMENT_CSTRING_H diff --git a/ios/include/utils/CallStack.h b/ios/include/utils/CallStack.h new file mode 100644 index 00000000..3eef679f --- /dev/null +++ b/ios/include/utils/CallStack.h @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef UTILS_CALLSTACK_H_ +#define UTILS_CALLSTACK_H_ + +#include +#include +#include + +#include +#include + +namespace utils { + +/** + * CallStack captures the current's thread call stack. + */ +class CallStack { +public: + /** + * Creates an empty call stack + * @see CallStack::capture() + */ + CallStack() = default; + CallStack(const CallStack&) = default; + ~CallStack() = default; + + /** + * A convenience method to create and capture the stack trace in one go. + * @param ignore number frames to ignore at the top of the stack. + * @return A CallStack object + */ + static CallStack unwind(size_t ignore = 0) noexcept; + + /** + * Capture the current thread's stack and replaces the existing one if any. + * @param ignore number frames to ignore at the top of the stack. + */ + void update(size_t ignore = 0) noexcept; + + /** + * Get the number of stack frames this object has recorded. + * @return How many stack frames are accessible through operator[] + */ + size_t getFrameCount() const noexcept; + + /** + * Return the program-counter of each stack frame captured + * @param index of the frame between 0 and getFrameCount()-1 + * @return the program-counter of the stack-frame recorded at index \p index + * @throw std::out_of_range if the index is out of range + */ + intptr_t operator [](size_t index) const; + + /** Demangles a C++ type name */ + static utils::CString demangleTypeName(const char* mangled); + + template + static utils::CString typeName() { +#if UTILS_HAS_RTTI + return demangleTypeName(typeid(T).name()); +#else + return CString(""); +#endif + } + + /** + * Outputs a CallStack into a stream. + * This will print, when possible, the demangled names of functions corresponding to the + * program-counter recorded. + */ + friend utils::io::ostream& operator <<(utils::io::ostream& stream, const CallStack& callstack); + + bool operator <(const CallStack& rhs) const; + + inline bool operator >(const CallStack& rhs) const { + return rhs < *this; + } + + inline bool operator !=(const CallStack& rhs) const { + return *this < rhs || rhs < *this; + } + + inline bool operator >=(const CallStack& rhs) const { + return !operator <(rhs); + } + + inline bool operator <=(const CallStack& rhs) const { + return !operator >(rhs); + } + + inline bool operator ==(const CallStack& rhs) const { + return !operator !=(rhs); + } + +private: + void update_gcc(size_t ignore) noexcept; + + static utils::CString demangle(const char* mangled); + + static constexpr size_t NUM_FRAMES = 20; + + struct StackFrameInfo { + intptr_t pc; + }; + + size_t m_frame_count = 0; + StackFrameInfo m_stack[NUM_FRAMES]; +}; + +} // namespace utils + +#endif // UTILS_CALLSTACK_H_ diff --git a/ios/include/utils/Condition.h b/ios/include/utils/Condition.h new file mode 100644 index 00000000..f9382fcf --- /dev/null +++ b/ios/include/utils/Condition.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef UTILS_CONDITION_H +#define UTILS_CONDITION_H + +#if defined(__linux__) && !defined(__SANITIZE_THREAD__) +#include +#else +#include +#endif + +#endif // UTILS_CONDITION_H diff --git a/ios/include/utils/CountDownLatch.h b/ios/include/utils/CountDownLatch.h new file mode 100644 index 00000000..541fbe2c --- /dev/null +++ b/ios/include/utils/CountDownLatch.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef UTILS_COUNTDOWNLATCH_H_ +#define UTILS_COUNTDOWNLATCH_H_ + +#include + +// note: we use our version of mutex/condition to keep this public header STL free +#include +#include + +namespace utils { + +/** + * A count down latch is used to block one or several threads until the latch is signaled + * a certain number of times. + * + * Threads entering the latch are blocked until the latch is signaled enough times. + * + * @see CyclicBarrier + */ +class CountDownLatch { +public: + /** + * Creates a count down latch with a specified count. The minimum useful value is 1. + * @param count the latch counter initial value + */ + explicit CountDownLatch(size_t count) noexcept; + ~CountDownLatch() = default; + + /** + * Blocks until latch() is called \p count times. + * @see CountDownLatch(size_t count) + */ + void await() noexcept; + + /** + * Releases threads blocked in await() when called \p count times. Calling latch() more than + * \p count times has no effect. + * @see reset() + */ + void latch() noexcept; + + /** + * Resets the count-down latch to the given value. + * + * @param new_count New latch count. A value of zero will immediately unblock all waiting + * threads. + * + * @warning Use with caution. It's only safe to reset the latch count when you're sure + * that no threads are waiting in await(). This can be guaranteed in various ways, for + * instance, if you have a single thread calling await(), you could call reset() from that + * thread, or you could use a CyclicBarrier to make sure all threads using the CountDownLatch + * are at a known place (i.e.: not in await()) when reset() is called. + */ + void reset(size_t new_count) noexcept; + + /** + * @return the number of times latch() has been called since construction or reset. + * @see reset(), CountDownLatch(size_t count) + */ + size_t getCount() const noexcept; + + CountDownLatch() = delete; + CountDownLatch(const CountDownLatch&) = delete; + CountDownLatch& operator=(const CountDownLatch&) = delete; + +private: + uint32_t m_initial_count; + uint32_t m_remaining_count; + mutable Mutex m_lock; + mutable Condition m_cv; +}; + +} // namespace utils + +#endif // UTILS_COUNTDOWNLATCH_H_ diff --git a/ios/include/utils/CyclicBarrier.h b/ios/include/utils/CyclicBarrier.h new file mode 100644 index 00000000..fb34ee30 --- /dev/null +++ b/ios/include/utils/CyclicBarrier.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef UTILS_CYCLIC_BARRIER_H_ +#define UTILS_CYCLIC_BARRIER_H_ + +#include + +// note: we use our version of mutex/condition to keep this public header STL free +#include +#include + +namespace utils { + +/** + * A cyclic barrier is used to synchronize several threads to a particular execution point. + * + * Threads entering the barrier are halted until all threads reach the barrier. + * + * @see CountDownLatch + */ +class CyclicBarrier { +public: + /** + * Creates a cyclic barrier with a specified number of threads to synchronize. The minimum + * useful value is 2. A value of 0 is invalid and is silently changed to 1. + * @param num_threads Number of threads to synchronize. + */ + explicit CyclicBarrier(size_t num_threads) noexcept; + + /** + * @return The number of thread that are synchronized. + */ + size_t getThreadCount() const noexcept; + + /** + * @return Number of threads currently waiting on the barrier. + */ + size_t getWaitingThreadCount() const noexcept; + + /** + * Blocks until getThreadCount()-1 other threads reach await(). + */ + void await() noexcept; + + /** + * Resets the cyclic barrier to its original state and releases all waiting threads. + */ + void reset() noexcept; + + CyclicBarrier() = delete; + CyclicBarrier(const CyclicBarrier&) = delete; + CyclicBarrier& operator=(const CyclicBarrier&) = delete; + +private: + enum class State { + TRAP, RELEASE + }; + + const size_t m_num_threads; + mutable Mutex m_lock; + mutable Condition m_cv; + + State m_state = State::TRAP; + size_t m_trapped_threads = 0; + size_t m_released_threads = 0; +}; + +} // namespace utils + +#endif // UTILS_CYCLIC_BARRIER_H_ diff --git a/ios/include/utils/Entity.h b/ios/include/utils/Entity.h new file mode 100644 index 00000000..9526cb2d --- /dev/null +++ b/ios/include/utils/Entity.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_UTILS_ENTITY_H +#define TNT_UTILS_ENTITY_H + +#include + +// FIXME: could we get rid of +#include // for std::hash + +#include +#include + +namespace utils { + +class UTILS_PUBLIC Entity { +public: + // this can be used to create an array of to-be-filled entities (see create()) + Entity() noexcept = default; + + // Entities can be copied + Entity(const Entity& e) noexcept = default; + Entity(Entity&& e) noexcept = default; + Entity& operator=(const Entity& e) noexcept = default; + Entity& operator=(Entity&& e) noexcept = default; + + // Entities can be compared + bool operator==(Entity e) const { return e.mIdentity == mIdentity; } + bool operator!=(Entity e) const { return e.mIdentity != mIdentity; } + + // Entities can be sorted + bool operator<(Entity e) const { return e.mIdentity < mIdentity; } + + bool isNull() const noexcept { + return mIdentity == 0; + } + + // an id that can be used for debugging/printing + uint32_t getId() const noexcept { + return mIdentity; + } + + explicit operator bool() const noexcept { return !isNull(); } + + void clear() noexcept { mIdentity = 0; } + + // Exports an entity to an int32_t which can be used "as is" in the Java programing language. + static int32_t smuggle(Entity entity) noexcept { + return int32_t(entity.getId()); + } + + // Imports an entity from an int32_t generated by smuggle() above. + static Entity import(int32_t identity) noexcept { + return Entity{ Type(identity) }; + } + +private: + friend class EntityManager; + friend class EntityManagerImpl; + friend struct std::hash; + using Type = uint32_t; + + explicit Entity(Type identity) noexcept : mIdentity(identity) { } + + Type mIdentity = 0; +}; + +} // namespace utils + + +namespace std { + +template<> +struct hash { + typedef utils::Entity argument_type; + typedef size_t result_type; + result_type operator()(argument_type const& e) const { + return e.getId(); + } +}; + +} // namespace std + +#endif // TNT_UTILS_ENTITY_H diff --git a/ios/include/utils/EntityInstance.h b/ios/include/utils/EntityInstance.h new file mode 100644 index 00000000..a3e84d10 --- /dev/null +++ b/ios/include/utils/EntityInstance.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_FILAMENT_UTILS_ENTITYINSTANCE_H +#define TNT_FILAMENT_UTILS_ENTITYINSTANCE_H + +#include + +#include + +#include + + +namespace utils { + +class UTILS_PUBLIC EntityInstanceBase { +public: + using Type = uint32_t; +protected: + Type mInstance = 0; +}; + +template +class UTILS_PUBLIC EntityInstance : public EntityInstanceBase { +public: + // default Instance is invalid + constexpr EntityInstance() noexcept = default; + + // check if this Instance is valid + constexpr bool isValid() const noexcept { return mInstance != 0; } + + // Instances of same type can be copied/assigned + constexpr EntityInstance(EntityInstance const& other) noexcept = default; + constexpr EntityInstance& operator=(EntityInstance const& other) noexcept = default; + + // EDIT instances can be converted to "read" Instances of same type + template > + constexpr explicit EntityInstance(EntityInstance const& other) noexcept { + mInstance = other.asValue(); + } + template > + EntityInstance& operator=(EntityInstance const& other) noexcept { + mInstance = other.asValue(); + return *this; + } + + // Instances can be compared + constexpr bool operator!=(EntityInstance e) const { return mInstance != e.mInstance; } + constexpr bool operator==(EntityInstance e) const { return mInstance == e.mInstance; } + + // Instances can be sorted + constexpr bool operator<(EntityInstance e) const { return mInstance < e.mInstance; } + constexpr bool operator<=(EntityInstance e) const { return mInstance <= e.mInstance; } + constexpr bool operator>(EntityInstance e) const { return mInstance > e.mInstance; } + constexpr bool operator>=(EntityInstance e) const { return mInstance >= e.mInstance; } + + // and we can iterate + constexpr EntityInstance& operator++() noexcept { ++mInstance; return *this; } + constexpr EntityInstance& operator--() noexcept { --mInstance; return *this; } + constexpr const EntityInstance operator++(int) const noexcept { return EntityInstance{ mInstance + 1 }; } + constexpr const EntityInstance operator--(int) const noexcept { return EntityInstance{ mInstance - 1 }; } + + + // return a value for this Instance (mostly needed for debugging + constexpr uint32_t asValue() const noexcept { return mInstance; } + + // auto convert to Type so it can be used as an index + constexpr operator Type() const noexcept { return mInstance; } // NOLINT(google-explicit-constructor) + + // conversion from Type so we can initialize from an index + constexpr EntityInstance(Type value) noexcept { mInstance = value; } // NOLINT(google-explicit-constructor) +}; + +} // namespace utils + +#endif // TNT_FILAMENT_UTILS_ENTITYINSTANCE_H diff --git a/ios/include/utils/EntityManager.h b/ios/include/utils/EntityManager.h new file mode 100644 index 00000000..f7e1544f --- /dev/null +++ b/ios/include/utils/EntityManager.h @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_UTILS_ENTITYMANAGER_H +#define TNT_UTILS_ENTITYMANAGER_H + +#include +#include + +#include +#include + +#ifndef FILAMENT_UTILS_TRACK_ENTITIES +#define FILAMENT_UTILS_TRACK_ENTITIES false +#endif + +#if FILAMENT_UTILS_TRACK_ENTITIES +#include +#include +#endif + +namespace utils { + +class UTILS_PUBLIC EntityManager { +public: + // Get the global EntityManager. Is is recommended to cache this value. + // Thread Safe. + static EntityManager& get() noexcept; + + class Listener { + public: + virtual void onEntitiesDestroyed(size_t n, Entity const* entities) noexcept = 0; + protected: + ~Listener() noexcept; + }; + + + // maximum number of entities that can exist at the same time + static size_t getMaxEntityCount() noexcept { + // because index 0 is reserved, we only have 2^GENERATION_SHIFT - 1 valid indices + return RAW_INDEX_COUNT - 1; + } + + // create n entities. Thread safe. + void create(size_t n, Entity* entities); + + // destroys n entities. Thread safe. + void destroy(size_t n, Entity* entities) noexcept; + + // create a new Entity. Thread safe. + // Return Entity.isNull() if the entity cannot be allocated. + Entity create() { + Entity e; + create(1, &e); + return e; + } + + // destroys an Entity. Thread safe. + void destroy(Entity e) noexcept { + destroy(1, &e); + } + + // return whether the given Entity has been destroyed (false) or not (true). + // Thread safe. + bool isAlive(Entity e) const noexcept { + assert(getIndex(e) < RAW_INDEX_COUNT); + return (!e.isNull()) && (getGeneration(e) == mGens[getIndex(e)]); + } + + // registers a listener to be called when an entity is destroyed. thread safe. + // if the listener is already register, this method has no effect. + void registerListener(Listener* l) noexcept; + + // unregisters a listener. + void unregisterListener(Listener* l) noexcept; + + + /* no user serviceable parts below */ + + // current generation of the given index. Use for debugging and testing. + uint8_t getGenerationForIndex(size_t index) const noexcept { + return mGens[index]; + } + // singleton, can't be copied + EntityManager(const EntityManager& rhs) = delete; + EntityManager& operator=(const EntityManager& rhs) = delete; + +#if FILAMENT_UTILS_TRACK_ENTITIES + std::vector getActiveEntities() const; + void dumpActiveEntities(utils::io::ostream& out) const; +#endif + +private: + friend class EntityManagerImpl; + EntityManager(); + ~EntityManager(); + + // GENERATION_SHIFT determines how many simultaneous Entities are available, the + // minimum memory requirement is 2^GENERATION_SHIFT bytes. + static constexpr const int GENERATION_SHIFT = 17; + static constexpr const size_t RAW_INDEX_COUNT = (1 << GENERATION_SHIFT); + static constexpr const Entity::Type INDEX_MASK = (1 << GENERATION_SHIFT) - 1u; + + static inline Entity::Type getGeneration(Entity e) noexcept { + return e.getId() >> GENERATION_SHIFT; + } + static inline Entity::Type getIndex(Entity e) noexcept { + return e.getId() & INDEX_MASK; + } + static inline Entity::Type makeIdentity(Entity::Type g, Entity::Type i) noexcept { + return (g << GENERATION_SHIFT) | (i & INDEX_MASK); + } + + // stores the generation of each index. + uint8_t * const mGens; +}; + +} // namespace utils + +#endif // TNT_UTILS_ENTITYMANAGER_H diff --git a/ios/include/utils/FixedCapacityVector.h b/ios/include/utils/FixedCapacityVector.h new file mode 100644 index 00000000..a3f27393 --- /dev/null +++ b/ios/include/utils/FixedCapacityVector.h @@ -0,0 +1,410 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_UTILS_FIXEDCAPACITYVECTOR_H +#define TNT_UTILS_FIXEDCAPACITYVECTOR_H + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#ifndef NDEBUG +#define FILAMENT_FORCE_CAPACITY_CHECK true +#else +#define FILAMENT_FORCE_CAPACITY_CHECK false +#endif + +namespace utils { + +/** + * FixedCapacityVector is (almost) a drop-in replacement for std::vector<> except it has a + * fixed capacity decided at runtime. The vector storage is never reallocated unless reserve() + * is called. Operations that add elements to the vector can fail if there is not enough + * capacity. + * + * An empty vector with a given capacity is created with + * FixedCapacityVector::with_capacity( capacity ); + * + * NOTE: When passing an initial size into the FixedCapacityVector constructor, default construction + * of the elements is skipped when their construction is trivial. This behavior is different from + * std::vector. e.g., std::vector(4) constructs 4 zeros while FixedCapacityVector(4) + * allocates 4 uninitialized values. Note that zero initialization is easily achieved by passing in + * the optional value argument, e.g. FixedCapacityVector(4, 0) or foo.resize(4, 0). + */ +template, bool CapacityCheck = true> +class UTILS_PUBLIC FixedCapacityVector { +public: + using allocator_type = A; + using value_type = T; + using reference = T&; + using const_reference = T const&; + using size_type = uint32_t; + using difference_type = int32_t; + using pointer = T*; + using const_pointer = T const*; + using iterator = pointer; + using const_iterator = const_pointer; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + +private: + using storage_traits = std::allocator_traits; + +public: + /** returns an empty vector with the specified capacity */ + static FixedCapacityVector with_capacity( + size_type capacity, const allocator_type& allocator = allocator_type()) { + return FixedCapacityVector(construct_with_capacity, capacity, allocator); + } + + FixedCapacityVector() = default; + + explicit FixedCapacityVector(const allocator_type& allocator) noexcept + : mCapacityAllocator({}, allocator) { + } + + explicit FixedCapacityVector(size_type size, const allocator_type& allocator = allocator_type()) + : mSize(size), + mCapacityAllocator(size, allocator) { + mData = this->allocator().allocate(this->capacity()); + construct(begin(), end()); + } + + FixedCapacityVector(size_type size, const_reference value, + const allocator_type& alloc = allocator_type()) + : mSize(size), + mCapacityAllocator(size, alloc) { + mData = this->allocator().allocate(this->capacity()); + construct(begin(), end(), value); + } + + FixedCapacityVector(FixedCapacityVector const& rhs) + : mSize(rhs.mSize), + mCapacityAllocator(rhs.capacity(), + storage_traits::select_on_container_copy_construction(rhs.allocator())) { + mData = allocator().allocate(capacity()); + std::uninitialized_copy(rhs.begin(), rhs.end(), begin()); + } + + FixedCapacityVector(FixedCapacityVector&& rhs) noexcept { + this->swap(rhs); + } + + ~FixedCapacityVector() noexcept { + destroy(begin(), end()); + allocator().deallocate(data(), capacity()); + } + + FixedCapacityVector& operator=(FixedCapacityVector const& rhs) { + if (this != &rhs) { + FixedCapacityVector t(rhs); + this->swap(t); + } + return *this; + } + + FixedCapacityVector& operator=(FixedCapacityVector&& rhs) noexcept { + this->swap(rhs); + return *this; + } + + allocator_type get_allocator() const noexcept { + return mCapacityAllocator.second(); + } + + // -------------------------------------------------------------------------------------------- + + iterator begin() noexcept { return data(); } + iterator end() noexcept { return data() + size(); } + const_iterator begin() const noexcept { return data(); } + const_iterator end() const noexcept { return data() + size(); } + reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } + const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } + reverse_iterator rend() noexcept { return reverse_iterator(begin()); } + const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } + const_iterator cbegin() const noexcept { return begin(); } + const_iterator cend() const noexcept { return end(); } + const_reverse_iterator crbegin() const noexcept { return rbegin(); } + const_reverse_iterator crend() const noexcept { return rend(); } + + // -------------------------------------------------------------------------------------------- + + size_type size() const noexcept { return mSize; } + size_type capacity() const noexcept { return mCapacityAllocator.first(); } + bool empty() const noexcept { return size() == 0; } + size_type max_size() const noexcept { + return std::min(storage_traits::max_size(allocator()), + std::numeric_limits::max()); + } + + // -------------------------------------------------------------------------------------------- + + reference operator[](size_type n) noexcept { + assert(n < size()); + return *(begin() + n); + } + + const_reference operator[](size_type n) const noexcept { + assert(n < size()); + return *(begin() + n); + } + + reference front() noexcept { return *begin(); } + const_reference front() const noexcept { return *begin(); } + reference back() noexcept { return *(end() - 1); } + const_reference back() const noexcept { return *(end() - 1); } + value_type* data() noexcept { return mData; } + const value_type* data() const noexcept { return mData; } + + // -------------------------------------------------------------------------------------------- + + void push_back(const_reference v) { + auto pos = assertCapacityForSize(size() + 1); + ++mSize; + storage_traits::construct(allocator(), pos, v); + } + + void push_back(value_type&& v) { + auto pos = assertCapacityForSize(size() + 1); + ++mSize; + storage_traits::construct(allocator(), pos, std::move(v)); + } + + template + reference emplace_back(ARGS&& ... args) { + auto pos = assertCapacityForSize(size() + 1); + ++mSize; + storage_traits::construct(allocator(), pos, std::forward(args)...); + return *pos; + } + + void pop_back() { + assert(!empty()); + --mSize; + destroy(end(), end() + 1); + } + + iterator insert(const_iterator position, const_reference v) { + if (position == end()) { + push_back(v); + } else { + assertCapacityForSize(size() + 1); + pointer p = const_cast(position); + move_range(p, end(), p + 1); + ++mSize; + // here we handle inserting an element of this vector! + const_pointer pv = std::addressof(v); + if (p <= pv && pv < end()) { + *p = *(pv + 1); + } else { + *p = v; + } + } + return const_cast(position); + } + + iterator insert(const_iterator position, value_type&& v) { + if (position == end()) { + push_back(std::move(v)); + } else { + assertCapacityForSize(size() + 1); + pointer p = const_cast(position); + move_range(p, end(), p + 1); + ++mSize; + *p = std::move(v); + } + return const_cast(position); + } + + iterator erase(const_iterator pos) { + assert(pos != end()); + return erase(pos, pos + 1); + } + + iterator erase(const_iterator first, const_iterator last) { + assert(first <= last); + auto e = std::move(const_cast(last), end(), const_cast(first)); + destroy(e, end()); + mSize -= std::distance(first, last); + return const_cast(first); + } + + void clear() noexcept { + destroy(begin(), end()); + mSize = 0; + } + + void resize(size_type count) { + assertCapacityForSize(count); + if constexpr(std::is_trivially_constructible_v && + std::is_trivially_destructible_v) { + // we check for triviality here so that the implementation could be non-inline + mSize = count; + } else { + resize_non_trivial(count); + } + } + + void resize(size_type count, const_reference v) { + assertCapacityForSize(count); + resize_non_trivial(count, v); + } + + void swap(FixedCapacityVector& other) { + using std::swap; + swap(mData, other.mData); + swap(mSize, other.mSize); + mCapacityAllocator.swap(other.mCapacityAllocator); + } + + UTILS_NOINLINE + void reserve(size_type c) { + if (c > capacity()) { + FixedCapacityVector t(construct_with_capacity, c, allocator()); + t.mSize = size(); + std::uninitialized_move(begin(), end(), t.begin()); + this->swap(t); + } + } + +private: + enum construct_with_capacity_tag{ construct_with_capacity }; + + FixedCapacityVector(construct_with_capacity_tag, + size_type capacity, const allocator_type& allocator = allocator_type()) + : mCapacityAllocator(capacity, allocator) { + mData = this->allocator().allocate(this->capacity()); + } + + allocator_type& allocator() noexcept { + return mCapacityAllocator.second(); + } + + allocator_type const& allocator() const noexcept { + return mCapacityAllocator.second(); + } + + iterator assertCapacityForSize(size_type s) { + if constexpr(CapacityCheck || FILAMENT_FORCE_CAPACITY_CHECK) { + ASSERT_PRECONDITION(capacity() >= s, + "capacity exceeded: requested size %lu, available capacity %lu.", + (unsigned long)s, (unsigned long)capacity()); + } + return end(); + } + + inline void construct(iterator first, iterator last) noexcept { + // we check for triviality here so that the implementation could be non-inline + if constexpr(!std::is_trivially_constructible_v) { + construct_non_trivial(first, last); + } + } + + void construct(iterator first, iterator last, const_reference proto) noexcept { + #pragma nounroll + while (first != last) { + storage_traits::construct(allocator(), first++, proto); + } + } + + // should this be NOINLINE? + void construct_non_trivial(iterator first, iterator last) noexcept { + #pragma nounroll + while (first != last) { + storage_traits::construct(allocator(), first++); + } + } + + + inline void destroy(iterator first, iterator last) noexcept { + // we check for triviality here so that the implementation could be non-inline + if constexpr(!std::is_trivially_destructible_v) { + destroy_non_trivial(first, last); + } + } + + // should this be NOINLINE? + void destroy_non_trivial(iterator first, iterator last) noexcept { + #pragma nounroll + while (first != last) { + storage_traits::destroy(allocator(), --last); + } + } + + // should this be NOINLINE? + void resize_non_trivial(size_type count) { + if (count > size()) { + construct(end(), begin() + count); + } else if (count < size()) { + destroy(begin() + count, end()); + } + mSize = count; + } + + // should this be NOINLINE? + void resize_non_trivial(size_type count, const_reference v) { + if (count > size()) { + construct(end(), begin() + count, v); + } else if (count < size()) { + destroy(begin() + count, end()); + } + mSize = count; + } + + // should this be NOINLINE? + void move_range(pointer s, pointer e, pointer to) { + if constexpr(std::is_trivially_copy_assignable_v + && std::is_trivially_destructible_v) { + // this generates memmove -- which doesn't happen otherwise + std::move_backward(s, e, to + std::distance(s, e)); + } else { + pointer our_end = end(); + difference_type n = our_end - to; // nb of elements to move by operator= + pointer i = s + n; // 1st element to move by move ctor + for (pointer d = our_end ; i < our_end ; ++i, ++d) { + storage_traits::construct(allocator(), d, std::move(*i)); + } + std::move_backward(s, s + n, our_end); + } + } + + template + class SizeTypeWrapper { + TYPE value{}; + public: + SizeTypeWrapper() noexcept = default; + SizeTypeWrapper(SizeTypeWrapper const& rhs) noexcept = default; + explicit SizeTypeWrapper(TYPE value) noexcept : value(value) { } + SizeTypeWrapper operator=(TYPE rhs) noexcept { value = rhs; return *this; } + operator TYPE() const noexcept { return value; } + }; + + pointer mData{}; + size_type mSize{}; + compressed_pair, allocator_type> mCapacityAllocator{}; +}; + +} // namespace utils + +#endif //TNT_UTILS_FIXEDCAPACITYVECTOR_H diff --git a/ios/include/utils/Hash.h b/ios/include/utils/Hash.h new file mode 100644 index 00000000..955531b9 --- /dev/null +++ b/ios/include/utils/Hash.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_UTILS_HASH_H +#define TNT_UTILS_HASH_H + +#include // for std::hash + +#include +#include + +namespace utils { +namespace hash { + +inline uint32_t murmur3(const uint32_t* key, size_t wordCount, uint32_t seed) noexcept { + uint32_t h = seed; + size_t i = wordCount; + do { + uint32_t k = *key++; + k *= 0xcc9e2d51u; + k = (k << 15u) | (k >> 17u); + k *= 0x1b873593u; + h ^= k; + h = (h << 13u) | (h >> 19u); + h = (h * 5u) + 0xe6546b64u; + } while (--i); + h ^= wordCount; + h ^= h >> 16u; + h *= 0x85ebca6bu; + h ^= h >> 13u; + h *= 0xc2b2ae35u; + h ^= h >> 16u; + return h; +} + +template +struct MurmurHashFn { + uint32_t operator()(const T& key) const noexcept { + static_assert(0 == (sizeof(key) & 3u), "Hashing requires a size that is a multiple of 4."); + return murmur3((const uint32_t*) &key, sizeof(key) / 4, 0); + } +}; + +// combines two hashes together +template +inline void combine(size_t& seed, const T& v) noexcept { + std::hash hasher; + seed ^= hasher(v) + 0x9e3779b9u + (seed << 6u) + (seed >> 2u); +} + +// combines two hashes together, faster but less good +template +inline void combine_fast(size_t& seed, const T& v) noexcept { + std::hash hasher; + seed ^= hasher(v) << 1u; +} + +} // namespace hash +} // namespace utils + +#endif // TNT_UTILS_HASH_H diff --git a/ios/include/utils/JobSystem.h b/ios/include/utils/JobSystem.h new file mode 100644 index 00000000..5d738197 --- /dev/null +++ b/ios/include/utils/JobSystem.h @@ -0,0 +1,545 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_UTILS_JOBSYSTEM_H +#define TNT_UTILS_JOBSYSTEM_H + +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace utils { + +class JobSystem { + static constexpr size_t MAX_JOB_COUNT = 16384; + static_assert(MAX_JOB_COUNT <= 0x7FFE, "MAX_JOB_COUNT must be <= 0x7FFE"); + using WorkQueue = WorkStealingDequeue; + +public: + class Job; + + using JobFunc = void(*)(void*, JobSystem&, Job*); + + class alignas(CACHELINE_SIZE) Job { + public: + Job() noexcept {} /* = default; */ /* clang bug */ // NOLINT(modernize-use-equals-default,cppcoreguidelines-pro-type-member-init) + Job(const Job&) = delete; + Job(Job&&) = delete; + + private: + friend class JobSystem; + + // Size is chosen so that we can store at least std::function<> + // the alignas() qualifier ensures we're multiple of a cache-line. + static constexpr size_t JOB_STORAGE_SIZE_BYTES = + sizeof(std::function) > 48 ? sizeof(std::function) : 48; + static constexpr size_t JOB_STORAGE_SIZE_WORDS = + (JOB_STORAGE_SIZE_BYTES + sizeof(void*) - 1) / sizeof(void*); + + // keep it first, so it's correctly aligned with all architectures + // this is were we store the job's data, typically a std::function<> + // v7 | v8 + void* storage[JOB_STORAGE_SIZE_WORDS]; // 48 | 48 + JobFunc function; // 4 | 8 + uint16_t parent; // 2 | 2 + std::atomic runningJobCount = { 1 }; // 2 | 2 + mutable std::atomic refCount = { 1 }; // 2 | 2 + // 6 | 2 (padding) + // 64 | 64 + }; + + explicit JobSystem(size_t threadCount = 0, size_t adoptableThreadsCount = 1) noexcept; + + ~JobSystem(); + + // Make the current thread part of the thread pool. + void adopt(); + + // Remove this adopted thread from the parent. This is intended to be used for + // shutting down a JobSystem. In particular, this doesn't allow the parent to + // adopt more thread. + void emancipate(); + + + // If a parent is not specified when creating a job, that job will automatically take the + // root job as a parent. + // The root job is reset when waited on. + Job* setRootJob(Job* job) noexcept { return mRootJob = job; } + + // use setRootJob() instead + UTILS_DEPRECATED + Job* setMasterJob(Job* job) noexcept { return setRootJob(job); } + + + Job* create(Job* parent, JobFunc func) noexcept; + + // NOTE: All methods below must be called from the same thread and that thread must be + // owned by JobSystem's thread pool. + + /* + * Job creation examples: + * ---------------------- + * + * struct Functor { + * uintptr_t storage[6]; + * void operator()(JobSystem&, Jobsystem::Job*); + * } functor; + * + * struct Foo { + * uintptr_t storage[6]; + * void method(JobSystem&, Jobsystem::Job*); + * } foo; + * + * Functor and Foo size muse be <= uintptr_t[6] + * + * createJob() + * createJob(parent) + * createJob(parent, &foo) + * createJob(parent, foo) + * createJob(parent, std::ref(foo)) + * createJob(parent, functor) + * createJob(parent, std::ref(functor)) + * createJob(parent, [ up-to 6 uintptr_t ](JobSystem*, Jobsystem::Job*){ }) + * + * Utility functions: + * ------------------ + * These are less efficient, but handle any size objects using the heap if needed. + * (internally uses std::function<>), and don't require the callee to take + * a (JobSystem&, Jobsystem::Job*) as parameter. + * + * struct BigFoo { + * uintptr_t large[16]; + * void operator()(); + * void method(int answerToEverything); + * static void exec(BigFoo&) { } + * } bigFoo; + * + * jobs::createJob(js, parent, [ any-capture ](int answerToEverything){}, 42); + * jobs::createJob(js, parent, &BigFoo::method, &bigFoo, 42); + * jobs::createJob(js, parent, &BigFoo::exec, std::ref(bigFoo)); + * jobs::createJob(js, parent, bigFoo); + * jobs::createJob(js, parent, std::ref(bigFoo)); + * etc... + * + * struct SmallFunctor { + * uintptr_t storage[3]; + * void operator()(T* data, size_t count); + * } smallFunctor; + * + * jobs::parallel_for(js, data, count, [ up-to 3 uintptr_t ](T* data, size_t count) { }); + * jobs::parallel_for(js, data, count, smallFunctor); + * jobs::parallel_for(js, data, count, std::ref(smallFunctor)); + * + */ + + // creates an empty (no-op) job with an optional parent + Job* createJob(Job* parent = nullptr) noexcept { + return create(parent, nullptr); + } + + // creates a job from a KNOWN method pointer w/ object passed by pointer + // the caller must ensure the object will outlive the Job + template + Job* createJob(Job* parent, T* data) noexcept { + Job* job = create(parent, [](void* user, JobSystem& js, Job* job) { + (*static_cast(user)->*method)(js, job); + }); + if (job) { + job->storage[0] = data; + } + return job; + } + + // creates a job from a KNOWN method pointer w/ object passed by value + template + Job* createJob(Job* parent, T data) noexcept { + static_assert(sizeof(data) <= sizeof(Job::storage), "user data too large"); + Job* job = create(parent, [](void* user, JobSystem& js, Job* job) { + T* that = static_cast(user); + (that->*method)(js, job); + that->~T(); + }); + if (job) { + new(job->storage) T(std::move(data)); + } + return job; + } + + // creates a job from a functor passed by value + template + Job* createJob(Job* parent, T functor) noexcept { + static_assert(sizeof(functor) <= sizeof(Job::storage), "functor too large"); + Job* job = create(parent, [](void* user, JobSystem& js, Job* job){ + T& that = *static_cast(user); + that(js, job); + that.~T(); + }); + if (job) { + new(job->storage) T(std::move(functor)); + } + return job; + } + + + /* + * Jobs are normally finished automatically, this can be used to cancel a job before it is run. + * + * Never use this once a flavor of run() has been called. + */ + void cancel(Job*& job) noexcept; + + /* + * Adds a reference to a Job. + * + * This allows the caller to waitAndRelease() on this job from multiple threads. + * Use runAndWait() if waiting from multiple threads is not needed. + * + * This job MUST BE waited on with waitAndRelease(), or released with release(). + */ + Job* retain(Job* job) noexcept; + + /* + * Releases a reference from a Job obtained with runAndRetain() or a call to retain(). + * + * The job can't be used after this call. + */ + void release(Job*& job) noexcept; + void release(Job*&& job) noexcept { + Job* p = job; + release(p); + } + + /* + * Add job to this thread's execution queue. It's reference will drop automatically. + * Current thread must be owned by JobSystem's thread pool. See adopt(). + * + * The job can't be used after this call. + */ + void run(Job*& job) noexcept; + void run(Job*&& job) noexcept { // allows run(createJob(...)); + Job* p = job; + run(p); + } + + void signal() noexcept; + + /* + * Add job to this thread's execution queue and and keep a reference to it. + * Current thread must be owned by JobSystem's thread pool. See adopt(). + * + * This job MUST BE waited on with wait(), or released with release(). + */ + Job* runAndRetain(Job* job) noexcept; + + /* + * Wait on a job and destroys it. + * Current thread must be owned by JobSystem's thread pool. See adopt(). + * + * The job must first be obtained from runAndRetain() or retain(). + * The job can't be used after this call. + */ + void waitAndRelease(Job*& job) noexcept; + + /* + * Runs and wait for a job. This is equivalent to calling + * runAndRetain(job); + * wait(job); + * + * The job can't be used after this call. + */ + void runAndWait(Job*& job) noexcept; + void runAndWait(Job*&& job) noexcept { // allows runAndWait(createJob(...)); + Job* p = job; + runAndWait(p); + } + + // for debugging + friend utils::io::ostream& operator << (utils::io::ostream& out, JobSystem const& js); + + + // utility functions... + + // set the name of the current thread (on OSes that support it) + static void setThreadName(const char* threadName) noexcept; + + enum class Priority { + NORMAL, + DISPLAY, + URGENT_DISPLAY + }; + + static void setThreadPriority(Priority priority) noexcept; + static void setThreadAffinityById(size_t id) noexcept; + + size_t getParallelSplitCount() const noexcept { + return mParallelSplitCount; + } + +private: + // this is just to avoid using std::default_random_engine, since we're in a public header. + class default_random_engine { + static constexpr uint32_t m = 0x7fffffffu; + uint32_t mState; // must be 0 < seed < 0x7fffffff + public: + inline constexpr explicit default_random_engine(uint32_t seed = 1u) noexcept + : mState(((seed % m) == 0u) ? 1u : seed % m) { + } + inline uint32_t operator()() noexcept { + return mState = uint32_t((uint64_t(mState) * 48271u) % m); + } + }; + + struct alignas(CACHELINE_SIZE) ThreadState { // this causes 40-bytes padding + // make sure storage is cache-line aligned + WorkQueue workQueue; + + // these are not accessed by the worker threads + alignas(CACHELINE_SIZE) // this causes 56-bytes padding + JobSystem* js; + std::thread thread; + default_random_engine rndGen; + uint32_t id; + }; + + static_assert(sizeof(ThreadState) % CACHELINE_SIZE == 0, + "ThreadState doesn't align to a cache line"); + + ThreadState& getState() noexcept; + + void incRef(Job const* job) noexcept; + void decRef(Job const* job) noexcept; + + Job* allocateJob() noexcept; + JobSystem::ThreadState* getStateToStealFrom(JobSystem::ThreadState& state) noexcept; + bool hasJobCompleted(Job const* job) noexcept; + + void requestExit() noexcept; + bool exitRequested() const noexcept; + bool hasActiveJobs() const noexcept; + + void loop(ThreadState* state) noexcept; + bool execute(JobSystem::ThreadState& state) noexcept; + Job* steal(JobSystem::ThreadState& state) noexcept; + void finish(Job* job) noexcept; + + void put(WorkQueue& workQueue, Job* job) noexcept { + assert(job); + size_t index = job - mJobStorageBase; + assert(index >= 0 && index < MAX_JOB_COUNT); + workQueue.push(uint16_t(index + 1)); + } + + Job* pop(WorkQueue& workQueue) noexcept { + size_t index = workQueue.pop(); + assert(index <= MAX_JOB_COUNT); + return !index ? nullptr : &mJobStorageBase[index - 1]; + } + + Job* steal(WorkQueue& workQueue) noexcept { + size_t index = workQueue.steal(); + assert(index <= MAX_JOB_COUNT); + return !index ? nullptr : &mJobStorageBase[index - 1]; + } + + void wait(std::unique_lock& lock, Job* job = nullptr) noexcept; + void wakeAll() noexcept; + void wakeOne() noexcept; + + // these have thread contention, keep them together + utils::Mutex mWaiterLock; + utils::Condition mWaiterCondition; + + std::atomic mActiveJobs = { 0 }; + utils::Arena, LockingPolicy::NoLock> mJobPool; + + template + using aligned_vector = std::vector>; + + // these are essentially const, make sure they're on a different cache-lines than the + // read-write atomics. + // We can't use "alignas(CACHELINE_SIZE)" because the standard allocator can't make this + // guarantee. + char padding[CACHELINE_SIZE]; + + alignas(16) // at least we align to half (or quarter) cache-line + aligned_vector mThreadStates; // actual data is stored offline + std::atomic mExitRequested = { false }; // this one is almost never written + std::atomic mAdoptedThreads = { 0 }; // this one is almost never written + Job* const mJobStorageBase; // Base for conversion to indices + uint16_t mThreadCount = 0; // total # of threads in the pool + uint8_t mParallelSplitCount = 0; // # of split allowable in parallel_for + Job* mRootJob = nullptr; + + utils::SpinLock mThreadMapLock; // this should have very little contention + tsl::robin_map mThreadMap; +}; + +// ------------------------------------------------------------------------------------------------- +// Utility functions built on top of JobSystem + +namespace jobs { + +// These are convenience C++11 style job creation methods that support lambdas +// +// IMPORTANT: these are less efficient to call and may perform heap allocation +// depending on the capture and parameters +// +template +JobSystem::Job* createJob(JobSystem& js, JobSystem::Job* parent, + CALLABLE&& func, ARGS&&... args) noexcept { + struct Data { + std::function f; + // Renaming the method below could cause an Arrested Development. + void gob(JobSystem&, JobSystem::Job*) noexcept { f(); } + } user{ std::bind(std::forward(func), + std::forward(args)...) }; + return js.createJob(parent, std::move(user)); +} + +template::type>::value + >::type +> +JobSystem::Job* createJob(JobSystem& js, JobSystem::Job* parent, + CALLABLE&& func, T&& o, ARGS&&... args) noexcept { + struct Data { + std::function f; + // Renaming the method below could cause an Arrested Development. + void gob(JobSystem&, JobSystem::Job*) noexcept { f(); } + } user{ std::bind(std::forward(func), std::forward(o), + std::forward(args)...) }; + return js.createJob(parent, std::move(user)); +} + + +namespace details { + +template +struct ParallelForJobData { + using SplitterType = S; + using Functor = F; + using JobData = ParallelForJobData; + using size_type = uint32_t; + + ParallelForJobData(size_type start, size_type count, uint8_t splits, + Functor functor, + const SplitterType& splitter) noexcept + : start(start), count(count), + functor(std::move(functor)), + splits(splits), + splitter(splitter) { + } + + void parallelWithJobs(JobSystem& js, JobSystem::Job* parent) noexcept { + assert(parent); + + // this branch is often miss-predicted (it both sides happen 50% of the calls) +right_side: + if (splitter.split(splits, count)) { + const size_type lc = count / 2; + JobData ld(start, lc, splits + uint8_t(1), functor, splitter); + JobSystem::Job* l = js.createJob(parent, std::move(ld)); + if (UTILS_UNLIKELY(l == nullptr)) { + // couldn't create a job, just pretend we're done splitting + goto execute; + } + + // start the left side before attempting the right side, so we parallelize in case + // of job creation failure -- rare, but still. + js.run(l); + + // don't spawn a job for the right side, just reuse us -- spawning jobs is more + // costly than we'd like. + start += lc; + count -= lc; + ++splits; + goto right_side; + + } else { +execute: + // we're done splitting, do the real work here! + functor(start, count); + } + } + +private: + size_type start; // 4 + size_type count; // 4 + Functor functor; // ? + uint8_t splits; // 1 + SplitterType splitter; // 1 +}; + +} // namespace details + + +// parallel jobs with start/count indices +template +JobSystem::Job* parallel_for(JobSystem& js, JobSystem::Job* parent, + uint32_t start, uint32_t count, F functor, const S& splitter) noexcept { + using JobData = details::ParallelForJobData; + JobData jobData(start, count, 0, std::move(functor), splitter); + return js.createJob(parent, std::move(jobData)); +} + +// parallel jobs with pointer/count +template +JobSystem::Job* parallel_for(JobSystem& js, JobSystem::Job* parent, + T* data, uint32_t count, F functor, const S& splitter) noexcept { + auto user = [data, f = std::move(functor)](uint32_t s, uint32_t c) { + f(data + s, c); + }; + using JobData = details::ParallelForJobData; + JobData jobData(0, count, 0, std::move(user), splitter); + return js.createJob(parent, std::move(jobData)); +} + +// parallel jobs on a Slice<> +template +JobSystem::Job* parallel_for(JobSystem& js, JobSystem::Job* parent, + utils::Slice slice, F functor, const S& splitter) noexcept { + return parallel_for(js, parent, slice.data(), slice.size(), functor, splitter); +} + + +template +class CountSplitter { +public: + bool split(size_t splits, size_t count) const noexcept { + return (splits < MAX_SPLITS && count >= COUNT * 2); + } +}; + +} // namespace jobs +} // namespace utils + +#endif // TNT_UTILS_JOBSYSTEM_H diff --git a/ios/include/utils/Log.h b/ios/include/utils/Log.h new file mode 100644 index 00000000..71de1b09 --- /dev/null +++ b/ios/include/utils/Log.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_UTILS_LOG_H +#define TNT_UTILS_LOG_H + +#include +#include + +namespace utils { + +struct UTILS_PUBLIC Loggers { + // DEBUG level logging stream + io::ostream& d; + + // ERROR level logging stream + io::ostream& e; + + // WARNING level logging stream + io::ostream& w; + + // INFORMATION level logging stream + io::ostream& i; +}; + +extern UTILS_PUBLIC Loggers const slog; + +} // namespace utils + +#endif // TNT_UTILS_LOG_H diff --git a/ios/include/utils/Mutex.h b/ios/include/utils/Mutex.h new file mode 100644 index 00000000..b0a74cb8 --- /dev/null +++ b/ios/include/utils/Mutex.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef UTILS_MUTEX_H +#define UTILS_MUTEX_H + +#if defined(__linux__) && !defined(__SANITIZE_THREAD__) +#include +#else +#include +#endif + +#endif // UTILS_MUTEX_H diff --git a/ios/include/utils/NameComponentManager.h b/ios/include/utils/NameComponentManager.h new file mode 100644 index 00000000..c12366a9 --- /dev/null +++ b/ios/include/utils/NameComponentManager.h @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_UTILS_NAMECOMPONENTMANAGER_H +#define TNT_UTILS_NAMECOMPONENTMANAGER_H + +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace utils { + +class EntityManager; + +namespace details { +class SafeString { +public: + SafeString() noexcept = default; + explicit SafeString(const char* str) noexcept : mCStr(strdup(str)) { } + SafeString(SafeString&& rhs) noexcept : mCStr(rhs.mCStr) { rhs.mCStr = nullptr; } + SafeString& operator=(SafeString&& rhs) noexcept { + std::swap(mCStr, rhs.mCStr); + return *this; + } + ~SafeString() { free((void*)mCStr); } + const char* c_str() const noexcept { return mCStr; } + +private: + char const* mCStr = nullptr; +}; +} // namespace details + + +/** + * \class NameComponentManager NameComponentManager.h utils/NameComponentManager.h + * \brief Allows clients to associate string labels with entities. + * + * To access the name of an existing entity, clients should first use NameComponentManager to get a + * temporary handle called an \em instance. Please note that instances are ephemeral; clients should + * store entities, not instances. + * + * Usage example: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * auto names = new NameComponentManager(EntityManager::get()); + * names->addComponent(myEntity); + * names->setName(names->getInstance(myEntity), "Jeanne d'Arc"); + * ... + * printf("%s\n", names->getName(names->getInstance(myEntity)); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +class UTILS_PUBLIC NameComponentManager : public SingleInstanceComponentManager { +public: + using Instance = EntityInstance; + + /** + * Creates a new name manager associated with the given entity manager. + * + * Note that multiple string labels could be associated with each entity simply by + * creating multiple instances of NameComponentManager. + */ + explicit NameComponentManager(EntityManager& em); + ~NameComponentManager(); + + /** + * Checks if the given entity already has a name component. + */ + using SingleInstanceComponentManager::hasComponent; + + /** + * Gets a temporary handle that can be used to access the name. + * + * @return Non-zero handle if the entity has a name component, 0 otherwise. + */ + Instance getInstance(Entity e) const noexcept { + return Instance(SingleInstanceComponentManager::getInstance(e)); + } + + /*! \cond PRIVATE */ + // these are implemented in SingleInstanceComponentManager<>, but we need to + // reimplement them in each manager, to ensure they are generated in an implementation file + // for backward binary compatibility reasons. + size_t getComponentCount() const noexcept; + Entity const* getEntities() const noexcept; + void gc(const EntityManager& em, size_t ratio = 4) noexcept; + /*! \endcond */ + + /** + * Adds a name component to the given entity if it doesn't already exist. + */ + void addComponent(Entity e); + + /** + * Removes the name component to the given entity if it exists. + */ + void removeComponent(Entity e); + + /** + * Stores a copy of the given string and associates it with the given instance. + */ + void setName(Instance instance, const char* name) noexcept; + + /** + * Retrieves the string associated with the given instance, or nullptr if none exists. + * + * @return pointer to the copy that was made during setName() + */ + const char* getName(Instance instance) const noexcept; +}; + +} // namespace utils + +#endif // TNT_UTILS_NAMECOMPONENTMANAGER_H diff --git a/ios/include/utils/Panic.h b/ios/include/utils/Panic.h new file mode 100644 index 00000000..31b6bea9 --- /dev/null +++ b/ios/include/utils/Panic.h @@ -0,0 +1,561 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef UTILS_PANIC_H_ +#define UTILS_PANIC_H_ + +#include + +#include +#include + +#ifdef __EXCEPTIONS +# define UTILS_EXCEPTIONS 1 +#else +#endif + +/** + * @defgroup errors Handling Catastrophic Failures (Panics) + * + * @brief Failure detection and reporting facilities + * + * ## What's a Panic? ## + * + * In the context of this document, a _panic_ is a type of error due to a _contract violation_, + * it shouldn't be confused with a _result_ or _status_ code. The POSIX API for instance, + * unfortunately often conflates the two. + * @see + * + * + * Here we give the following definition of a _panic_: + * + * 1. Failures to meet a function's own **postconditions**\n + * The function cannot establish one of its own postconditions, such as (but not limited to) + * producing a valid return value object. + * + * Often these failures are only detectable at runtime, for instance they can be caused by + * arithmetic errors, as it was the case for the Ariane 5 rocket. Ariane 5 crashed because it + * reused an inertial module from Ariane 4, which didn't account for the greater horizontal + * acceleration of Ariane 5 and caused an overflow in the computations. Ariane 4's module + * wasn't per-say buggy, but was improperly used and failed to meet, obviously, certain + * postconditions. + * @see + * + * 2. Failures to meet the **preconditions** of any of a function's callees\n + * The function cannot meet a precondition of another function it must call, such as a + * restriction on a parameter. + * + * Not to be confused with the case where the preconditions of a function are already + * violated upon entry, which indicates a programming error from the caller. + * + * Typically these failures can be avoided and arise because of programming errors. + * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * ## Failure reporting vs. handling ## + * + * Very often when a panic, as defined above, is detected, the program has little other choice + * but to terminate.\n + * Typically these situations can be handled by _assert()_. However, _assert()_ also conflates two + * very different concepts: detecting and handling failures.\n + * The place where a failure is detected is rarely the place where there is enough + * context to decide what to do. _assert()_ terminates the program which, may or may not be + * appropriate. At the very least the failure must be logged (which _assert()_ does in a crude way), + * but some other actions might need to happen, such as:\n + * + * - logging the failure in the system-wide logger + * - providing enough information in development builds to analyze/debug the problem + * - cleanly releasing some resources, such as communication channels with other processes\n + * e.g.: to avoid their pre- or postconditions from being violated as well. + * + * In some _rare_ cases, the failure might even be ignored altogether because it doesn't matter in + * the context where it happened. This decision clearly doesn't always lie at the failure-site. + * + * It is therefore important to separate failure detection from handling. + * + * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * ## Failure detection and handling facilities ## + * + * Clearly, catastrophic failures should be **rare**; in fact they should + * never happen, except possibly for "failures to meet a function's own postconditions", which + * may depend on external factors and should still be very rare. Yet, when a failure happens, it + * must be detected and handled appropriately.\n + * Since panics are rare, it is desirable that the handling mechanism be as unobtrusive + * as possible, without allowing such failures to go unnoticed or swallowed by mistake. Ideally, the + * programmer using an API should have nothing special to do to handle that API's failure + * conditions.\n\n + * + * An important feature of the Panic Handling facilities here is that **panics are not part of + * the API of a function or method**\n\n + * + * + * The panic handling facility has the following benefits: + * - provides an easy way to detect and report failure + * - separates failure detection from handling + * - makes it hard for detected failures to be ignored (i.e.: not handled) + * - doesn't add burden on the API design + * - doesn't add overhead (visual or otherwise) at call sites + * - has very little performance overhead for failure detection + * - has little to no performance impact for failure handling in the common (success) case + * - is flexible and extensible + * + * Since we have established that failures are **rare**, **exceptional** situations, it would be + * appropriate to handle them with an _assert_ mechanism and that's what the API below + * provides. However, under-the-hood it uses C++ exceptions as a means to separate + * _reporting_ from _handling_. + * + * \note On devices where exceptions are not supported or appropriate, these APIs can be turned + * into a regular _std::terminate()_. + * + * + * ASSERT_PRECONDITION(condition, format, ...) + * ASSERT_POSTCONDITION(condition, format, ...) + * ASSERT_ARITHMETIC(condition, format, ...) + * ASSERT_DESTRUCTOR(condition, format, ...) + * + * + * @see ASSERT_PRECONDITION, ASSERT_POSTCONDITION, ASSERT_ARITHMETIC + * @see ASSERT_DESTRUCTOR + * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * ## Writing code that can assert ## + * + * Because we've separated failure reporting from failure handling, there are some considerations + * that need to be thought about when writing code that calls the macros above (i.e.: the program + * won't terminate at the point where the failure is detected).\n\n + * + * + * ### Panic guarantees ### + * + * After the failure condition is reported by a function, additional guarantees may be provided + * with regards to the state of the program. The following four levels of guarantee are + * generally recognized, each of which is a strict superset of its successors: + * + * 1. Nothrow exception guarantee\n + * The function never asserts. e.g.: This should always be the case with destructors.\n\n + * + * 2. Strong exception guarantee\n + * If the function asserts, the state of the program is rolled back to the state just before + * the function call.\n\n + * + * 3. Basic exception guarantee\n + * If the function asserts, the program is in a valid state. It may require cleanup, + * but all invariants are intact.\n\n + * + * 4. No exception guarantee\n + * If the function asserts, the program may not be in a valid state: resource leaks, memory + * corruption, or other invariant-destroying failures may have occurred. + * + * In each function, give the **strongest** safety guarantee that won't penalize callers who + * don't need it, but **always give at least the basic guarantee**. The RAII (Resource + * Acquisition Is Initialization) pattern can help with achieving these guarantees. + * + * @see [RAII](http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization) + * + * ### Special considerations for Constructors ### + * + * Constructors are a bit special because if a failure occurs during their execution, the + * destructor won't be called (how could it? since the object wasn't constructed!). This can lead + * to leaked resources allocated in the constructor prior to the failure. Thankfully there is + * a nice C++ syntax to handle this case: + * + * @code + * Foo::Foo(size_t s) try : m_size(s), m_buf(new uint32_t[s]) { + * ASSERT_POSTCONDITION(s&0xF==0, + * "object size is %u, but must be multiple of 16", s); + * } catch (...) { + * delete [] m_buf; + * // the exception will be automatically re-thrown + * } + * @endcode + * + * Unfortunately, this usage leaks the underlying, exception-based, implementation of the + * panic handling macros. For this reason, it is best to keep constructors simple and guarantee + * they can't fail. An _init()_ function with a factory can be used for actual initialization. + * + * + * ### Special considerations for Destructors ### + * + * In C++ destructors cannot throw exceptions and since the above macros internally use exceptions + * they cannot be used in destructors. Doing so will result in immediate termination of the + * program by _std::terminate()_.\n + * It is therefore best to always guarantee that destructors won't fail. In case of such a + * failure in a destructor the ASSERT_DESTRUCTOR() macro can be used instead, it + * will log the failure but won't terminate the program, instead it'll proceed as if nothing + * happened. Generally this will result in some resource leak which, eventually, will cause + * another failure (typically a postcondition violation).\n\n + * + * Rationale for this behavior: There are fundamentally no way to report a failure from a + * destructor in C++, violently terminating the process is inadequate because it again conflates + * failure reporting and failure handling; for instance a failure in glDeleteTextures() shouldn't + * be necessarily fatal (certainly not without saving the user's data first). The alternative + * would be for the caller to swallow the failure entirely, but that's not great either because the + * failure would go unnoticed. The solution retained here is a compromise. + * + * @see ASSERT_DESTRUCTOR + * + * ### Testing Code that Uses Panics ### + * + * Since panics use exceptions for their underlying implementation, you can test code that uses + * panics with EXPECT_THROW by doing the following things: + * \li Set panic mode to THROW (default is TERMINATE) + * \li Pass Panic to EXPECT_THROW as the exception type + * + * Example code for your test file: + * + * @code + * #include // since your code uses panics, this should include utils/Panic.hpp + * + * using utils::Panic; + * + * TEST(MyClassTest, value_that_causes_panic) { + * EXPECT_THROW(MyClass::function(value_that_causes_panic), Panic); + * } + * + * // ... other tests ... + * + * int main(int argc, char** argv) { + * ::testing::InitGoogleTest(&argc, argv); + * Panic::setMode(Panic::Mode::THROW); + * return RUN_ALL_TESTS(); + * } + * @endcode + * + */ + +namespace utils { + +// ----------------------------------------------------------------------------------------------- + +/** + * @ingroup errors + * + * \brief Base class of all exceptions thrown by all the ASSERT macros + * + * The Panic class provides the std::exception protocol, it is the base exception object + * used for all thrown exceptions. + */ +class UTILS_PUBLIC Panic { +public: + virtual ~Panic() noexcept; + + /** + * @return a detailed description of the error + * @see std::exception + */ + virtual const char* what() const noexcept = 0; + + /** + * Get the function name where the panic was detected + * @return a C string containing the function name where the panic was detected + */ + virtual const char* getFunction() const noexcept = 0; + + /** + * Get the file name where the panic was detected + * @return a C string containing the file name where the panic was detected + */ + virtual const char* getFile() const noexcept = 0; + + /** + * Get the line number in the file where the panic was detected + * @return an integer containing the line number in the file where the panic was detected + */ + virtual int getLine() const noexcept = 0; + + /** + * Logs this exception to the system-log + */ + virtual void log() const noexcept = 0; + + /** + * Get the CallStack when the panic was detected + * @return the CallStack when the panic was detected + */ + virtual const CallStack& getCallStack() const noexcept = 0; +}; + +// ----------------------------------------------------------------------------------------------- + +/** + * @ingroup errors + * + * \brief Concrete implementation of the Panic interface. + * + * The TPanic<> class implements the std::exception protocol as well as the Panic + * interface common to all exceptions thrown by the framework. + */ +template +class UTILS_PUBLIC TPanic : public Panic { +public: + // std::exception protocol + const char* what() const noexcept override; + + // Panic interface + const char* getFunction() const noexcept override; + const char* getFile() const noexcept override; + int getLine() const noexcept override; + const CallStack& getCallStack() const noexcept override; + void log() const noexcept override; + + /** + * Depending on the mode set, either throws an exception of type T with the given reason plus + * extra information about the error-site, or logs the error and calls std::terminate(). + * This function never returns. + * @param function the name of the function where the error was detected + * @param file the file where the above function in implemented + * @param line the line in the above file where the error was detected + * @param format printf style string describing the error + * @see ASSERT_PRECONDITION, ASSERT_POSTCONDITION, ASSERT_ARITHMETIC + * @see PANIC_PRECONDITION, PANIC_POSTCONDITION, PANIC_ARITHMETIC + * @see setMode() + */ + static void panic(char const* function, char const* file, int line, const char* format, ...) + UTILS_NORETURN; + + /** + * Depending on the mode set, either throws an exception of type T with the given reason plus + * extra information about the error-site, or logs the error and calls std::terminate(). + * This function never returns. + * @param function the name of the function where the error was detected + * @param file the file where the above function in implemented + * @param line the line in the above file where the error was detected + * @param s std::string describing the error + * @see ASSERT_PRECONDITION, ASSERT_POSTCONDITION, ASSERT_ARITHMETIC + * @see PANIC_PRECONDITION, PANIC_POSTCONDITION, PANIC_ARITHMETIC + * @see setMode() + */ + static inline void panic(char const* function, char const* file, int line, const std::string& s) + UTILS_NORETURN { + panic(function, file, line, s.c_str()); + } + +protected: + /** + * Creates a Panic. + * @param reason a description of the cause of the error + */ + explicit TPanic(std::string reason); + + /** + * Creates a Panic with extra information about the error-site. + * @param function the name of the function where the error was detected + * @param file the file where the above function in implemented + * @param line the line in the above file where the error was detected + * @param reason a description of the cause of the error + */ + TPanic(char const* function, char const* file, int line, std::string reason); + + ~TPanic() override; + +private: + void buildMessage(); + + CallStack m_callstack; + std::string m_reason; + char const* const m_function = nullptr; + char const* const m_file = nullptr; + const int m_line = -1; + mutable std::string m_msg; +}; + +namespace details { +// these are private, don't use +void panicLog( + char const* function, char const* file, int line, const char* format, ...) noexcept; +} // namespace details + +// ----------------------------------------------------------------------------------------------- + +/** + * @ingroup errors + * + * ASSERT_PRECONDITION uses this Panic to report a precondition failure. + * @see ASSERT_PRECONDITION + */ +class UTILS_PUBLIC PreconditionPanic : public TPanic { + // Programming error, can be avoided + // e.g.: invalid arguments + using TPanic::TPanic; + friend class TPanic; +}; + +/** + * @ingroup errors + * + * ASSERT_POSTCONDITION uses this Panic to report a postcondition failure. + * @see ASSERT_POSTCONDITION + */ +class UTILS_PUBLIC PostconditionPanic : public TPanic { + // Usually only detectable at runtime + // e.g.: dead-lock would occur, arithmetic errors + using TPanic::TPanic; + friend class TPanic; +}; + +/** + * @ingroup errors + * + * ASSERT_ARITHMETIC uses this Panic to report an arithmetic (postcondition) failure. + * @see ASSERT_ARITHMETIC + */ +class UTILS_PUBLIC ArithmeticPanic : public TPanic { + // A common case of post-condition error + // e.g.: underflow, overflow, internal computations errors + using TPanic::TPanic; + friend class TPanic; +}; + +// ----------------------------------------------------------------------------------------------- +} // namespace utils + +#ifndef NDEBUG +# define PANIC_FILE(F) (F) +#else +# define PANIC_FILE(F) "" +#endif + +/** + * PANIC_PRECONDITION is a macro that reports a PreconditionPanic + * @param format printf-style string describing the error in more details + */ +#define PANIC_PRECONDITION(format, ...) \ + ::utils::PreconditionPanic::panic(__PRETTY_FUNCTION__, \ + PANIC_FILE(__FILE__), __LINE__, format, ##__VA_ARGS__) + +/** + * PANIC_POSTCONDITION is a macro that reports a PostconditionPanic + * @param format printf-style string describing the error in more details + */ +#define PANIC_POSTCONDITION(format, ...) \ + ::utils::PostconditionPanic::panic(__PRETTY_FUNCTION__, \ + PANIC_FILE(__FILE__), __LINE__, format, ##__VA_ARGS__) + +/** + * PANIC_ARITHMETIC is a macro that reports a ArithmeticPanic + * @param format printf-style string describing the error in more details + */ +#define PANIC_ARITHMETIC(format, ...) \ + ::utils::ArithmeticPanic::panic(__PRETTY_FUNCTION__, \ + PANIC_FILE(__FILE__), __LINE__, format, ##__VA_ARGS__) + +/** + * PANIC_LOG is a macro that logs a Panic, and continues as usual. + * @param format printf-style string describing the error in more details + */ +#define PANIC_LOG(format, ...) \ + ::utils::details::panicLog(__PRETTY_FUNCTION__, \ + PANIC_FILE(__FILE__), __LINE__, format, ##__VA_ARGS__) + +/** + * @ingroup errors + * + * ASSERT_PRECONDITION is a macro that checks the given condition and reports a PreconditionPanic + * if it evaluates to false. + * @param cond a boolean expression + * @param format printf-style string describing the error in more details + */ +#define ASSERT_PRECONDITION(cond, format, ...) \ + (!UTILS_LIKELY(cond) ? PANIC_PRECONDITION(format, ##__VA_ARGS__) : (void)0) + +#if defined(UTILS_EXCEPTIONS) || !defined(NDEBUG) +#define ASSERT_PRECONDITION_NON_FATAL(cond, format, ...) \ + (!UTILS_LIKELY(cond) ? PANIC_PRECONDITION(format, ##__VA_ARGS__), false : true) +#else +#define ASSERT_PRECONDITION_NON_FATAL(cond, format, ...) \ + (!UTILS_LIKELY(cond) ? PANIC_LOG(format, ##__VA_ARGS__), false : true) +#endif + + +/** + * @ingroup errors + * + * ASSERT_POSTCONDITION is a macro that checks the given condition and reports a PostconditionPanic + * if it evaluates to false. + * @param cond a boolean expression + * @param format printf-style string describing the error in more details + * + * Example: + * @code + * int& Foo::operator[](size_t index) { + * ASSERT_POSTCONDITION(index=0 && v<65536, "overflow occurred"); + * return uint32_t(v); + * } + * @endcode + */ +#define ASSERT_ARITHMETIC(cond, format, ...) \ + (!(cond) ? PANIC_ARITHMETIC(format, ##__VA_ARGS__) : (void)0) + +#if defined(UTILS_EXCEPTIONS) || !defined(NDEBUG) +#define ASSERT_ARITHMETIC_NON_FATAL(cond, format, ...) \ + (!UTILS_LIKELY(cond) ? PANIC_ARITHMETIC(format, ##__VA_ARGS__), false : true) +#else +#define ASSERT_ARITHMETIC_NON_FATAL(cond, format, ...) \ + (!UTILS_LIKELY(cond) ? PANIC_LOG(format, ##__VA_ARGS__), false : true) +#endif + +/** + * @ingroup errors + * + * ASSERT_DESTRUCTOR is a macro that checks the given condition and logs an error + * if it evaluates to false. + * @param cond a boolean expression + * @param format printf-style string describing the error in more details + * + * @warning Use this macro if a destructor can fail, which should be avoided at all costs. + * Unlike the other ASSERT macros, this will never result in the process termination. Instead, + * the error will be logged and the program will continue as if nothing happened. + * + * Example: + * @code + * Foo::~Foo() { + * glDeleteTextures(1, &m_texture); + * GLint err = glGetError(); + * ASSERT_DESTRUCTOR(err == GL_NO_ERROR, "cannot free GL resource!"); + * } + * @endcode + */ +#define ASSERT_DESTRUCTOR(cond, format, ...) (!(cond) ? PANIC_LOG(format, ##__VA_ARGS__) : (void)0) + +#endif // UTILS_PANIC_H_ diff --git a/ios/include/utils/Path.h b/ios/include/utils/Path.h new file mode 100644 index 00000000..59942d44 --- /dev/null +++ b/ios/include/utils/Path.h @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef UTILS_PATH_H_ +#define UTILS_PATH_H_ + +#include + +#include +#include +#include + +namespace utils { + +/** + * An abstract representation of file and directory paths. + */ +class UTILS_PUBLIC Path { +public: + /** + * Creates a new empty path. + */ + Path() = default; + ~Path() = default; + + /** + * Creates a new path with the specified pathname. + * + * @param pathname a non-null pathname string + */ + Path(const char* pathname); + + /** + * Creates a new path with the specified pathname. + * + * @param pathname a pathname string + */ + Path(const std::string& pathname); + + /** + * Tests whether the file or directory denoted by this abstract + * pathname exists. + * + * @return true if the file or directory denoted by this + * abstract pathname exists, false otherwise + */ + bool exists() const; + + /** + * Tests whether this abstract pathname represents a regular file. + * This method can only return true if the path exists. + * + * @return true if this pathname represents an existing file, + * false if the path doesn't exist or represents something + * else (directory, symlink, etc.) + */ + bool isFile() const; + + /** + * Tests whether this abstract pathname represents a directory. + * This method can only return true if the path exists. + * + * @return true if this pathname represents an existing directory, + * false if the path doesn't exist or represents a file + */ + bool isDirectory() const; + + /** + * Tests whether this path is empty. An empty path does not + * exist. + * + * @return true if the underlying abstract pathname is empty, + * false otherwise + */ + bool isEmpty() const { return m_path.empty(); } + + const char* c_str() const { return m_path.c_str(); } + + /** + * Replaces the abstract pathname of this object with the + * specified pathname. + * + * @param pathname a pathname string + */ + void setPath(const std::string& pathname) { + m_path = getCanonicalPath(pathname); + } + + /** + * @return the canonical pathname this path represents + */ + const std::string& getPath() const { return m_path; } + + /** + * Returns the parent of this path as Path. + * @return a new path containing the parent of this path + */ + Path getParent() const; + + /** + * Returns ancestor path where "0" is the immediate parent. + * @return a new path containing the ancestor of this path + */ + Path getAncestor(int n) const; + + /** + * Returns the name of the file or directory represented by + * this abstract pathname. + * + * @return the name of the file or directory represented by + * this abstract pathname, or an empty string if + * this path is empty + */ + std::string getName() const; + + /** + * Returns the name of the file or directory represented by + * this abstract pathname without its extension. + * + * @return the name of the file or directory represented by + * this abstract pathname, or an empty string if + * this path is empty + */ + std::string getNameWithoutExtension() const; + + /** + * Returns the file extension (after the ".") if one is present. + * Returns the empty string if no filename is present or if the + * path is a directory. + * + * @return the file extension (if one is present and + * this is not a directory), else the empty string. + */ + std::string getExtension() const; + + /** + * Returns the absolute representation of this path. + * If this path's pathname starts with a leading '/', + * the returned path is equal to this path. Otherwise, + * this path's pathname is concatenated with the current + * working directory and the result is returned. + * + * @return a new path containing the absolute representation + * of this path + */ + Path getAbsolutePath() const; + + /** + * @return true if this abstract pathname is not empty + * and starts with a leading '/', false otherwise + */ + bool isAbsolute() const; + + /** + * Splits this object's abstract pathname in a vector of file + * and directory name. If the underlying abstract pathname + * starts with a '/', the returned vector's first element + * will be the string "/". + * + * @return a vector of strings, empty if this path is empty + */ + std::vector split() const; + + /** + * Concatenates the specified path with this path in a new + * path object. + * + * @note if the pathname to concatenate with starts with + * a leading '/' then that pathname is returned without + * being concatenated to this object's pathname. + * + * @param path the path to concatenate with + * + * @return the concatenation of the two paths + */ + Path concat(const Path& path) const; + + /** + * Concatenates the specified path with this path and + * stores the result in this path. + * + * @note if the pathname to concatenate with starts with + * a leading '/' then that pathname replaces this object's + * pathname. + * + * @param path the path to concatenate with + */ + void concatToSelf(const Path& path); + + operator std::string const&() const { return m_path; } + + Path operator+(const Path& rhs) const { return concat(rhs); } + Path& operator+=(const Path& rhs) { + concatToSelf(rhs); + return *this; + } + + bool operator==(const Path& rhs) const { return m_path == rhs.m_path; } + bool operator!=(const Path& rhs) const { return m_path != rhs.m_path; } + + bool operator<(const Path& rhs) const { return m_path < rhs.m_path; } + bool operator>(const Path& rhs) const { return m_path > rhs.m_path; } + + friend std::ostream& operator<<(std::ostream& os, const Path& path); + + /** + * Returns a canonical copy of the specified pathname by removing + * unnecessary path segments such as ".", ".." and "/". + * + * @param pathname a pathname string + * + * @return the canonical representation of the specified pathname + */ + static std::string getCanonicalPath(const std::string& pathname); + + /** + * This method is equivalent to calling root.concat(leaf). + */ + static Path concat(const std::string& root, const std::string& leaf); + + /** + * @return a path representing the current working directory + */ + static Path getCurrentDirectory(); + + /** + * @return a path representing the current executable + */ + static Path getCurrentExecutable(); + + /** + * @return a path representing a directory where temporary files can be stored + */ + static Path getTemporaryDirectory(); + + /** + * Creates a directory denoted by the given path. + * This is not recursive and doesn't create intermediate directories. + * + * @return True if directory was successfully created. + * When false, errno should have details on actual error. + */ + bool mkdir() const; + + /** + * Creates a directory denoted by the given path. + * This is recursive and parent directories will be created if they do not + * exist. + * + * @return True if directory was successfully created or already exists. + * When false, errno should have details on actual error. + */ + bool mkdirRecursive() const; + + /** + * Deletes this file. + * + * @return True if file was successfully deleted. + * When false, errno should have details on actual error. + */ + bool unlinkFile(); + + /** + * Lists the contents of this directory, skipping hidden files. + * + * @return A vector of paths of the contents of the directory. If the path points to a file, + * nonexistent directory, or empty directory, an empty vector is returned. + */ + std::vector listContents() const; + +private: + std::string m_path; +}; + +} // namespace utils + +#endif // UTILS_PATH_H_ diff --git a/ios/include/utils/Profiler.h b/ios/include/utils/Profiler.h new file mode 100644 index 00000000..87a3d121 --- /dev/null +++ b/ios/include/utils/Profiler.h @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_UTILS_PROFILER_H +#define TNT_UTILS_PROFILER_H + +#include +#include +#include + +#include // note: This is safe (only used inline) + +#if defined(__linux__) +# include +# include +# include +#endif + +#include + +namespace utils { + +class Profiler { +public: + enum { + INSTRUCTIONS = 0, // must be zero + CPU_CYCLES = 1, + DCACHE_REFS = 2, + DCACHE_MISSES = 3, + BRANCHES = 4, + BRANCH_MISSES = 5, + ICACHE_REFS = 6, + ICACHE_MISSES = 7, + + // Must be last one + EVENT_COUNT + }; + + enum { + EV_CPU_CYCLES = 1u << CPU_CYCLES, + EV_L1D_REFS = 1u << DCACHE_REFS, + EV_L1D_MISSES = 1u << DCACHE_MISSES, + EV_BPU_REFS = 1u << BRANCHES, + EV_BPU_MISSES = 1u << BRANCH_MISSES, + EV_L1I_REFS = 1u << ICACHE_REFS, + EV_L1I_MISSES = 1u << ICACHE_MISSES, + // helpers + EV_L1D_RATES = EV_L1D_REFS | EV_L1D_MISSES, + EV_L1I_RATES = EV_L1I_REFS | EV_L1I_MISSES, + EV_BPU_RATES = EV_BPU_REFS | EV_BPU_MISSES, + }; + + Profiler() noexcept; // must call resetEvents() + explicit Profiler(uint32_t eventMask) noexcept; + ~Profiler() noexcept; + + Profiler(const Profiler& rhs) = delete; + Profiler(Profiler&& rhs) = delete; + Profiler& operator=(const Profiler& rhs) = delete; + Profiler& operator=(Profiler&& rhs) = delete; + + // selects which events are enabled. + uint32_t resetEvents(uint32_t eventMask) noexcept; + + uint32_t getEnabledEvents() const noexcept { return mEnabledEvents; } + + // could return false if performance counters are not supported/enabled + bool isValid() const { return mCountersFd[0] >= 0; } + + class Counters { + friend class Profiler; + uint64_t nr; + uint64_t time_enabled; + uint64_t time_running; + struct { + uint64_t value; + uint64_t id; + } counters[Profiler::EVENT_COUNT]; + + friend Counters operator-(Counters lhs, const Counters& rhs) noexcept { + lhs.nr -= rhs.nr; + lhs.time_enabled -= rhs.time_enabled; + lhs.time_running -= rhs.time_running; + for (size_t i = 0; i < EVENT_COUNT; ++i) { + lhs.counters[i].value -= rhs.counters[i].value; + } + return lhs; + } + + public: + uint64_t getInstructions() const { return counters[INSTRUCTIONS].value; } + uint64_t getCpuCycles() const { return counters[CPU_CYCLES].value; } + uint64_t getL1DReferences() const { return counters[DCACHE_REFS].value; } + uint64_t getL1DMisses() const { return counters[DCACHE_MISSES].value; } + uint64_t getL1IReferences() const { return counters[ICACHE_REFS].value; } + uint64_t getL1IMisses() const { return counters[ICACHE_MISSES].value; } + uint64_t getBranchInstructions() const { return counters[BRANCHES].value; } + uint64_t getBranchMisses() const { return counters[BRANCH_MISSES].value; } + + std::chrono::duration getWallTime() const { + return std::chrono::duration(time_enabled); + } + + std::chrono::duration getRunningTime() const { + return std::chrono::duration(time_running); + } + + double getIPC() const noexcept { + uint64_t cpuCycles = getCpuCycles(); + uint64_t instructions = getInstructions(); + return double(instructions) / double(cpuCycles); + } + + double getCPI() const noexcept { + uint64_t cpuCycles = getCpuCycles(); + uint64_t instructions = getInstructions(); + return double(cpuCycles) / double(instructions); + } + + double getL1DMissRate() const noexcept { + uint64_t cacheReferences = getL1DReferences(); + uint64_t cacheMisses = getL1DMisses(); + return double(cacheMisses) / double(cacheReferences); + } + + double getL1DHitRate() const noexcept { + return 1.0 - getL1DMissRate(); + } + + double getL1IMissRate() const noexcept { + uint64_t cacheReferences = getL1IReferences(); + uint64_t cacheMisses = getL1IMisses(); + return double(cacheMisses) / double(cacheReferences); + } + + double getL1IHitRate() const noexcept { + return 1.0 - getL1IMissRate(); + } + + double getBranchMissRate() const noexcept { + uint64_t branchReferences = getBranchInstructions(); + uint64_t branchMisses = getBranchMisses(); + return double(branchMisses) / double(branchReferences); + } + + double getBranchHitRate() const noexcept { + return 1.0 - getBranchMissRate(); + } + + double getMPKI(uint64_t misses) const noexcept { + return (misses * 1000.0) / getInstructions(); + } + }; + +#if defined(__linux__) + + void reset() noexcept { + int fd = mCountersFd[0]; + ioctl(fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP); + } + + void start() noexcept { + int fd = mCountersFd[0]; + ioctl(fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP); + } + + void stop() noexcept { + int fd = mCountersFd[0]; + ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP); + } + + Counters readCounters() noexcept; + +#else // !__linux__ + + void reset() noexcept { } + void start() noexcept { } + void stop() noexcept { } + Counters readCounters() noexcept { return {}; } + +#endif // __linux__ + + bool hasBranchRates() const noexcept { + return (mCountersFd[BRANCHES] >= 0) && (mCountersFd[BRANCH_MISSES] >= 0); + } + + bool hasICacheRates() const noexcept { + return (mCountersFd[ICACHE_REFS] >= 0) && (mCountersFd[ICACHE_MISSES] >= 0); + } + +private: + UTILS_UNUSED uint8_t mIds[EVENT_COUNT] = {}; + int mCountersFd[EVENT_COUNT]; + uint32_t mEnabledEvents = 0; +}; + +} // namespace utils + +#endif // TNT_UTILS_PROFILER_H diff --git a/ios/include/utils/Range.h b/ios/include/utils/Range.h new file mode 100644 index 00000000..08e668dc --- /dev/null +++ b/ios/include/utils/Range.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_UTILS_RANGE_H +#define TNT_UTILS_RANGE_H + +#include + +#include +#include + +#include + +namespace utils { + +template +struct Range { + using value_type = T; + T first = 0; + T last = 0; + + size_t size() const noexcept { return last - first; } + bool empty() const noexcept { return !size(); } + + class const_iterator { + friend struct Range; + T value = {}; + + public: + const_iterator() noexcept = default; + explicit const_iterator(T value) noexcept : value(value) {} + + using value_type = T; + using pointer = value_type*; + using difference_type = ptrdiff_t; + using iterator_category = std::random_access_iterator_tag; + + + const value_type operator*() const { return value; } + const value_type operator[](size_t n) const { return value + n; } + + const_iterator& operator++() { ++value; return *this; } + const_iterator& operator--() { --value; return *this; } + + const const_iterator operator++(int) { const_iterator t(value); value++; return t; } + const const_iterator operator--(int) { const_iterator t(value); value--; return t; } + + const_iterator operator+(size_t rhs) const { return { value + rhs }; } + const_iterator operator+(size_t rhs) { return { value + rhs }; } + const_iterator operator-(size_t rhs) const { return { value - rhs }; } + + difference_type operator-(const_iterator const& rhs) const { return value - rhs.value; } + + bool operator==(const_iterator const& rhs) const { return (value == rhs.value); } + bool operator!=(const_iterator const& rhs) const { return (value != rhs.value); } + bool operator>=(const_iterator const& rhs) const { return (value >= rhs.value); } + bool operator> (const_iterator const& rhs) const { return (value > rhs.value); } + bool operator<=(const_iterator const& rhs) const { return (value <= rhs.value); } + bool operator< (const_iterator const& rhs) const { return (value < rhs.value); } + }; + + const_iterator begin() noexcept { return const_iterator{ first }; } + const_iterator end() noexcept { return const_iterator{ last }; } + const_iterator begin() const noexcept { return const_iterator{ first }; } + const_iterator end() const noexcept { return const_iterator{ last }; } + + const_iterator front() const noexcept { return const_iterator{ first }; } + const_iterator back() const noexcept { return const_iterator{ last - 1 }; } +}; + +} // namespace utils + +#endif // TNT_UTILS_RANGE_H diff --git a/ios/include/utils/SingleInstanceComponentManager.h b/ios/include/utils/SingleInstanceComponentManager.h new file mode 100644 index 00000000..76f31165 --- /dev/null +++ b/ios/include/utils/SingleInstanceComponentManager.h @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_UTILS_SINGLEINSTANCECOMPONENTMANAGER_H +#define TNT_UTILS_SINGLEINSTANCECOMPONENTMANAGER_H + +#include +#include +#include +#include + +#include + +#include +#include +#include + +namespace utils { + +class EntityManager; + +/* + * Helper class to create single instance component managers. + * + * This handles the component's storage as a structure-of-arrays, as well + * as the garbage collection. + * + * This is intended to be used as base class for a real component manager. When doing so, + * and the real component manager is a public API, make sure to forward the public methods + * to the implementation. + * + */ +template +class UTILS_PUBLIC SingleInstanceComponentManager { +private: + + // this is just to avoid using std::default_random_engine, since we're in a public header. + class default_random_engine { + uint32_t mState = 1u; // must be 0 < seed < 0x7fffffff + public: + inline uint32_t operator()() noexcept { + return mState = uint32_t((uint64_t(mState) * 48271u) % 0x7fffffffu); + } + }; + +protected: + static constexpr size_t ENTITY_INDEX = sizeof ... (Elements); + +public: + using SoA = StructureOfArrays; + + using Instance = EntityInstanceBase::Type; + + SingleInstanceComponentManager() noexcept { + // We always start with a dummy entry because index=0 is reserved. The component + // at index = 0, is guaranteed to be default-initialized. + // Sub-classes can use this to their advantage. + mData.push_back(); + } + + SingleInstanceComponentManager(SingleInstanceComponentManager&& rhs) noexcept {/* = default */} + SingleInstanceComponentManager& operator=(SingleInstanceComponentManager&& rhs) noexcept {/* = default */} + ~SingleInstanceComponentManager() noexcept = default; + + // not copyable + SingleInstanceComponentManager(SingleInstanceComponentManager const& rhs) = delete; + SingleInstanceComponentManager& operator=(SingleInstanceComponentManager const& rhs) = delete; + + + // returns true if the given Entity has a component of this Manager + bool hasComponent(Entity e) const noexcept { + return getInstance(e) != 0; + } + + // Get instance of this Entity to be used to retrieve components + UTILS_NOINLINE + Instance getInstance(Entity e) const noexcept { + auto const& map = mInstanceMap; + // find() generates quite a bit of code + auto pos = map.find(e); + return pos != map.end() ? pos->second : 0; + } + + // returns the number of components (i.e. size of each arrays) + size_t getComponentCount() const noexcept { + // The array as an extra dummy component at index 0, so the visible count is 1 less. + return mData.size() - 1; + } + + bool empty() const noexcept { + return getComponentCount() == 0; + } + + // returns a pointer to the Entity array. This is basically the list + // of entities this component manager handles. + // The pointer becomes invalid when adding or removing a component. + Entity const* getEntities() const noexcept { + return begin(); + } + + Entity getEntity(Instance i) const noexcept { + return elementAt(i); + } + + // Add a component to the given Entity. If the entity already has a component from this + // manager, this function is a no-op. + // This invalidates all pointers components. + inline Instance addComponent(Entity e); + + // Removes a component from the given entity. + // This invalidates all pointers components. + inline Instance removeComponent(Entity e); + + // trigger one round of garbage collection. this is intended to be called on a regular + // basis. This gc gives up after it cannot randomly free 'ratio' component in a row. + void gc(const EntityManager& em, size_t ratio = 4) noexcept { + gc(em, ratio, [this](Entity e) { + removeComponent(e); + }); + } + + // return the first instance + Instance begin() const noexcept { return 1u; } + + // return the past-the-last instance + Instance end() const noexcept { return Instance(begin() + getComponentCount()); } + + // return a pointer to the first element of the ElementIndex'th array + template + typename SoA::template TypeAt* begin() noexcept { + return mData.template data() + 1; + } + + template + typename SoA::template TypeAt const* begin() const noexcept { + return mData.template data() + 1; + } + + // return a pointer to the past-the-end element of the ElementIndex'th array + template + typename SoA::template TypeAt* end() noexcept { + return begin() + getComponentCount(); + } + + template + typename SoA::template TypeAt const* end() const noexcept { + return begin() + getComponentCount(); + } + + // return a Slice<> + template + Slice> slice() noexcept { + return { begin(), end() }; + } + + template + Slice> slice() const noexcept { + return { begin(), end() }; + } + + // return a reference to the index'th element of the ElementIndex'th array + template + typename SoA::template TypeAt& elementAt(Instance index) noexcept { + assert(index); + return data()[index]; + } + + template + typename SoA::template TypeAt const& elementAt(Instance index) const noexcept { + assert(index); + return data()[index]; + } + + // returns a pointer to the RAW ARRAY of components including the first dummy component + // Use with caution. + template + typename SoA::template TypeAt const* raw_array() const noexcept { + return data(); + } + + // We need our own version of Field because mData is private + template + struct Field : public SoA::template Field { + Field(SingleInstanceComponentManager& soa, EntityInstanceBase::Type i) noexcept + : SoA::template Field{ soa.mData, i } { + } + using SoA::template Field::operator =; + }; + +protected: + template + typename SoA::template TypeAt* data() noexcept { + return mData.template data(); + } + + template + typename SoA::template TypeAt const* data() const noexcept { + return mData.template data(); + } + + // swap only internals + void swap(Instance i, Instance j) noexcept { + assert(i); + assert(j); + if (i && j) { + // update the index map + auto& map = mInstanceMap; + Entity& ei = elementAt(i); + Entity& ej = elementAt(j); + std::swap(ei, ej); + if (ei) { + map[ei] = i; + } + if (ej) { + map[ej] = j; + } + } + } + + template + void gc(const EntityManager& em, size_t ratio, + REMOVE removeComponent) noexcept { + Entity const* entities = getEntities(); + size_t count = getComponentCount(); + size_t aliveInARow = 0; + default_random_engine& rng = mRng; + #pragma nounroll + while (count && aliveInARow < ratio) { + // note: using the modulo favorizes lower number + size_t i = rng() % count; + if (UTILS_LIKELY(em.isAlive(entities[i]))) { + ++aliveInARow; + continue; + } + aliveInARow = 0; + count--; + removeComponent(entities[i]); + } + } + +protected: + SoA mData; + +private: + // maps an entity to an instance index + tsl::robin_map mInstanceMap; + default_random_engine mRng; +}; + +// Keep these outside of the class because CLion has trouble parsing them +template +typename SingleInstanceComponentManager::Instance +SingleInstanceComponentManager::addComponent(Entity e) { + Instance ci = 0; + if (!e.isNull()) { + if (!hasComponent(e)) { + // this is like a push_back(e); + mData.push_back().template back() = e; + // index 0 is used when the component doesn't exist + ci = Instance(mData.size() - 1); + mInstanceMap[e] = ci; + } else { + // if the entity already has this component, just return its instance + ci = mInstanceMap[e]; + } + } + assert(ci != 0); + return ci; +} + +// Keep these outside of the class because CLion has trouble parsing them +template +typename SingleInstanceComponentManager::Instance +SingleInstanceComponentManager::removeComponent(Entity e) { + auto& map = mInstanceMap; + auto pos = map.find(e); + if (UTILS_LIKELY(pos != map.end())) { + size_t index = pos->second; + assert(index != 0); + size_t last = mData.size() - 1; + if (last != index) { + // move the last item to where we removed this component, as to keep + // the array tightly packed. + mData.forEach([index, last](auto* p) { + p[index] = std::move(p[last]); + }); + + Entity lastEntity = mData.template elementAt(index); + map[lastEntity] = index; + } + mData.pop_back(); + map.erase(pos); + return last; + } + return 0; +} + + +} // namespace filament + +#endif // TNT_UTILS_SINGLEINSTANCECOMPONENTMANAGER_H diff --git a/ios/include/utils/Slice.h b/ios/include/utils/Slice.h new file mode 100644 index 00000000..ed61616c --- /dev/null +++ b/ios/include/utils/Slice.h @@ -0,0 +1,373 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_UTILS_SLICE_H +#define TNT_UTILS_SLICE_H + +#include +#include + +#include +#include + +#include + +namespace utils { + +/* + * A fixed-size slice of a container + */ +template +class Slice { +public: + using iterator = T*; + using const_iterator = T const*; + using value_type = T; + using reference = T&; + using const_reference = T const&; + using pointer = T*; + using const_pointer = T const*; + using size_type = SIZE_TYPE; + + Slice() noexcept = default; + + Slice(const_iterator begin, const_iterator end) noexcept + : mBegin(const_cast(begin)), mEndOffset(size_type(end - begin)) { + } + + Slice(const_pointer begin, size_type count) noexcept + : mBegin(const_cast(begin)), mEndOffset(size_type(count)) { + } + + Slice(Slice const& rhs) noexcept = default; + Slice(Slice&& rhs) noexcept = default; + Slice& operator=(Slice const& rhs) noexcept = default; + Slice& operator=(Slice&& rhs) noexcept = default; + + void set(pointer begin, size_type count) UTILS_RESTRICT noexcept { + mBegin = begin; + mEndOffset = size_type(count); + } + + void set(iterator begin, iterator end) UTILS_RESTRICT noexcept { + mBegin = begin; + mEndOffset = size_type(end - begin); + } + + void swap(Slice& rhs) UTILS_RESTRICT noexcept { + std::swap(mBegin, rhs.mBegin); + std::swap(mEndOffset, rhs.mEndOffset); + } + + void clear() UTILS_RESTRICT noexcept { + mBegin = nullptr; + mEndOffset = 0; + } + + // size + size_t size() const UTILS_RESTRICT noexcept { return mEndOffset; } + size_t sizeInBytes() const UTILS_RESTRICT noexcept { return size() * sizeof(T); } + bool empty() const UTILS_RESTRICT noexcept { return size() == 0; } + + // iterators + iterator begin() UTILS_RESTRICT noexcept { return mBegin; } + const_iterator begin() const UTILS_RESTRICT noexcept { return mBegin; } + const_iterator cbegin() const UTILS_RESTRICT noexcept { return this->begin(); } + iterator end() UTILS_RESTRICT noexcept { return &mBegin[mEndOffset]; } + const_iterator end() const UTILS_RESTRICT noexcept { return &mBegin[mEndOffset]; } + const_iterator cend() const UTILS_RESTRICT noexcept { return this->end(); } + + // data access + reference operator[](size_t n) UTILS_RESTRICT noexcept { + assert(n < size()); + return mBegin[n]; + } + + const_reference operator[](size_t n) const UTILS_RESTRICT noexcept { + assert(n < size()); + return mBegin[n]; + } + + reference at(size_t n) UTILS_RESTRICT noexcept { + return operator[](n); + } + + const_reference at(size_t n) const UTILS_RESTRICT noexcept { + return operator[](n); + } + + reference front() UTILS_RESTRICT noexcept { + assert(!empty()); + return *mBegin; + } + + const_reference front() const UTILS_RESTRICT noexcept { + assert(!empty()); + return *mBegin; + } + + reference back() UTILS_RESTRICT noexcept { + assert(!empty()); + return *(this->end() - 1); + } + + const_reference back() const UTILS_RESTRICT noexcept { + assert(!empty()); + return *(this->end() - 1); + } + + pointer data() UTILS_RESTRICT noexcept { + return this->begin(); + } + + const_pointer data() const UTILS_RESTRICT noexcept { + return this->begin(); + } + +protected: + iterator mBegin = nullptr; + size_type mEndOffset = 0; +}; + +/* + * A fixed-capacity (but growable) slice of a container + */ +template +class UTILS_PRIVATE GrowingSlice : public Slice { +public: + using iterator = typename Slice::iterator; + using const_iterator = typename Slice::const_iterator; + using value_type = typename Slice::value_type; + using reference = typename Slice::reference; + using const_reference = typename Slice::const_reference; + using pointer = typename Slice::pointer; + using const_pointer = typename Slice::const_pointer; + using size_type = typename Slice::size_type; + + GrowingSlice() noexcept = default; + GrowingSlice(GrowingSlice const& rhs) noexcept = default; + GrowingSlice(GrowingSlice&& rhs) noexcept = default; + + template + GrowingSlice(Iter begin, size_type count) noexcept + : Slice(begin, size_type(0)), + mCapOffset(count) { + } + + GrowingSlice& operator=(GrowingSlice const& rhs) noexcept = default; + GrowingSlice& operator=(GrowingSlice&& rhs) noexcept = default; + + // size + size_t remain() const UTILS_RESTRICT noexcept { return mCapOffset - this->mEndOffset; } + size_t capacity() const UTILS_RESTRICT noexcept { return mCapOffset; } + + template + void set(Iter begin, size_type count) UTILS_RESTRICT noexcept { + this->Slice::set(begin, count); + mCapOffset = count; + } + + template + void set(Iter begin, Iter end) UTILS_RESTRICT noexcept { + this->Slice::set(begin, end); + mCapOffset = size_type(end - begin); + } + + void swap(GrowingSlice& rhs) UTILS_RESTRICT noexcept { + Slice::swap(rhs); + std::swap(mCapOffset, rhs.mCapOffset); + } + + void clear() UTILS_RESTRICT noexcept { + this->mEndOffset = 0; + } + + void resize(size_type count) UTILS_RESTRICT noexcept { + assert(count < mCapOffset); + this->mEndOffset = size_type(count); + } + + T* grow(size_type count) UTILS_RESTRICT noexcept { + assert(this->size() + count <= mCapOffset); + size_t offset = this->mEndOffset; + this->mEndOffset += count; + return this->mBegin + offset; + } + + // data access + void push_back(T const& item) UTILS_RESTRICT noexcept { + T* const p = this->grow(1); + *p = item; + } + + void push_back(T&& item) UTILS_RESTRICT noexcept { + T* const p = this->grow(1); + *p = std::move(item); + } + + template + void emplace_back(ARGS&& ... args) UTILS_RESTRICT noexcept { + T* const p = this->grow(1); + new(p) T(std::forward(args)...); + } + +private: + // we use size_type == uint32_t to reduce the size on 64-bits machines + size_type mCapOffset = 0; +}; + +// ------------------------------------------------------------------------------------------------ + +/* + * A fixed-capacity (but atomically growable) slice of a container + */ +template +class AtomicGrowingSlice { +public: + using iterator = T*; + using const_iterator = T const*; + using value_type = T; + using reference = T&; + using const_reference = T const&; + using pointer = T*; + using const_pointer = T const*; + using size_type = SIZE_TYPE; + + AtomicGrowingSlice() noexcept = default; + + template + AtomicGrowingSlice(Iter begin, Iter end) noexcept + : mBegin(iterator(begin)), + mEndOffset(0), + mCapOffset(size_type(iterator(end) - iterator(begin))) { + } + + template + AtomicGrowingSlice(Iter begin, size_type count) noexcept + : mBegin(iterator(begin)), mEndOffset(0), mCapOffset(size_type(count)) { + } + + template + void set(Iter begin, size_type count) noexcept { + assert(mBegin == nullptr); + mBegin = iterator(begin); + mEndOffset.store(0, std::memory_order_relaxed); + mCapOffset = count; + } + + // clear + void clear() noexcept { + mEndOffset.store(0, std::memory_order_relaxed); + } + + // size + size_type size() const noexcept { return mEndOffset.load(std::memory_order_relaxed); } + bool empty() const noexcept { return size() == 0; } + size_type remain() const noexcept { return mCapOffset - size(); } + size_type capacity() const noexcept { return mCapOffset; } + + // iterators + iterator begin() noexcept { return mBegin; } + const_iterator begin() const noexcept { return mBegin; } + const_iterator cbegin() const noexcept { return begin(); } + iterator end() noexcept { return &mBegin[size()]; } + const_iterator end() const noexcept { return &mBegin[size()]; } + const_iterator cend() const noexcept { return end(); } + + // data access + reference operator[](size_type n) noexcept { + assert(n < size()); + return mBegin[n]; + } + + const_reference operator[](size_type n) const noexcept { + assert(n < size()); + return mBegin[n]; + } + + reference at(size_type n) noexcept { + return operator[](n); + } + + const_reference at(size_type n) const noexcept { + return operator[](n); + } + + reference front() noexcept { + assert(!empty()); + return *mBegin; + } + + const_reference front() const noexcept { + assert(!empty()); + return *mBegin; + } + + reference back() noexcept { + assert(!empty()); + return *(end() - 1); + } + + const_reference back() const noexcept { + assert(!empty()); + return *(end() - 1); + } + + pointer data() noexcept { + return begin(); + } + + const_pointer data() const noexcept { + return begin(); + } + + T* grow(size_type count) noexcept { + size_type offset = this->mEndOffset.load(std::memory_order_relaxed); + do { + if (UTILS_UNLIKELY(offset + count > mCapOffset)) { + return nullptr; + } + } while (UTILS_UNLIKELY(!this->mEndOffset.compare_exchange_weak(offset, offset + count, + std::memory_order_relaxed, std::memory_order_relaxed))); + return this->mBegin + offset; + } + + // data access + void push_back(T const& item) noexcept { + T* const p = this->grow(1); + *p = item; + } + + void push_back(T&& item) noexcept { + T* const p = this->grow(1); + *p = std::move(item); + } + + template + void emplace_back(ARGS&& ... args) noexcept { + T* const p = this->grow(1); + new(p) T(std::forward(args)...); + } + +private: + iterator mBegin = nullptr; + std::atomic mEndOffset = ATOMIC_VAR_INIT(0); + size_type mCapOffset = 0; +}; + +} // namespace utils + +#endif // TNT_UTILS_SLICE_H diff --git a/ios/include/utils/SpinLock.h b/ios/include/utils/SpinLock.h new file mode 100644 index 00000000..797e1627 --- /dev/null +++ b/ios/include/utils/SpinLock.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_UTILS_SPINLOCK_H +#define TNT_UTILS_SPINLOCK_H + +#include + +#include + +#include +#include + +#include +#include + +namespace utils { +namespace details { + +class SpinLock { + std::atomic_flag mLock = ATOMIC_FLAG_INIT; + +public: + void lock() noexcept { + UTILS_PREFETCHW(&mLock); +#ifdef __ARM_ACLE + // we signal an event on this CPU, so that the first yield() will be a no-op, + // and falls through the test_and_set(). This is more efficient than a while { } + // construct. + UTILS_SIGNAL_EVENT(); + do { + yield(); + } while (mLock.test_and_set(std::memory_order_acquire)); +#else + goto start; + do { + yield(); +start: ; + } while (mLock.test_and_set(std::memory_order_acquire)); +#endif + } + + void unlock() noexcept { + mLock.clear(std::memory_order_release); +#ifdef __ARM_ARCH_7A__ + // on ARMv7a SEL is needed + UTILS_SIGNAL_EVENT(); + // as well as a memory barrier is needed + __dsb(0xA); // ISHST = 0xA (b1010) +#else + // on ARMv8 we could avoid the call to SE, but we'd need to write the + // test_and_set() above by hand, so the WFE only happens without a STRX first. + UTILS_BROADCAST_EVENT(); +#endif + } + +private: + inline void yield() noexcept { + // on x86 call pause instruction, on ARM call WFE + UTILS_WAIT_FOR_EVENT(); + } +}; +} // namespace details + +#if defined(__SANITIZE_THREAD__) +// Unfortunately TSAN doesn't support homegrown synchronization primitives +using SpinLock = Mutex; +#elif defined(__ARM_ARCH_7A__) +// We've had problems with "wfe" on some ARM-V7 devices, causing spurious SIGILL +using SpinLock = Mutex; +#else +using SpinLock = details::SpinLock; +#endif + +} // namespace utils + +#endif //TNT_UTILS_SPINLOCK_H diff --git a/ios/include/utils/StructureOfArrays.h b/ios/include/utils/StructureOfArrays.h new file mode 100644 index 00000000..dcd62ed6 --- /dev/null +++ b/ios/include/utils/StructureOfArrays.h @@ -0,0 +1,646 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_UTILS_STRUCTUREOFARRAYS_H +#define TNT_UTILS_STRUCTUREOFARRAYS_H + +#include // note: this is safe, see how std::array is used below (inline / private) +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace utils { + +template +class StructureOfArraysBase { + // number of elements + static constexpr const size_t kArrayCount = sizeof...(Elements); + +public: + using SoA = StructureOfArraysBase; + + // Type of the Nth array + template + using TypeAt = typename std::tuple_element>::type; + + // Number of arrays + static constexpr size_t getArrayCount() noexcept { return kArrayCount; } + + // Size needed to store "size" array elements + static size_t getNeededSize(size_t size) noexcept { + return getOffset(kArrayCount - 1, size) + sizeof(TypeAt) * size; + } + + // -------------------------------------------------------------------------------------------- + + class Structure; + template class Iterator; + using iterator = Iterator; + using const_iterator = Iterator; + using size_type = size_t; + using difference_type = ptrdiff_t; + + /* + * An object that represents a reference to the type dereferenced by iterator. + * In other words, it's the return type of iterator::operator*(), and since it + * cannot be a C++ reference (&), it's an object that acts like it. + */ + class StructureRef { + friend class Structure; + friend iterator; + friend const_iterator; + StructureOfArraysBase* const UTILS_RESTRICT soa; + size_t const index; + + StructureRef(StructureOfArraysBase* soa, size_t index) : soa(soa), index(index) { } + + // assigns a value_type to a reference (i.e. assigns to what's pointed to by the reference) + template + StructureRef& assign(Structure const& rhs, std::index_sequence); + + // assigns a value_type to a reference (i.e. assigns to what's pointed to by the reference) + template + StructureRef& assign(Structure&& rhs, std::index_sequence) noexcept; + + // objects pointed to by reference can be swapped, so provide the special swap() function. + friend void swap(StructureRef lhs, StructureRef rhs) { + lhs.soa->swap(lhs.index, rhs.index); + } + + public: + // references can be created by copy-assignment only + StructureRef(StructureRef const& rhs) noexcept : soa(rhs.soa), index(rhs.index) { } + + // copy the content of a reference to the content of this one + StructureRef& operator=(StructureRef const& rhs); + + // move the content of a reference to the content of this one + StructureRef& operator=(StructureRef&& rhs) noexcept; + + // copy a value_type to the content of this reference + StructureRef& operator=(Structure const& rhs) { + return assign(rhs, std::make_index_sequence()); + } + + // move a value_type to the content of this reference + StructureRef& operator=(Structure&& rhs) noexcept { + return assign(rhs, std::make_index_sequence()); + } + + // access the elements of this reference (i.e. the "fields" of the structure) + template TypeAt const& get() const { return soa->elementAt(index); } + template TypeAt& get() { return soa->elementAt(index); } + }; + + + /* + * The value_type of iterator. This is basically the "structure" of the SoA. + * Internally we're using a tuple<> to store the data. + * This object is not trivial to construct, as it copies an entry of the SoA. + */ + class Structure { + friend class StructureRef; + friend iterator; + friend const_iterator; + using Type = std::tuple::type...>; + Type elements; + + template + static Type init(StructureRef const& rhs, std::index_sequence) { + return Type{ rhs.soa->template elementAt(rhs.index)... }; + } + + template + static Type init(StructureRef&& rhs, std::index_sequence) noexcept { + return Type{ std::move(rhs.soa->template elementAt(rhs.index))... }; + } + + public: + Structure(Structure const& rhs) = default; + Structure(Structure&& rhs) noexcept = default; + Structure& operator=(Structure const& rhs) = default; + Structure& operator=(Structure&& rhs) noexcept = default; + + // initialize and assign from a StructureRef + Structure(StructureRef const& rhs) + : elements(init(rhs, std::make_index_sequence())) {} + Structure(StructureRef&& rhs) noexcept + : elements(init(rhs, std::make_index_sequence())) {} + Structure& operator=(StructureRef const& rhs) { return operator=(Structure(rhs)); } + Structure& operator=(StructureRef&& rhs) noexcept { return operator=(Structure(rhs)); } + + // access the elements of this value_Type (i.e. the "fields" of the structure) + template TypeAt const& get() const { return std::get(elements); } + template TypeAt& get() { return std::get(elements); } + }; + + + /* + * An iterator to the SoA. This is only intended to be used with STL's algorithm, e.g.: sort(). + * Normally, SoA is not iterated globally, but rather an array at a time. + * Iterating itself is not too costly, as well as dereferencing by reference. However, + * dereferencing by value is. + */ + template + class Iterator { + friend class StructureOfArraysBase; + CVQualifiedSOAPointer soa; // don't use restrict, can have aliases if multiple iterators are created + size_t index; + + Iterator(CVQualifiedSOAPointer soa, size_t index) : soa(soa), index(index) {} + + public: + using value_type = Structure; + using reference = StructureRef; + using pointer = StructureRef*; // FIXME: this should be a StructurePtr type + using difference_type = ptrdiff_t; + using iterator_category = std::random_access_iterator_tag; + + Iterator(Iterator const& rhs) noexcept = default; + Iterator& operator=(Iterator const& rhs) = default; + + reference operator*() const { return { soa, index }; } + reference operator*() { return { soa, index }; } + reference operator[](size_t n) { return *(*this + n); } + + template TypeAt const& get() const { return soa->template elementAt(index); } + template TypeAt& get() { return soa->template elementAt(index); } + + Iterator& operator++() { ++index; return *this; } + Iterator& operator--() { --index; return *this; } + Iterator& operator+=(size_t n) { index += n; return *this; } + Iterator& operator-=(size_t n) { index -= n; return *this; } + Iterator operator+(size_t n) const { return { soa, index + n }; } + Iterator operator-(size_t n) const { return { soa, index - n }; } + difference_type operator-(Iterator const& rhs) const { return index - rhs.index; } + bool operator==(Iterator const& rhs) const { return (index == rhs.index); } + bool operator!=(Iterator const& rhs) const { return (index != rhs.index); } + bool operator>=(Iterator const& rhs) const { return (index >= rhs.index); } + bool operator> (Iterator const& rhs) const { return (index > rhs.index); } + bool operator<=(Iterator const& rhs) const { return (index <= rhs.index); } + bool operator< (Iterator const& rhs) const { return (index < rhs.index); } + + // Postfix operator needed by Microsoft STL. + const Iterator operator++(int) { Iterator it(*this); index++; return it; } + const Iterator operator--(int) { Iterator it(*this); index--; return it; } + }; + + iterator begin() noexcept { return { this, 0u }; } + iterator end() noexcept { return { this, mSize }; } + const_iterator begin() const noexcept { return { this, 0u }; } + const_iterator end() const noexcept { return { this, mSize }; } + + // -------------------------------------------------------------------------------------------- + + StructureOfArraysBase() = default; + + explicit StructureOfArraysBase(size_t capacity) { + setCapacity(capacity); + } + + // not copyable for now + StructureOfArraysBase(StructureOfArraysBase const& rhs) = delete; + StructureOfArraysBase& operator=(StructureOfArraysBase const& rhs) = delete; + + // movability is trivial, so support it + StructureOfArraysBase(StructureOfArraysBase&& rhs) noexcept { + using std::swap; + swap(mCapacity, rhs.mCapacity); + swap(mSize, rhs.mSize); + swap(mArrayOffset, rhs.mArrayOffset); + swap(mAllocator, rhs.mAllocator); + } + + StructureOfArraysBase& operator=(StructureOfArraysBase&& rhs) noexcept { + if (this != &rhs) { + using std::swap; + swap(mCapacity, rhs.mCapacity); + swap(mSize, rhs.mSize); + swap(mArrayOffset, rhs.mArrayOffset); + swap(mAllocator, rhs.mAllocator); + } + return *this; + } + + ~StructureOfArraysBase() { + destroy_each(0, mSize); + mAllocator.free(mArrayOffset[0]); + } + + // -------------------------------------------------------------------------------------------- + + // return the size the array + size_t size() const noexcept { + return mSize; + } + + // return the capacity of the array + size_t capacity() const noexcept { + return mCapacity; + } + + // set the capacity of the array. the capacity cannot be smaller than the current size, + // the call is a no-op in that case. + UTILS_NOINLINE + void setCapacity(size_t capacity) { + // allocate enough space for "capacity" elements of each array + // capacity cannot change when optional storage is specified + if (capacity >= mSize) { + const size_t sizeNeeded = getNeededSize(capacity); + void* buffer = mAllocator.alloc(sizeNeeded); + + // move all the items (one array at a time) from the old allocation to the new + // this also update the array pointers + move_each(buffer, capacity); + + // free the old buffer + std::swap(buffer, mArrayOffset[0]); + mAllocator.free(buffer); + + // and make sure to update the capacity + mCapacity = capacity; + } + } + + void ensureCapacity(size_t needed) { + if (UTILS_UNLIKELY(needed > mCapacity)) { + // not enough space, increase the capacity + const size_t capacity = (needed * 3 + 1) / 2; + setCapacity(capacity); + } + } + + // grow or shrink the array to the given size. When growing, new elements are constructed + // with their default constructor. when shrinking, discarded elements are destroyed. + // If the arrays don't have enough capacity, the capacity is increased accordingly + // (the capacity is set to 3/2 of the asked size). + UTILS_NOINLINE + void resize(size_t needed) { + ensureCapacity(needed); + resizeNoCheck(needed); + if (needed <= mCapacity) { + // TODO: see if we should shrink the arrays + } + } + + void clear() noexcept { + resizeNoCheck(0); + } + + + inline void swap(size_t i, size_t j) noexcept { + forEach([i, j](auto p) { + using std::swap; + swap(p[i], p[j]); + }); + } + + // remove and destroy the last element of each array + inline void pop_back() noexcept { + if (mSize) { + destroy_each(mSize - 1, mSize); + mSize--; + } + } + + // create an element at the end of each array + StructureOfArraysBase& push_back() noexcept { + resize(mSize + 1); + return *this; + } + + StructureOfArraysBase& push_back(Elements const& ... args) noexcept { + ensureCapacity(mSize + 1); + return push_back_unsafe(args...); + } + + StructureOfArraysBase& push_back(Elements&& ... args) noexcept { + ensureCapacity(mSize + 1); + return push_back_unsafe(std::forward(args)...); + } + + StructureOfArraysBase& push_back_unsafe(Elements const& ... args) noexcept { + const size_t last = mSize++; + size_t i = 0; + int UTILS_UNUSED dummy[] = { + (new(getArray(i) + last)Elements(args), i++, 0)... }; + return *this; + } + + StructureOfArraysBase& push_back_unsafe(Elements&& ... args) noexcept { + const size_t last = mSize++; + size_t i = 0; + int UTILS_UNUSED dummy[] = { + (new(getArray(i) + last)Elements(std::forward(args)), i++, 0)... }; + return *this; + } + + template + void forEach(F&& f, ARGS&& ... args) { + size_t i = 0; + int UTILS_UNUSED dummy[] = { + (f(getArray(i), std::forward(args)...), i++, 0)... }; + } + + // return a pointer to the first element of the ElementIndex]th array + template + TypeAt* data() noexcept { + return getArray>(ElementIndex); + } + + template + TypeAt const* data() const noexcept { + return getArray>(ElementIndex); + } + + template + TypeAt* begin() noexcept { + return getArray>(ElementIndex); + } + + template + TypeAt const* begin() const noexcept { + return getArray>(ElementIndex); + } + + template + TypeAt* end() noexcept { + return getArray>(ElementIndex) + size(); + } + + template + TypeAt const* end() const noexcept { + return getArray>(ElementIndex) + size(); + } + + template + Slice> slice() noexcept { + return { begin(), end() }; + } + + template + Slice> slice() const noexcept { + return { begin(), end() }; + } + + // return a reference to the index'th element of the ElementIndex'th array + template + TypeAt& elementAt(size_t index) noexcept { + return data()[index]; + } + + template + TypeAt const& elementAt(size_t index) const noexcept { + return data()[index]; + } + + // return a reference to the last element of the ElementIndex'th array + template + TypeAt& back() noexcept { + return data()[size() - 1]; + } + + template + TypeAt const& back() const noexcept { + return data()[size() - 1]; + } + + template + struct Field { + SoA& soa; + EntityInstanceBase::Type i; + using Type = typename SoA::template TypeAt; + + UTILS_ALWAYS_INLINE Field& operator = (Field&& rhs) noexcept { + soa.elementAt(i) = soa.elementAt(rhs.i); + return *this; + } + + // auto-conversion to the field's type + UTILS_ALWAYS_INLINE operator Type&() noexcept { + return soa.elementAt(i); + } + UTILS_ALWAYS_INLINE operator Type const&() const noexcept { + return soa.elementAt(i); + } + // dereferencing the selected field + UTILS_ALWAYS_INLINE Type& operator ->() noexcept { + return soa.elementAt(i); + } + UTILS_ALWAYS_INLINE Type const& operator ->() const noexcept { + return soa.elementAt(i); + } + // address-of the selected field + UTILS_ALWAYS_INLINE Type* operator &() noexcept { + return &soa.elementAt(i); + } + UTILS_ALWAYS_INLINE Type const* operator &() const noexcept { + return &soa.elementAt(i); + } + // assignment to the field + UTILS_ALWAYS_INLINE Type const& operator = (Type const& other) noexcept { + return (soa.elementAt(i) = other); + } + UTILS_ALWAYS_INLINE Type const& operator = (Type&& other) noexcept { + return (soa.elementAt(i) = other); + } + // comparisons + UTILS_ALWAYS_INLINE bool operator==(Type const& other) const { + return (soa.elementAt(i) == other); + } + UTILS_ALWAYS_INLINE bool operator!=(Type const& other) const { + return (soa.elementAt(i) != other); + } + // calling the field + template + UTILS_ALWAYS_INLINE decltype(auto) operator()(ARGS&& ... args) noexcept { + return soa.elementAt(i)(std::forward(args)...); + } + template + UTILS_ALWAYS_INLINE decltype(auto) operator()(ARGS&& ... args) const noexcept { + return soa.elementAt(i)(std::forward(args)...); + } + }; + +private: + template + T const* getArray(size_t arrayIndex) const { + return static_cast(mArrayOffset[arrayIndex]); + } + + template + T* getArray(size_t arrayIndex) { + return static_cast(mArrayOffset[arrayIndex]); + } + + inline void resizeNoCheck(size_t needed) noexcept { + assert(mCapacity >= needed); + if (needed < mSize) { + // we shrink the arrays + destroy_each(needed, mSize); + } else if (needed > mSize) { + // we grow the arrays + construct_each(mSize, needed); + } + // record the new size of the arrays + mSize = needed; + } + + // this calculate the offset adjusted for all data alignment of a given array + static inline size_t getOffset(size_t index, size_t capacity) noexcept { + auto offsets = getOffsets(capacity); + return offsets[index]; + } + + static inline std::array getOffsets(size_t capacity) noexcept { + // compute the required size of each array + const size_t sizes[] = { (sizeof(Elements) * capacity)... }; + + // we align each array to the same alignment guaranteed by malloc + const size_t align = alignof(std::max_align_t); + + // hopefully most of this gets unrolled and inlined + std::array offsets; + offsets[0] = 0; + #pragma unroll + for (size_t i = 1; i < kArrayCount; i++) { + size_t unalignment = sizes[i - 1] % align; + size_t alignment = unalignment ? (align - unalignment) : 0; + offsets[i] = offsets[i - 1] + (sizes[i - 1] + alignment); + } + return offsets; + } + + void construct_each(size_t from, size_t to) noexcept { + forEach([from, to](auto p) { + using T = typename std::decay::type; + // note: scalar types like int/float get initialized to zero + for (size_t i = from; i < to; i++) { + new(p + i) T(); + } + }); + } + + void destroy_each(size_t from, size_t to) noexcept { + forEach([from, to](auto p) { + using T = typename std::decay::type; + for (size_t i = from; i < to; i++) { + p[i].~T(); + } + }); + } + + void move_each(void* buffer, size_t capacity) noexcept { + auto offsets = getOffsets(capacity); + size_t index = 0; + if (mSize) { + auto size = mSize; // placate a compiler warning + forEach([buffer, &index, &offsets, size](auto p) { + using T = typename std::decay::type; + T* UTILS_RESTRICT b = static_cast(buffer); + + // go through each element and move them from the old array to the new + // then destroy them from the old array + T* UTILS_RESTRICT const arrayPointer = + reinterpret_cast(uintptr_t(b) + offsets[index]); + + // for trivial cases, just call memcpy() + if (std::is_trivially_copyable::value && + std::is_trivially_destructible::value) { + memcpy(arrayPointer, p, size * sizeof(T)); + } else { + for (size_t i = 0; i < size; i++) { + // we move an element by using the in-place move-constructor + new(arrayPointer + i) T(std::move(p[i])); + // and delete them by calling the destructor directly + p[i].~T(); + } + } + index++; + }); + } + + // update the pointers (the first offset will be filled later + for (size_t i = 1; i < kArrayCount; i++) { + mArrayOffset[i] = (char*)buffer + offsets[i]; + } + } + + // capacity in array elements + size_t mCapacity = 0; + // size in array elements + size_t mSize = 0; + // N pointers to each arrays + void *mArrayOffset[kArrayCount] = { nullptr }; + Allocator mAllocator; +}; + +template +inline +typename StructureOfArraysBase::StructureRef& +StructureOfArraysBase::StructureRef::operator=( + StructureOfArraysBase::StructureRef const& rhs) { + return operator=(Structure(rhs)); +} + +template +inline +typename StructureOfArraysBase::StructureRef& +StructureOfArraysBase::StructureRef::operator=( + StructureOfArraysBase::StructureRef&& rhs) noexcept { + return operator=(Structure(rhs)); +} + +template +template +inline +typename StructureOfArraysBase::StructureRef& +StructureOfArraysBase::StructureRef::assign( + StructureOfArraysBase::Structure const& rhs, std::index_sequence) { + // implements StructureRef& StructureRef::operator=(Structure const& rhs) + auto UTILS_UNUSED l = { (soa->elementAt(index) = std::get(rhs.elements), 0)... }; + return *this; +} + +template +template +inline +typename StructureOfArraysBase::StructureRef& +StructureOfArraysBase::StructureRef::assign( + StructureOfArraysBase::Structure&& rhs, std::index_sequence) noexcept { + // implements StructureRef& StructureRef::operator=(Structure&& rhs) noexcept + auto UTILS_UNUSED l = { + (soa->elementAt(index) = std::move(std::get(rhs.elements)), 0)... }; + return *this; +} + +template +using StructureOfArrays = StructureOfArraysBase, Elements ...>; + +} // namespace utils + +#endif // TNT_UTILS_STRUCTUREOFARRAYS_H + diff --git a/ios/include/utils/Systrace.h b/ios/include/utils/Systrace.h new file mode 100644 index 00000000..eaab835a --- /dev/null +++ b/ios/include/utils/Systrace.h @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_UTILS_SYSTRACE_H +#define TNT_UTILS_SYSTRACE_H + + +#define SYSTRACE_TAG_NEVER (0) +#define SYSTRACE_TAG_ALWAYS (1<<0) +#define SYSTRACE_TAG_FILAMENT (1<<1) // don't change, used in makefiles +#define SYSTRACE_TAG_JOBSYSTEM (1<<2) + + +#if defined(ANDROID) + +#include + +#include +#include +#include + +#include + +/* + * The SYSTRACE_ macros use SYSTRACE_TAG as a the TAG, which should be defined + * before this file is included. If not, the SYSTRACE_TAG_ALWAYS tag will be used. + */ + +#ifndef SYSTRACE_TAG +#define SYSTRACE_TAG (SYSTRACE_TAG_ALWAYS) +#endif + +// enable tracing +#define SYSTRACE_ENABLE() ::utils::details::Systrace::enable(SYSTRACE_TAG) + +// disable tracing +#define SYSTRACE_DISABLE() ::utils::details::Systrace::disable(SYSTRACE_TAG) + + +/** + * Creates a Systrace context in the current scope. needed for calling all other systrace + * commands below. + */ +#define SYSTRACE_CONTEXT() ::utils::details::Systrace ___tracer(SYSTRACE_TAG) + + +// SYSTRACE_NAME traces the beginning and end of the current scope. To trace +// the correct start and end times this macro should be declared first in the +// scope body. +// It also automatically creates a Systrace context +#define SYSTRACE_NAME(name) ::utils::details::ScopedTrace ___tracer(SYSTRACE_TAG, name) + +// SYSTRACE_CALL is an SYSTRACE_NAME that uses the current function name. +#define SYSTRACE_CALL() SYSTRACE_NAME(__FUNCTION__) + +#define SYSTRACE_NAME_BEGIN(name) \ + ___tracer.traceBegin(SYSTRACE_TAG, name) + +#define SYSTRACE_NAME_END() \ + ___tracer.traceEnd(SYSTRACE_TAG) + + +/** + * Trace the beginning of an asynchronous event. Unlike ATRACE_BEGIN/ATRACE_END + * contexts, asynchronous events do not need to be nested. The name describes + * the event, and the cookie provides a unique identifier for distinguishing + * simultaneous events. The name and cookie used to begin an event must be + * used to end it. + */ +#define SYSTRACE_ASYNC_BEGIN(name, cookie) \ + ___tracer.asyncBegin(SYSTRACE_TAG, name, cookie) + +/** + * Trace the end of an asynchronous event. + * This should have a corresponding SYSTRACE_ASYNC_BEGIN. + */ +#define SYSTRACE_ASYNC_END(name, cookie) \ + ___tracer.asyncEnd(SYSTRACE_TAG, name, cookie) + +/** + * Traces an integer counter value. name is used to identify the counter. + * This can be used to track how a value changes over time. + */ +#define SYSTRACE_VALUE32(name, val) \ + ___tracer.value(SYSTRACE_TAG, name, int32_t(val)) + +#define SYSTRACE_VALUE64(name, val) \ + ___tracer.value(SYSTRACE_TAG, name, int64_t(val)) + +// ------------------------------------------------------------------------------------------------ +// No user serviceable code below... +// ------------------------------------------------------------------------------------------------ + +namespace utils { +namespace details { + +class Systrace { +public: + + enum tags { + NEVER = SYSTRACE_TAG_NEVER, + ALWAYS = SYSTRACE_TAG_ALWAYS, + FILAMENT = SYSTRACE_TAG_FILAMENT, + JOBSYSTEM = SYSTRACE_TAG_JOBSYSTEM + // we could define more TAGS here, as we need them. + }; + + Systrace(uint32_t tag) noexcept { + if (tag) init(tag); + } + + static void enable(uint32_t tags) noexcept; + static void disable(uint32_t tags) noexcept; + + + inline void traceBegin(uint32_t tag, const char* name) noexcept { + if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) { + beginSection(this, name); + } + } + + inline void traceEnd(uint32_t tag) noexcept { + if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) { + endSection(this); + } + } + + inline void asyncBegin(uint32_t tag, const char* name, int32_t cookie) noexcept { + if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) { + beginAsyncSection(this, name, cookie); + } + } + + inline void asyncEnd(uint32_t tag, const char* name, int32_t cookie) noexcept { + if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) { + endAsyncSection(this, name, cookie); + } + } + + inline void value(uint32_t tag, const char* name, int32_t value) noexcept { + if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) { + setCounter(this, name, value); + } + } + + inline void value(uint32_t tag, const char* name, int64_t value) noexcept { + if (tag && UTILS_UNLIKELY(mIsTracingEnabled)) { + setCounter(this, name, value); + } + } + +private: + friend class ScopedTrace; + + // whether tracing is supported at all by the platform + + using ATrace_isEnabled_t = bool (*)(void); + using ATrace_beginSection_t = void (*)(const char* sectionName); + using ATrace_endSection_t = void (*)(void); + using ATrace_beginAsyncSection_t = void (*)(const char* sectionName, int32_t cookie); + using ATrace_endAsyncSection_t = void (*)(const char* sectionName, int32_t cookie); + using ATrace_setCounter_t = void (*)(const char* counterName, int64_t counterValue); + + struct GlobalState { + bool isTracingAvailable; + std::atomic isTracingEnabled; + int markerFd; + + ATrace_isEnabled_t ATrace_isEnabled; + ATrace_beginSection_t ATrace_beginSection; + ATrace_endSection_t ATrace_endSection; + ATrace_beginAsyncSection_t ATrace_beginAsyncSection; + ATrace_endAsyncSection_t ATrace_endAsyncSection; + ATrace_setCounter_t ATrace_setCounter; + + void (*beginSection)(Systrace* that, const char* name); + void (*endSection)(Systrace* that); + void (*beginAsyncSection)(Systrace* that, const char* name, int32_t cookie); + void (*endAsyncSection)(Systrace* that, const char* name, int32_t cookie); + void (*setCounter)(Systrace* that, const char* name, int64_t value); + }; + + static GlobalState sGlobalState; + + + // per-instance versions for better performance + ATrace_isEnabled_t ATrace_isEnabled; + ATrace_beginSection_t ATrace_beginSection; + ATrace_endSection_t ATrace_endSection; + ATrace_beginAsyncSection_t ATrace_beginAsyncSection; + ATrace_endAsyncSection_t ATrace_endAsyncSection; + ATrace_setCounter_t ATrace_setCounter; + + void (*beginSection)(Systrace* that, const char* name); + void (*endSection)(Systrace* that); + void (*beginAsyncSection)(Systrace* that, const char* name, int32_t cookie); + void (*endAsyncSection)(Systrace* that, const char* name, int32_t cookie); + void (*setCounter)(Systrace* that, const char* name, int64_t value); + + void init(uint32_t tag) noexcept; + + // cached values for faster access, no need to be initialized + bool mIsTracingEnabled; + int mMarkerFd = -1; + pid_t mPid; + + static void setup() noexcept; + static void init_once() noexcept; + static bool isTracingEnabled(uint32_t tag) noexcept; + + static void begin_body(int fd, int pid, const char* name) noexcept; + static void end_body(int fd, int pid) noexcept; + static void async_begin_body(int fd, int pid, const char* name, int32_t cookie) noexcept; + static void async_end_body(int fd, int pid, const char* name, int32_t cookie) noexcept; + static void int64_body(int fd, int pid, const char* name, int64_t value) noexcept; +}; + +// ------------------------------------------------------------------------------------------------ + +class ScopedTrace { +public: + // we don't inline this because it's relatively heavy due to a global check + ScopedTrace(uint32_t tag, const char* name) noexcept : mTrace(tag), mTag(tag) { + mTrace.traceBegin(tag, name); + } + + inline ~ScopedTrace() noexcept { + mTrace.traceEnd(mTag); + } + + inline void value(uint32_t tag, const char* name, int32_t v) noexcept { + mTrace.value(tag, name, v); + } + + inline void value(uint32_t tag, const char* name, int64_t v) noexcept { + mTrace.value(tag, name, v); + } + +private: + Systrace mTrace; + const uint32_t mTag; +}; + +} // namespace details +} // namespace utils + +// ------------------------------------------------------------------------------------------------ +#else // !ANDROID +// ------------------------------------------------------------------------------------------------ + +#define SYSTRACE_ENABLE() +#define SYSTRACE_DISABLE() +#define SYSTRACE_CONTEXT() +#define SYSTRACE_NAME(name) +#define SYSTRACE_NAME_BEGIN(name) +#define SYSTRACE_NAME_END() +#define SYSTRACE_CALL() +#define SYSTRACE_ASYNC_BEGIN(name, cookie) +#define SYSTRACE_ASYNC_END(name, cookie) +#define SYSTRACE_VALUE32(name, val) +#define SYSTRACE_VALUE64(name, val) + +#endif // ANDROID + +#endif // TNT_UTILS_SYSTRACE_H diff --git a/ios/include/utils/WorkStealingDequeue.h b/ios/include/utils/WorkStealingDequeue.h new file mode 100644 index 00000000..073b9b64 --- /dev/null +++ b/ios/include/utils/WorkStealingDequeue.h @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_UTILS_WORKSTEALINGDEQUEUE_H +#define TNT_UTILS_WORKSTEALINGDEQUEUE_H + +#include + +#include +#include + +namespace utils { + +/* + * A templated, lockless, fixed-size work-stealing dequeue + * + * + * top bottom + * v v + * |----|----|----|----|----|----| + * steal() push(), pop() + * any thread main thread + * + * + */ +template +class WorkStealingDequeue { + static_assert(!(COUNT & (COUNT - 1)), "COUNT must be a power of two"); + static constexpr size_t MASK = COUNT - 1; + + // mTop and mBottom must be signed integers. We use 64-bits atomics so we don't have + // to worry about wrapping around. + using index_t = int64_t; + + std::atomic mTop = { 0 }; // written/read in pop()/steal() + std::atomic mBottom = { 0 }; // written only in pop(), read in push(), steal() + + TYPE mItems[COUNT]; + + // NOTE: it's not safe to return a reference because getItemAt() can be called + // concurrently and the caller could std::move() the item unsafely. + TYPE getItemAt(index_t index) noexcept { return mItems[index & MASK]; } + + void setItemAt(index_t index, TYPE item) noexcept { mItems[index & MASK] = item; } + +public: + using value_type = TYPE; + + inline void push(TYPE item) noexcept; + inline TYPE pop() noexcept; + inline TYPE steal() noexcept; + + size_t getSize() const noexcept { return COUNT; } + + // for debugging only... + size_t getCount() const noexcept { + index_t bottom = mBottom.load(std::memory_order_relaxed); + index_t top = mTop.load(std::memory_order_relaxed); + return bottom - top; + } +}; + +/* + * Adds an item at the BOTTOM of the queue. + * + * Must be called from the main thread. + */ +template +void WorkStealingDequeue::push(TYPE item) noexcept { + // std::memory_order_relaxed is sufficient because this load doesn't acquire anything from + // another thread. mBottom is only written in pop() which cannot be concurrent with push() + index_t bottom = mBottom.load(std::memory_order_relaxed); + setItemAt(bottom, item); + + // std::memory_order_release is used because we release the item we just pushed to other + // threads which are calling steal(). + mBottom.store(bottom + 1, std::memory_order_release); +} + +/* + * Removes an item from the BOTTOM of the queue. + * + * Must be called from the main thread. + */ +template +TYPE WorkStealingDequeue::pop() noexcept { + // std::memory_order_seq_cst is needed to guarantee ordering in steal() + // Note however that this is not a typical acquire/release operation: + // - not acquire because mBottom is only written in push() which is not concurrent + // - not release because we're not publishing anything to steal() here + // + // QUESTION: does this prevent mTop load below to be reordered before the "store" part of + // fetch_sub()? Hopefully it does. If not we'd need a full memory barrier. + // + index_t bottom = mBottom.fetch_sub(1, std::memory_order_seq_cst) - 1; + + // bottom could be -1 if we tried to pop() from an empty queue. This will be corrected below. + assert( bottom >= -1 ); + + // std::memory_order_seq_cst is needed to guarantee ordering in steal() + // Note however that this is not a typical acquire operation + // (i.e. other thread's writes of mTop don't publish data) + index_t top = mTop.load(std::memory_order_seq_cst); + + if (top < bottom) { + // Queue isn't empty and it's not the last item, just return it, this is the common case. + return getItemAt(bottom); + } + + TYPE item{}; + if (top == bottom) { + // we just took the last item + item = getItemAt(bottom); + + // Because we know we took the last item, we could be racing with steal() -- the last + // item being both at the top and bottom of the queue. + // We resolve this potential race by also stealing that item from ourselves. + if (mTop.compare_exchange_strong(top, top + 1, + std::memory_order_seq_cst, + std::memory_order_relaxed)) { + // success: we stole our last item from ourself, meaning that a concurrent steal() + // would have failed. + // mTop now equals top + 1, we adjust top to make the queue empty. + top++; + } else { + // failure: mTop was not equal to top, which means the item was stolen under our feet. + // top now equals to mTop. Simply discard the item we just popped. + // The queue is now empty. + item = TYPE(); + } + } else { + // We could be here if the item was stolen just before we read mTop, we'll adjust + // mBottom below. + assert(top - bottom == 1); + } + + // std::memory_order_relaxed used because we're not publishing any data. + // no concurrent writes to mBottom possible, it's always safe to write mBottom. + mBottom.store(top, std::memory_order_relaxed); + return item; +} + +/* + * Steals an item from the TOP of another thread's queue. + * + * This can be called concurrently with steal(), push() or pop() + * + * steal() never fails, either there is an item and it atomically takes it, or there isn't and + * it returns an empty item. + */ +template +TYPE WorkStealingDequeue::steal() noexcept { + while (true) { + /* + * Note: A Key component of this algorithm is that mTop is read before mBottom here + * (and observed as such in other threads) + */ + + // std::memory_order_seq_cst is needed to guarantee ordering in pop() + // Note however that this is not a typical acquire operation + // (i.e. other thread's writes of mTop don't publish data) + index_t top = mTop.load(std::memory_order_seq_cst); + + // std::memory_order_acquire is needed because we're acquiring items published in push(). + // std::memory_order_seq_cst is needed to guarantee ordering in pop() + index_t bottom = mBottom.load(std::memory_order_seq_cst); + + if (top >= bottom) { + // queue is empty + return TYPE(); + } + + // The queue isn't empty + TYPE item(getItemAt(top)); + if (mTop.compare_exchange_strong(top, top + 1, + std::memory_order_seq_cst, + std::memory_order_relaxed)) { + // success: we stole an item, just return it. + return item; + } + // failure: the item we just tried to steal was pop()'ed under our feet, + // simply discard it; nothing to do -- it's okay to try again. + } +} + + +} // namespace utils + +#endif // TNT_UTILS_WORKSTEALINGDEQUEUE_H diff --git a/ios/include/utils/Zip2Iterator.h b/ios/include/utils/Zip2Iterator.h new file mode 100644 index 00000000..344277e8 --- /dev/null +++ b/ios/include/utils/Zip2Iterator.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_UTILS_ZIP2ITERATOR_H +#define TNT_UTILS_ZIP2ITERATOR_H + +#include +#include + +#include + +namespace utils { + +/* + * A random access iterator that wraps two other random access iterators. + * This mostly exists so that one can sort an array using values from another. + */ + +template +class Zip2Iterator { + std::pair mIt; + using Ref1 = typename std::iterator_traits::reference; + using Ref2 = typename std::iterator_traits::reference; + using Val1 = typename std::iterator_traits::value_type; + using Val2 = typename std::iterator_traits::value_type; + +public: + struct Ref : public std::pair { + using std::pair::pair; + using std::pair::operator=; + private: + friend void swap(Ref lhs, Ref rhs) { + using std::swap; + swap(lhs.first, rhs.first); + swap(lhs.second, rhs.second); + } + }; + + using value_type = std::pair; + using reference = Ref; + using pointer = value_type*; + using difference_type = ptrdiff_t; + using iterator_category = std::random_access_iterator_tag; + + Zip2Iterator() = default; + Zip2Iterator(It1 first, It2 second) : mIt({first, second}) {} + Zip2Iterator(Zip2Iterator const& rhs) noexcept = default; + Zip2Iterator& operator=(Zip2Iterator const& rhs) = default; + + reference operator*() const { return { *mIt.first, *mIt.second }; } + + reference operator[](size_t n) const { return *(*this + n); } + + Zip2Iterator& operator++() { + ++mIt.first; + ++mIt.second; + return *this; + } + + Zip2Iterator& operator--() { + --mIt.first; + --mIt.second; + return *this; + } + + // Postfix operator needed by Microsoft C++ + const Zip2Iterator operator++(int) { + Zip2Iterator t(*this); + mIt.first++; + mIt.second++; + return t; + } + + const Zip2Iterator operator--(int) { + Zip2Iterator t(*this); + mIt.first--; + mIt.second--; + return t; + } + + Zip2Iterator& operator+=(size_t v) { + mIt.first += v; + mIt.second += v; + return *this; + } + + Zip2Iterator& operator-=(size_t v) { + mIt.first -= v; + mIt.second -= v; + return *this; + } + + Zip2Iterator operator+(size_t rhs) const { return { mIt.first + rhs, mIt.second + rhs }; } + Zip2Iterator operator+(size_t rhs) { return { mIt.first + rhs, mIt.second + rhs }; } + Zip2Iterator operator-(size_t rhs) const { return { mIt.first - rhs, mIt.second - rhs }; } + + difference_type operator-(Zip2Iterator const& rhs) const { return mIt.first - rhs.mIt.first; } + + bool operator==(Zip2Iterator const& rhs) const { return (mIt.first == rhs.mIt.first); } + bool operator!=(Zip2Iterator const& rhs) const { return (mIt.first != rhs.mIt.first); } + bool operator>=(Zip2Iterator const& rhs) const { return (mIt.first >= rhs.mIt.first); } + bool operator> (Zip2Iterator const& rhs) const { return (mIt.first > rhs.mIt.first); } + bool operator<=(Zip2Iterator const& rhs) const { return (mIt.first <= rhs.mIt.first); } + bool operator< (Zip2Iterator const& rhs) const { return (mIt.first < rhs.mIt.first); } +}; + +} // namespace utils + +#endif /* TNT_UTILS_ZIP2ITERATOR_H */ diff --git a/ios/include/utils/algorithm.h b/ios/include/utils/algorithm.h new file mode 100644 index 00000000..54c28fef --- /dev/null +++ b/ios/include/utils/algorithm.h @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_UTILS_ALGORITHM_H +#define TNT_UTILS_ALGORITHM_H + +#include + +#include // for std::less +#include // for std::enable_if + +#include +#include + +namespace utils { + +namespace details { + +template +constexpr inline T popcount(T v) noexcept { + static_assert(sizeof(T) * CHAR_BIT <= 128, "details::popcount() only support up to 128 bits"); + constexpr T ONES = ~T(0); + v = v - ((v >> 1u) & ONES / 3); + v = (v & ONES / 15 * 3) + ((v >> 2u) & ONES / 15 * 3); + v = (v + (v >> 4u)) & ONES / 255 * 15; + return (T) (v * (ONES / 255)) >> (sizeof(T) - 1) * CHAR_BIT; +} + +template::value>> +constexpr inline T clz(T x) noexcept { + static_assert(sizeof(T) * CHAR_BIT <= 128, "details::clz() only support up to 128 bits"); + x |= (x >> 1u); + x |= (x >> 2u); + x |= (x >> 4u); + x |= (x >> 8u); + x |= (x >> 16u); + if (sizeof(T) * CHAR_BIT >= 64) { // just to silence compiler warning + x |= (x >> 32u); + } + if (sizeof(T) * CHAR_BIT >= 128) { // just to silence compiler warning + x |= (x >> 64u); + } + return T(sizeof(T) * CHAR_BIT) - details::popcount(x); +} + +template::value>> +constexpr inline T ctz(T x) noexcept { + static_assert(sizeof(T) * CHAR_BIT <= 64, "details::ctz() only support up to 64 bits"); + T c = sizeof(T) * CHAR_BIT; + x &= -x; // equivalent to x & (~x + 1) + if (x) c--; + if (sizeof(T) * CHAR_BIT >= 64) { + if (x & T(0x00000000FFFFFFFF)) c -= 32; + } + if (x & T(0x0000FFFF0000FFFF)) c -= 16; + if (x & T(0x00FF00FF00FF00FF)) c -= 8; + if (x & T(0x0F0F0F0F0F0F0F0F)) c -= 4; + if (x & T(0x3333333333333333)) c -= 2; + if (x & T(0x5555555555555555)) c -= 1; + return c; +} + +} // namespace details + +constexpr inline UTILS_PUBLIC UTILS_PURE +unsigned int UTILS_ALWAYS_INLINE clz(unsigned int x) noexcept { +#if __has_builtin(__builtin_clz) + return __builtin_clz(x); +#else + return details::clz(x); +#endif +} + +constexpr inline UTILS_PUBLIC UTILS_PURE +unsigned long UTILS_ALWAYS_INLINE clz(unsigned long x) noexcept { +#if __has_builtin(__builtin_clzl) + return __builtin_clzl(x); +#else + return details::clz(x); +#endif +} + +constexpr inline UTILS_PUBLIC UTILS_PURE +unsigned long long UTILS_ALWAYS_INLINE clz(unsigned long long x) noexcept { +#if __has_builtin(__builtin_clzll) + return __builtin_clzll(x); +#else + return details::clz(x); +#endif +} + +constexpr inline UTILS_PUBLIC UTILS_PURE +unsigned int UTILS_ALWAYS_INLINE ctz(unsigned int x) noexcept { +#if __has_builtin(__builtin_ctz) + return __builtin_ctz(x); +#else + return details::ctz(x); +#endif +} + +constexpr inline UTILS_PUBLIC UTILS_PURE +unsigned long UTILS_ALWAYS_INLINE ctz(unsigned long x) noexcept { +#if __has_builtin(__builtin_ctzl) + return __builtin_ctzl(x); +#else + return details::ctz(x); +#endif +} + +constexpr inline UTILS_PUBLIC UTILS_PURE +unsigned long long UTILS_ALWAYS_INLINE ctz(unsigned long long x) noexcept { +#if __has_builtin(__builtin_ctzll) + return __builtin_ctzll(x); +#else + return details::ctz(x); +#endif +} + +constexpr inline UTILS_PUBLIC UTILS_PURE +unsigned int UTILS_ALWAYS_INLINE popcount(unsigned int x) noexcept { +#if __has_builtin(__builtin_popcount) + return __builtin_popcount(x); +#else + return details::popcount(x); +#endif +} + +constexpr inline UTILS_PUBLIC UTILS_PURE +unsigned long UTILS_ALWAYS_INLINE popcount(unsigned long x) noexcept { +#if __has_builtin(__builtin_popcountl) + return __builtin_popcountl(x); +#else + return details::popcount(x); +#endif +} + +constexpr inline UTILS_PUBLIC UTILS_PURE +unsigned long long UTILS_ALWAYS_INLINE popcount(unsigned long long x) noexcept { +#if __has_builtin(__builtin_popcountll) + return __builtin_popcountll(x); +#else + return details::popcount(x); +#endif +} + +constexpr inline UTILS_PUBLIC UTILS_PURE +uint8_t UTILS_ALWAYS_INLINE popcount(uint8_t x) noexcept { + return (uint8_t)popcount((unsigned int)x); +} + +template::value && std::is_unsigned::value>> +constexpr inline UTILS_PUBLIC UTILS_PURE +T log2i(T x) noexcept { + return (sizeof(x) * 8 - 1u) - clz(x); +} + +/* + * branch-less version of std::lower_bound and std::upper_bound. + * These versions are intended to be fully inlined, which only happens when the size + * of the array is known at compile time. This code also performs better if the + * array is a power-of-two in size. + * + * These code works even if the conditions above are not met, and becomes a less-branches + * algorithm instead of a branch-less one! + */ + +template> +inline UTILS_PUBLIC +RandomAccessIterator lower_bound( + RandomAccessIterator first, RandomAccessIterator last, const T& value, + COMPARE comp = std::less(), + bool assume_power_of_two = false) { + size_t len = last - first; + + if (!assume_power_of_two) { + // handle non power-of-two sized arrays. If it's POT, the next line is a no-op + // and gets optimized out if the size is known at compile time. + len = 1u << (31 - clz(uint32_t(len))); // next power of two length / 2 + size_t difference = (last - first) - len; + // If len was already a POT, then difference will be 0. + // We need to explicitly check this case to avoid dereferencing past the end of the array + first += !difference || comp(first[len], value) ? difference : 0; + } + + while (len) { + // The number of repetitions here doesn't affect the result. We manually unroll the loop + // twice, to guarantee we have at least two iterations without branches (for the case + // where the size is not known at compile time + first += comp(first[len >>= 1u], value) ? len : 0; + first += comp(first[len >>= 1u], value) ? len : 0; + } + first += comp(*first, value); + return first; +} + +template> +inline UTILS_PUBLIC +RandomAccessIterator upper_bound( + RandomAccessIterator first, RandomAccessIterator last, + const T& value, COMPARE comp = std::less(), + bool assume_power_of_two = false) { + size_t len = last - first; + + if (!assume_power_of_two) { + // handle non power-of-two sized arrays. If it's POT, the next line is a no-op + // and gets optimized out if the size is known at compile time. + len = 1u << (31 - clz(uint32_t(len))); // next power of two length / 2 + size_t difference = (last - first) - len; + // If len was already a POT, then difference will be 0. + // We need to explicitly check this case to avoid dereferencing past the end of the array + first += !difference || comp(value, first[len]) ? 0 : difference; + } + + while (len) { + // The number of repetitions here doesn't affect the result. We manually unroll the loop + // twice, to guarantee we have at least two iterations without branches (for the case + // where the size is not known at compile time + first += !comp(value, first[len >>= 1u]) ? len : 0; + first += !comp(value, first[len >>= 1u]) ? len : 0; + } + first += !comp(value, *first); + return first; +} + +template +inline UTILS_PUBLIC +RandomAccessIterator partition_point( + RandomAccessIterator first, RandomAccessIterator last, COMPARE pred, + bool assume_power_of_two = false) { + size_t len = last - first; + + if (!assume_power_of_two) { + // handle non power-of-two sized arrays. If it's POT, the next line is a no-op + // and gets optimized out if the size is known at compile time. + len = 1u << (31 - clz(uint32_t(len))); // next power of two length / 2 + size_t difference = (last - first) - len; + first += !difference || pred(first[len]) ? difference : 0; + } + + while (len) { + // The number of repetitions here doesn't affect the result. We manually unroll the loop + // twice, to guarantee we have at least two iterations without branches (for the case + // where the size is not known at compile time + first += pred(first[len>>=1u]) ? len : 0; + first += pred(first[len>>=1u]) ? len : 0; + } + first += pred(*first); + return first; +} + +template +typename std::enable_if_t< + (sizeof(To) == sizeof(From)) && + std::is_trivially_copyable::value, + To> +// constexpr support needs compiler magic +bit_cast(const From &src) noexcept { + return reinterpret_cast(src); +} + +} // namespace utils + +#endif // TNT_UTILS_ALGORITHM_H diff --git a/ios/include/utils/api_level.h b/ios/include/utils/api_level.h new file mode 100644 index 00000000..b8d3dc41 --- /dev/null +++ b/ios/include/utils/api_level.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_UTILS_API_H +#define TNT_UTILS_API_H + +#include + +namespace utils { + +/** + * Returns this platform's API level. On Android this function will return + * the API level as defined by the SDK API level version. If a platform does + * not have an API level, this function returns 0. + */ +UTILS_PUBLIC +int api_level(); + +} // namespace utils + +#endif // TNT_UTILS_ARCHITECTURE_H diff --git a/ios/include/utils/architecture.h b/ios/include/utils/architecture.h new file mode 100644 index 00000000..60b6ed70 --- /dev/null +++ b/ios/include/utils/architecture.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_UTILS_ARCHITECTURE_H +#define TNT_UTILS_ARCHITECTURE_H + +#include + +namespace utils { + +constexpr size_t CACHELINE_SIZE = 64; + +} // namespace utils + +#endif // TNT_UTILS_ARCHITECTURE_H diff --git a/ios/include/utils/ashmem.h b/ios/include/utils/ashmem.h new file mode 100644 index 00000000..bacd3bfc --- /dev/null +++ b/ios/include/utils/ashmem.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_UTILS_ASHMEM_H +#define TNT_UTILS_ASHMEM_H + +#include + +namespace utils { + +int ashmem_create_region(const char *name, size_t size); + +} // namespace utils + +#endif /* TNT_UTILS_ASHMEM_H */ diff --git a/ios/include/utils/bitset.h b/ios/include/utils/bitset.h new file mode 100644 index 00000000..b9cfe35c --- /dev/null +++ b/ios/include/utils/bitset.h @@ -0,0 +1,316 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_UTILS_BITSET_H +#define TNT_UTILS_BITSET_H + +#include +#include + +#include +#include +#include + +#include +#include + +#if defined(__ARM_NEON) +# if defined(__ARM_ACLE) && defined(__aarch64__) +# include +# define TNT_UTILS_BITSET_USE_NEON 1 +# endif +#endif + +namespace utils { + +/* + * This bitset<> class is different from std::bitset<> in that it allows us to control + * the exact storage size. This is useful for small bitset (e.g. < 64, on 64-bits machines). + * It also allows for lexicographical compares (i.e. sorting). + */ + +template::value && + std::is_unsigned::value>::type> +class UTILS_PUBLIC bitset { + T storage[N]; + +public: + static constexpr T BITS_PER_WORD = sizeof(T) * 8; + static constexpr T BIT_COUNT = BITS_PER_WORD * N; + static constexpr T WORLD_COUNT = N; + using container_type = T; + + bitset() noexcept { + std::fill(std::begin(storage), std::end(storage), 0); + } + + T getBitsAt(size_t n) const noexcept { + return storage[n]; + } + + T& getBitsAt(size_t n) noexcept { + return storage[n]; + } + + T getValue() const noexcept { + static_assert(N == 1, "bitfield must only have one storage word"); + return storage[0]; + } + + void setValue(T value) noexcept { + static_assert(N == 1, "bitfield must only have one storage word"); + storage[0] = value; + } + + template + void forEachSetBit(F exec) const noexcept { + for (size_t i = 0; i < N; i++) { + T v = storage[i]; + while (v) { + T k = utils::ctz(v); + v &= ~(T(1) << k); + exec(size_t(k + BITS_PER_WORD * i)); + } + } + } + + size_t size() const noexcept { return N * BITS_PER_WORD; } + + bool test(size_t bit) const noexcept { return operator[](bit); } + + void set(size_t b) noexcept { + storage[b / BITS_PER_WORD] |= T(1) << (b % BITS_PER_WORD); + } + + void set(size_t b, bool value) noexcept { + storage[b / BITS_PER_WORD] &= ~(T(1) << (b % BITS_PER_WORD)); + storage[b / BITS_PER_WORD] |= T(value) << (b % BITS_PER_WORD); + } + + void unset(size_t b) noexcept { + storage[b / BITS_PER_WORD] &= ~(T(1) << (b % BITS_PER_WORD)); + } + + void flip(size_t b) noexcept { + storage[b / BITS_PER_WORD] ^= T(1) << (b % BITS_PER_WORD); + } + + + void reset() noexcept { + std::fill(std::begin(storage), std::end(storage), 0); + } + + bool operator[](size_t b) const noexcept { + return bool(storage[b / BITS_PER_WORD] & (T(1) << (b % BITS_PER_WORD))); + } + + size_t count() const noexcept { +#if defined(TNT_UTILS_BITSET_USE_NEON) + if (BIT_COUNT % 128 == 0 && BIT_COUNT / 128 < 31) { + // Use NEON for bitset multiple of 128 bits. + // The intermediate computation can't handle more than 31*128 bits because + // intermediate counts must be 8 bits. + uint8x16_t const* const p = (uint8x16_t const*) storage; + uint8x16_t counts = vcntq_u8(p[0]); + for (size_t i = 1; i < BIT_COUNT / 128; ++i) { + counts += vcntq_u8(p[i]); + } + return vaddlvq_u8(counts); + } else +#endif + { + T r = utils::popcount(storage[0]); + for (size_t i = 1; i < N; ++i) { + r += utils::popcount(storage[i]); + } + return r; + } + } + + bool any() const noexcept { +#if defined(TNT_UTILS_BITSET_USE_NEON) + if (BIT_COUNT % 128 == 0) { + uint64x2_t const* const p = (uint64x2_t const*) storage; + uint64x2_t r = p[0]; + for (size_t i = 1; i < BIT_COUNT / 128; ++i) { + r |= p[i]; + } + return bool(r[0] | r[1]); + } else +#endif + { + T r = storage[0]; + for (size_t i = 1; i < N; ++i) { + r |= storage[i]; + } + return bool(r); + } + } + + bool none() const noexcept { + return !any(); + } + + bool all() const noexcept { +#if defined(TNT_UTILS_BITSET_USE_NEON) + if (BIT_COUNT % 128 == 0) { + uint64x2_t const* const p = (uint64x2_t const*) storage; + uint64x2_t r = p[0]; + for (size_t i = 1; i < BIT_COUNT / 128; ++i) { + r &= p[i]; + } + return T(~(r[0] & r[1])) == T(0); + } else +#endif + { + T r = storage[0]; + for (size_t i = 1; i < N; ++i) { + r &= storage[i]; + } + return T(~r) == T(0); + } + } + + bool operator!=(const bitset& b) const noexcept { +#if defined(TNT_UTILS_BITSET_USE_NEON) + if (BIT_COUNT % 128 == 0) { + bitset temp(*this ^ b); + uint64x2_t const* const p = (uint64x2_t const*) temp.storage; + uint64x2_t r = p[0]; + for (size_t i = 1; i < BIT_COUNT / 128; ++i) { + r |= p[i]; + } + return bool(r[0] | r[1]); + } else +#endif + { + T r = storage[0] ^ b.storage[0]; + for (size_t i = 1; i < N; ++i) { + r |= storage[i] ^ b.storage[i]; + } + return bool(r); + } + } + + bool operator==(const bitset& b) const noexcept { + return !operator!=(b); + } + + bitset& operator&=(const bitset& b) noexcept { +#if defined(TNT_UTILS_BITSET_USE_NEON) + if (BIT_COUNT % 128 == 0) { + uint8x16_t* const p = (uint8x16_t*) storage; + uint8x16_t const* const q = (uint8x16_t const*) b.storage; + for (size_t i = 0; i < BIT_COUNT / 128; ++i) { + p[i] &= q[i]; + } + } else +#endif + { + for (size_t i = 0; i < N; ++i) { + storage[i] &= b.storage[i]; + } + } + return *this; + } + + bitset& operator|=(const bitset& b) noexcept { +#if defined(TNT_UTILS_BITSET_USE_NEON) + if (BIT_COUNT % 128 == 0) { + uint8x16_t* const p = (uint8x16_t*) storage; + uint8x16_t const* const q = (uint8x16_t const*) b.storage; + for (size_t i = 0; i < BIT_COUNT / 128; ++i) { + p[i] |= q[i]; + } + } else +#endif + { + for (size_t i = 0; i < N; ++i) { + storage[i] |= b.storage[i]; + } + } + return *this; + } + + bitset& operator^=(const bitset& b) noexcept { +#if defined(TNT_UTILS_BITSET_USE_NEON) + if (BIT_COUNT % 128 == 0) { + uint8x16_t* const p = (uint8x16_t*) storage; + uint8x16_t const* const q = (uint8x16_t const*) b.storage; + for (size_t i = 0; i < BIT_COUNT / 128; ++i) { + p[i] ^= q[i]; + } + } else +#endif + { + for (size_t i = 0; i < N; ++i) { + storage[i] ^= b.storage[i]; + } + } + return *this; + } + + bitset operator~() const noexcept { + bitset r; +#if defined(TNT_UTILS_BITSET_USE_NEON) + if (BIT_COUNT % 128 == 0) { + uint8x16_t* const p = (uint8x16_t*) r.storage; + uint8x16_t const* const q = (uint8x16_t const*) storage; + for (size_t i = 0; i < BIT_COUNT / 128; ++i) { + p[i] = ~q[i]; + } + } else +#endif + { + for (size_t i = 0; i < N; ++i) { + r.storage[i] = ~storage[i]; + } + } + return r; + } + +private: + friend bool operator<(bitset const& lhs, bitset const& rhs) noexcept { + return std::lexicographical_compare( + std::begin(lhs.storage), std::end(lhs.storage), + std::begin(rhs.storage), std::end(rhs.storage) + ); + } + + friend bitset operator&(const bitset& lhs, const bitset& rhs) noexcept { + return bitset(lhs) &= rhs; + } + + friend bitset operator|(const bitset& lhs, const bitset& rhs) noexcept { + return bitset(lhs) |= rhs; + } + + friend bitset operator^(const bitset& lhs, const bitset& rhs) noexcept { + return bitset(lhs) ^= rhs; + } +}; + +using bitset8 = bitset; +using bitset32 = bitset; +using bitset256 = bitset; + +static_assert(sizeof(bitset8) == sizeof(uint8_t), "bitset8 isn't 8 bits!"); +static_assert(sizeof(bitset32) == sizeof(uint32_t), "bitset32 isn't 32 bits!"); + +} // namespace utils + +#endif // TNT_UTILS_BITSET_H diff --git a/ios/include/utils/compiler.h b/ios/include/utils/compiler.h new file mode 100644 index 00000000..f8e91922 --- /dev/null +++ b/ios/include/utils/compiler.h @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_UTILS_COMPILER_H +#define TNT_UTILS_COMPILER_H + +// compatibility with non-clang compilers... +#ifndef __has_attribute +#define __has_attribute(x) 0 +#endif + +#ifndef __has_feature +#define __has_feature(x) 0 +#endif + +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +#if __has_attribute(visibility) +# define UTILS_PUBLIC __attribute__((visibility("default"))) +#else +# define UTILS_PUBLIC +#endif + +#if __has_attribute(deprecated) +# define UTILS_DEPRECATED [[deprecated]] +#else +# define UTILS_DEPRECATED +#endif + +#if __has_attribute(packed) +# define UTILS_PACKED __attribute__((packed)) +#else +# define UTILS_PACKED +#endif + +#if __has_attribute(noreturn) +# define UTILS_NORETURN __attribute__((noreturn)) +#else +# define UTILS_NORETURN +#endif + +#if __has_attribute(visibility) +# ifndef TNT_DEV +# define UTILS_PRIVATE __attribute__((visibility("hidden"))) +# else +# define UTILS_PRIVATE +# endif +#else +# define UTILS_PRIVATE +#endif + +#define UTILS_NO_SANITIZE_THREAD +#if defined(__has_feature) +# if __has_feature(thread_sanitizer) +# undef UTILS_NO_SANITIZE_THREAD +# define UTILS_NO_SANITIZE_THREAD __attribute__((no_sanitize("thread"))) +# endif +#endif + +#define UTILS_HAS_SANITIZE_MEMORY 0 +#if defined(__has_feature) +# if __has_feature(memory_sanitizer) +# undef UTILS_HAS_SANITIZE_MEMORY +# define UTILS_HAS_SANITIZE_MEMORY 1 +# endif +#endif + +/* + * helps the compiler's optimizer predicting branches + */ +#if __has_builtin(__builtin_expect) +# ifdef __cplusplus +# define UTILS_LIKELY( exp ) (__builtin_expect( !!(exp), true )) +# define UTILS_UNLIKELY( exp ) (__builtin_expect( !!(exp), false )) +# else +# define UTILS_LIKELY( exp ) (__builtin_expect( !!(exp), 1 )) +# define UTILS_UNLIKELY( exp ) (__builtin_expect( !!(exp), 0 )) +# endif +#else +# define UTILS_LIKELY( exp ) (!!(exp)) +# define UTILS_UNLIKELY( exp ) (!!(exp)) +#endif + +#if __has_builtin(__builtin_prefetch) +# define UTILS_PREFETCH( exp ) (__builtin_prefetch(exp)) +#else +# define UTILS_PREFETCH( exp ) +#endif + +#if __has_builtin(__builtin_assume) +# define UTILS_ASSUME( exp ) (__builtin_assume(exp)) +#else +# define UTILS_ASSUME( exp ) +#endif + +#if (defined(__i386__) || defined(__x86_64__)) +# define UTILS_HAS_HYPER_THREADING 1 // on x86 we assume we have hyper-threading. +#else +# define UTILS_HAS_HYPER_THREADING 0 +#endif + +#if defined(__EMSCRIPTEN__) || defined(FILAMENT_SINGLE_THREADED) +# define UTILS_HAS_THREADING 0 +#else +# define UTILS_HAS_THREADING 1 +#endif + +#if __has_attribute(noinline) +#define UTILS_NOINLINE __attribute__((noinline)) +#else +#define UTILS_NOINLINE +#endif + +#if __has_attribute(always_inline) +#define UTILS_ALWAYS_INLINE __attribute__((always_inline)) +#else +#define UTILS_ALWAYS_INLINE +#endif + +#if __has_attribute(pure) +#define UTILS_PURE __attribute__((pure)) +#else +#define UTILS_PURE +#endif + +#if __has_attribute(maybe_unused) +#define UTILS_UNUSED [[maybe_unused]] +#define UTILS_UNUSED_IN_RELEASE [[maybe_unused]] +#elif __has_attribute(unused) +#define UTILS_UNUSED __attribute__((unused)) +#define UTILS_UNUSED_IN_RELEASE __attribute__((unused)) +#else +#define UTILS_UNUSED +#define UTILS_UNUSED_IN_RELEASE +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1900 +# define UTILS_RESTRICT __restrict +#elif (defined(__clang__) || defined(__GNUC__)) +# define UTILS_RESTRICT __restrict__ +#else +# define UTILS_RESTRICT +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1900 +# define UTILS_HAS_FEATURE_CXX_THREAD_LOCAL 1 +#elif __has_feature(cxx_thread_local) +# define UTILS_HAS_FEATURE_CXX_THREAD_LOCAL 1 +#else +# define UTILS_HAS_FEATURE_CXX_THREAD_LOCAL 0 +#endif + +#if __has_feature(cxx_rtti) || defined(_CPPRTTI) +# define UTILS_HAS_RTTI 1 +#else +# define UTILS_HAS_RTTI 0 +#endif + +#ifdef __ARM_ACLE +# include +# define UTILS_WAIT_FOR_INTERRUPT() __wfi() +# define UTILS_WAIT_FOR_EVENT() __wfe() +# define UTILS_BROADCAST_EVENT() __sev() +# define UTILS_SIGNAL_EVENT() __sevl() +# define UTILS_PAUSE() __yield() +# define UTILS_PREFETCHW(addr) __pldx(1, 0, 0, addr) +#else // !__ARM_ACLE +# if (defined(__i386__) || defined(__x86_64__)) +# define UTILS_X86_PAUSE {__asm__ __volatile__( "rep; nop" : : : "memory" );} +# define UTILS_WAIT_FOR_INTERRUPT() UTILS_X86_PAUSE +# define UTILS_WAIT_FOR_EVENT() UTILS_X86_PAUSE +# define UTILS_BROADCAST_EVENT() +# define UTILS_SIGNAL_EVENT() +# define UTILS_PAUSE() UTILS_X86_PAUSE +# define UTILS_PREFETCHW(addr) UTILS_PREFETCH(addr) +# else // !x86 +# define UTILS_WAIT_FOR_INTERRUPT() +# define UTILS_WAIT_FOR_EVENT() +# define UTILS_BROADCAST_EVENT() +# define UTILS_SIGNAL_EVENT() +# define UTILS_PAUSE() +# define UTILS_PREFETCHW(addr) UTILS_PREFETCH(addr) +# endif // x86 +#endif // __ARM_ACLE + + +// ssize_t is a POSIX type. +#if defined(WIN32) || defined(_WIN32) +#include +typedef SSIZE_T ssize_t; +#endif + +#ifdef _MSC_VER +# define UTILS_EMPTY_BASES __declspec(empty_bases) +#else +# define UTILS_EMPTY_BASES +#endif + +#if defined(WIN32) || defined(_WIN32) + #define IMPORTSYMB __declspec(dllimport) +#else + #define IMPORTSYMB +#endif + +#if defined(_MSC_VER) && !defined(__PRETTY_FUNCTION__) +# define __PRETTY_FUNCTION__ __FUNCSIG__ +#endif + + + +#endif // TNT_UTILS_COMPILER_H diff --git a/ios/include/utils/compressed_pair.h b/ios/include/utils/compressed_pair.h new file mode 100644 index 00000000..c3b9195b --- /dev/null +++ b/ios/include/utils/compressed_pair.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_UTILS_COMPRESSED_PAIR_H +#define TNT_UTILS_COMPRESSED_PAIR_H + +#include +#include + +namespace utils { + +template +struct dependent_type : public T { +}; + +template, bool> = true> +struct compressed_pair : private T1, private T2 { + + template, Dummy>::value && + dependent_type, Dummy>::value>> + compressed_pair() : T1(), T2() {} + + template + compressed_pair(U1&& other1, U2&& other2) + : T1(std::forward(other1)), + T2(std::forward(other2)) {} + + T1& first() noexcept { + return static_cast(*this); + } + + T2& second() noexcept { + return static_cast(*this); + } + + T1 const& first() const noexcept { + return static_cast(*this); + } + + T2 const& second() const noexcept { + return static_cast(*this); + } + + void swap(compressed_pair& other) noexcept { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } +}; + +} // namespace utils + +#endif //TNT_UTILS_COMPRESSED_PAIR_H diff --git a/ios/include/utils/debug.h b/ios/include/utils/debug.h new file mode 100644 index 00000000..6984f75e --- /dev/null +++ b/ios/include/utils/debug.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_UTILS_DEBUG_H +#define TNT_UTILS_DEBUG_H + +#include + +namespace utils { +void panic(const char *func, const char * file, int line, const char *assertion) noexcept; +} // namespace filament + +#ifdef NDEBUG +# define assert_invariant(e) ((void)0) +#else +# define assert_invariant(e) \ + (UTILS_LIKELY(e) ? ((void)0) : utils::panic(__func__, __FILE__, __LINE__, #e)) +#endif // NDEBUG + +#endif //TNT_UTILS_DEBUG_H diff --git a/ios/include/utils/generic/Condition.h b/ios/include/utils/generic/Condition.h new file mode 100644 index 00000000..8df002f9 --- /dev/null +++ b/ios/include/utils/generic/Condition.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef UTILS_GENERIC_CONDITION_H +#define UTILS_GENERIC_CONDITION_H + +#include + +namespace utils { + +class Condition : public std::condition_variable { +public: + using std::condition_variable::condition_variable; + + inline void notify_n(size_t n) noexcept { + if (n == 1) { + notify_one(); + } else if (n > 1) { + notify_all(); + } + } +}; + +} // namespace utils + +#endif // UTILS_GENERIC_CONDITION_H diff --git a/ios/include/utils/generic/Mutex.h b/ios/include/utils/generic/Mutex.h new file mode 100644 index 00000000..cbd90942 --- /dev/null +++ b/ios/include/utils/generic/Mutex.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef UTILS_GENERIC_MUTEX_H +#define UTILS_GENERIC_MUTEX_H + +#include + +namespace utils { + +using Mutex = std::mutex; + +} // namespace utils + +#endif // UTILS_GENERIC_MUTEX_H diff --git a/ios/include/utils/linux/Condition.h b/ios/include/utils/linux/Condition.h new file mode 100644 index 00000000..15f14143 --- /dev/null +++ b/ios/include/utils/linux/Condition.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef UTILS_LINUX_CONDITION_H +#define UTILS_LINUX_CONDITION_H + +#include +#include +#include // for cv_status +#include +#include // for unique_lock + +#include + +#include + +namespace utils { + +/* + * A very simple condition variable class that can be used as an (almost) drop-in replacement + * for std::condition_variable (doesn't have the timed wait() though). + * It is very low overhead as most of it is inlined. + */ + +class Condition { +public: + Condition() noexcept = default; + Condition(const Condition&) = delete; + Condition& operator=(const Condition&) = delete; + + void notify_all() noexcept { + pulse(std::numeric_limits::max()); + } + + void notify_one() noexcept { + pulse(1); + } + + void notify_n(size_t n) noexcept { + if (n > 0) pulse(n); + } + + void wait(std::unique_lock& lock) noexcept { + wait_until(lock.mutex(), false, nullptr); + } + + template + void wait(std::unique_lock& lock, P predicate) { + while (!predicate()) { + wait(lock); + } + } + + template + std::cv_status wait_until(std::unique_lock& lock, + const std::chrono::time_point& timeout_time) noexcept { + // convert to nanoseconds + uint64_t ns = std::chrono::duration(timeout_time.time_since_epoch()).count(); + using sec_t = decltype(timespec::tv_sec); + using nsec_t = decltype(timespec::tv_nsec); + timespec ts{ sec_t(ns / 1000000000), nsec_t(ns % 1000000000) }; + return wait_until(lock.mutex(), false, &ts); + } + + template + std::cv_status wait_until(std::unique_lock& lock, + const std::chrono::time_point& timeout_time) noexcept { + // convert to nanoseconds + uint64_t ns = std::chrono::duration(timeout_time.time_since_epoch()).count(); + using sec_t = decltype(timespec::tv_sec); + using nsec_t = decltype(timespec::tv_nsec); + timespec ts{ sec_t(ns / 1000000000), nsec_t(ns % 1000000000) }; + return wait_until(lock.mutex(), true, &ts); + } + + template + bool wait_until(std::unique_lock& lock, + const std::chrono::time_point& timeout_time, P predicate) noexcept { + while (!predicate()) { + if (wait_until(lock, timeout_time) == std::cv_status::timeout) { + return predicate(); + } + } + return true; + } + + template + std::cv_status wait_for(std::unique_lock& lock, + const std::chrono::duration& rel_time) noexcept { + return wait_until(lock, std::chrono::steady_clock::now() + rel_time); + } + + template + bool wait_for(std::unique_lock& lock, + const std::chrono::duration& rel_time, P pred) noexcept { + return wait_until(lock, std::chrono::steady_clock::now() + rel_time, std::move(pred)); + } + +private: + std::atomic mState = { 0 }; + + void pulse(int threadCount) noexcept; + + std::cv_status wait_until(Mutex* lock, + bool realtimeClock, timespec* ts) noexcept; +}; + +} // namespace utils + +#endif // UTILS_LINUX_CONDITION_H diff --git a/ios/include/utils/linux/Mutex.h b/ios/include/utils/linux/Mutex.h new file mode 100644 index 00000000..672a2592 --- /dev/null +++ b/ios/include/utils/linux/Mutex.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef UTILS_LINUX_MUTEX_H +#define UTILS_LINUX_MUTEX_H + +#include + +#include + +namespace utils { + +/* + * A very simple mutex class that can be used as an (almost) drop-in replacement + * for std::mutex. + * It is very low overhead as most of it is inlined. + */ + +class Mutex { +public: + constexpr Mutex() noexcept = default; + Mutex(const Mutex&) = delete; + Mutex& operator=(const Mutex&) = delete; + + void lock() noexcept { + uint32_t old_state = UNLOCKED; + if (UTILS_UNLIKELY(!mState.compare_exchange_strong(old_state, + LOCKED, std::memory_order_acquire, std::memory_order_relaxed))) { + wait(); + } + } + + void unlock() noexcept { + if (UTILS_UNLIKELY(mState.exchange(UNLOCKED, std::memory_order_release) == LOCKED_CONTENDED)) { + wake(); + } + } + +private: + enum { + UNLOCKED = 0, LOCKED = 1, LOCKED_CONTENDED = 2 + }; + std::atomic mState = { UNLOCKED }; + + void wait() noexcept; + void wake() noexcept; +}; + +} // namespace utils + +#endif // UTILS_LINUX_MUTEX_H diff --git a/ios/include/utils/memalign.h b/ios/include/utils/memalign.h new file mode 100644 index 00000000..6a05f019 --- /dev/null +++ b/ios/include/utils/memalign.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_UTILS_MEMALIGN_H +#define TNT_UTILS_MEMALIGN_H + +#include + +#include +#include +#include + +#if defined(WIN32) +#include +#endif + +namespace utils { + +inline void* aligned_alloc(size_t size, size_t align) noexcept { + assert(align && !(align & align - 1)); + + void* p = nullptr; + + // must be a power of two and >= sizeof(void*) + while (align < sizeof(void*)) { + align <<= 1u; + } + +#if defined(WIN32) + p = ::_aligned_malloc(size, align); +#else + ::posix_memalign(&p, align, size); +#endif + return p; +} + +inline void aligned_free(void* p) noexcept { +#if defined(WIN32) + ::_aligned_free(p); +#else + ::free(p); +#endif +} + +/* + * This allocator can be used with std::vector for instance to ensure all items are aligned + * to their alignof(). e.g. + * + * template + * using aligned_vector = std::vector>; + * + * aligned_vector foos; + * + */ +template +class STLAlignedAllocator { + static_assert(!(alignof(TYPE) & (alignof(TYPE) - 1)), "alignof(T) must be a power of two"); + +public: + using value_type = TYPE; + using pointer = TYPE*; + using const_pointer = const TYPE*; + using reference = TYPE&; + using const_reference = const TYPE&; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using propagate_on_container_move_assignment = std::true_type; + using is_always_equal = std::true_type; + + template + struct rebind { using other = STLAlignedAllocator; }; + + inline STLAlignedAllocator() noexcept = default; + + template + inline explicit STLAlignedAllocator(const STLAlignedAllocator&) noexcept {} + + inline ~STLAlignedAllocator() noexcept = default; + + inline pointer allocate(size_type n) noexcept { + return (pointer)aligned_alloc(n * sizeof(value_type), alignof(TYPE)); + } + + inline void deallocate(pointer p, size_type) { + aligned_free(p); + } + + // stateless allocators are always equal + template + bool operator==(const STLAlignedAllocator& rhs) const noexcept { + return true; + } + + template + bool operator!=(const STLAlignedAllocator& rhs) const noexcept { + return false; + } +}; + +} // namespace utils + +#endif // TNT_UTILS_MEMALIGN_H diff --git a/ios/include/utils/ostream.h b/ios/include/utils/ostream.h new file mode 100644 index 00000000..596af86d --- /dev/null +++ b/ios/include/utils/ostream.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_UTILS_OSTREAM_H +#define TNT_UTILS_OSTREAM_H + +#include + +#include +#include // ssize_t is a POSIX type. + +namespace utils { +namespace io { + +class UTILS_PUBLIC ostream { +public: + + virtual ~ostream(); + + ostream& operator<<(short value) noexcept; + ostream& operator<<(unsigned short value) noexcept; + + ostream& operator<<(char value) noexcept; + ostream& operator<<(unsigned char value) noexcept; + + ostream& operator<<(int value) noexcept; + ostream& operator<<(unsigned int value) noexcept; + + ostream& operator<<(long value) noexcept; + ostream& operator<<(unsigned long value) noexcept; + + ostream& operator<<(long long value) noexcept; + ostream& operator<<(unsigned long long value) noexcept; + + ostream& operator<<(float value) noexcept; + ostream& operator<<(double value) noexcept; + ostream& operator<<(long double value) noexcept; + + ostream& operator<<(bool value) noexcept; + + ostream& operator<<(const void* value) noexcept; + + ostream& operator<<(const char* string) noexcept; + ostream& operator<<(const unsigned char* string) noexcept; + + ostream& operator<<(ostream& (* f)(ostream&)) noexcept { return f(*this); } + + ostream& dec() noexcept; + ostream& hex() noexcept; + +protected: + class Buffer { + public: + Buffer() noexcept; + ~Buffer() noexcept; + + Buffer(const Buffer&) = delete; + Buffer& operator=(const Buffer&) = delete; + + char* buffer = nullptr; // buffer address + char* curr = nullptr; // current pointer + size_t size = 0; // size remaining + size_t capacity = 0; // total capacity of the buffer + const char* get() const noexcept { return buffer; } + void advance(ssize_t n) noexcept; + void reset() noexcept; + void reserve(size_t newSize) noexcept; + }; + + Buffer mData; + Buffer& getBuffer() noexcept { return mData; } + +private: + virtual ostream& flush() noexcept = 0; + + friend ostream& hex(ostream& s) noexcept; + friend ostream& dec(ostream& s) noexcept; + friend ostream& endl(ostream& s) noexcept; + friend ostream& flush(ostream& s) noexcept; + + enum type { + SHORT, USHORT, CHAR, UCHAR, INT, UINT, LONG, ULONG, LONG_LONG, ULONG_LONG, DOUBLE, + LONG_DOUBLE + }; + + bool mShowHex = false; + const char* getFormat(type t) const noexcept; + + /* + * Checks that the buffer has room for s additional bytes, growing the allocation if necessary. + */ + void growBufferIfNeeded(size_t s) noexcept; +}; + +// handles std::string +inline ostream& operator << (ostream& o, std::string const& s) noexcept { return o << s.c_str(); } + +// handles utils::bitset +inline ostream& operator << (ostream& o, utils::bitset32 const& s) noexcept { + return o << (void*)uintptr_t(s.getValue()); +} + +// handles vectors from libmath (but we do this generically, without needing a dependency on libmath) +template class VECTOR, typename T> +inline ostream& operator<<(ostream& stream, const VECTOR& v) { + stream << "< "; + for (size_t i = 0; i < v.size() - 1; i++) { + stream << v[i] << ", "; + } + stream << v[v.size() - 1] << " >"; + return stream; +} + +inline ostream& hex(ostream& s) noexcept { return s.hex(); } +inline ostream& dec(ostream& s) noexcept { return s.dec(); } +inline ostream& endl(ostream& s) noexcept { s << "\n"; return s.flush(); } +inline ostream& flush(ostream& s) noexcept { return s.flush(); } + +} // namespace io + +} // namespace utils + +#endif // TNT_UTILS_OSTREAM_H diff --git a/ios/include/utils/sstream.h b/ios/include/utils/sstream.h new file mode 100644 index 00000000..411a128a --- /dev/null +++ b/ios/include/utils/sstream.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TNT_SSTREAM_H +#define TNT_SSTREAM_H + +#include + +namespace utils { +namespace io { + +class sstream : public ostream { +public: + + ostream &flush() noexcept override; + + const char* c_str() const noexcept; + +}; + +} // namespace io +} // namespace utils + +#endif //TNT_SSTREAM_H diff --git a/ios/include/utils/trap.h b/ios/include/utils/trap.h new file mode 100644 index 00000000..f95933d7 --- /dev/null +++ b/ios/include/utils/trap.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef UTILS_TRAP_H +#define UTILS_TRAP_H + +#include + +#if defined(WIN32) +#include +#include +#endif + +namespace utils { + +// This can be used as a programmatic breakpoint. +inline void debug_trap() noexcept { +#if defined(WIN32) + DebugBreak(); +#else + std::raise(SIGINT); +#endif +} + +} // namespace utils + +#endif // UTILS_TRAP_H diff --git a/ios/include/utils/unwindows.h b/ios/include/utils/unwindows.h new file mode 100644 index 00000000..6bd6574d --- /dev/null +++ b/ios/include/utils/unwindows.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if defined (WIN32) + +#ifdef max +#undef max +#endif + +#ifdef min +#undef min +#endif + +#ifdef far +#undef far +#endif + +#ifdef near +#undef near +#endif + +#ifdef ERROR +#undef ERROR +#endif + +#ifdef OPAQUE +#undef OPAQUE +#endif + +#ifdef TRANSPARENT +#undef TRANSPARENT +#endif + +#ifdef PURE +#undef PURE +#endif + +#endif diff --git a/ios/include/utils/vector.h b/ios/include/utils/vector.h new file mode 100644 index 00000000..fa150da0 --- /dev/null +++ b/ios/include/utils/vector.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#ifndef UTILS_VECTOR_H +#define UTILS_VECTOR_H + +namespace utils { + +/** + * Inserts the specified item in the vector at its sorted position. + */ +template +static inline void insert_sorted(std::vector& v, T item) { + auto pos = std::lower_bound(v.begin(), v.end(), item); + v.insert(pos, std::move(item)); +} + +/** + * Inserts the specified item in the vector at its sorted position. + * The item type must implement the < operator. If the specified + * item is already present in the vector, this method returns without + * inserting the item again. + * + * @return True if the item was inserted at is sorted position, false + * if the item already exists in the vector. + */ +template +static inline bool insert_sorted_unique(std::vector& v, T item) { + if (UTILS_LIKELY(v.size() == 0 || v.back() < item)) { + v.push_back(item); + return true; + } + + auto pos = std::lower_bound(v.begin(), v.end(), item); + if (UTILS_LIKELY(pos == v.end() || item < *pos)) { + v.insert(pos, std::move(item)); + return true; + } + + return false; +} + +} // end utils namespace + +#endif //UTILS_VECTOR_H diff --git a/ios/include/utils/win32/stdtypes.h b/ios/include/utils/win32/stdtypes.h new file mode 100644 index 00000000..669f1cee --- /dev/null +++ b/ios/include/utils/win32/stdtypes.h @@ -0,0 +1,33 @@ +/* +* Copyright (C) 2018 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef TNT_UTILS_WIN32_STDTYPES_H +#define TNT_UTILS_WIN32_STDTYPES_H + +#if defined(WIN32) +#include + +// Copied from linux libc sys/stat.h: +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#define PATH_MAX (MAX_PATH) + +// For getcwd +#include +#define getcwd _getcwd + +#endif +#endif diff --git a/ios/include/viewer/AutomationEngine.h b/ios/include/viewer/AutomationEngine.h new file mode 100644 index 00000000..cddff04a --- /dev/null +++ b/ios/include/viewer/AutomationEngine.h @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VIEWER_AUTOMATION_ENGINE_H +#define VIEWER_AUTOMATION_ENGINE_H + +#include + +namespace filament { + +class ColorGrading; +class Engine; +class LightManager; +class MaterialInstance; +class Renderer; +class View; + +namespace viewer { + +/** + * The AutomationEngine makes it easy to push a bag of settings values to Filament. + * It can also be used to iterate through settings permutations for testing purposes. + * + * When creating an automation engine for testing purposes, clients give it an immutable reference + * to an AutomationSpec. It is always in one of two states: running or idle. The running state can + * be entered immediately (startRunning) or by requesting batch mode (startBatchMode). + * + * When executing a test, clients should call tick() after each frame is rendered, which gives + * automation an opportunity to push settings to Filament, increment the current test index (if + * enough time has elapsed), and request an asychronous screenshot. + * + * The time to sleep between tests is configurable and can be set to zero. Automation also waits a + * specified minimum number of frames between tests. + * + * Batch mode is meant for non-interactive applications. In batch mode, automation defers applying + * the first test case until the client unblocks it via signalBatchMode(). This is useful when + * waiting for a large model file to become fully loaded. Batch mode also offers a query + * (shouldClose) that is triggered after the last test has been invoked. + */ +class UTILS_PUBLIC AutomationEngine { +public: + /** + * Allows users to toggle screenshots, change the sleep duration between tests, etc. + */ + struct Options { + /** + * Minimum time that automation waits between applying a settings object and advancing + * to the next test case. Specified in seconds. + */ + float sleepDuration = 0.2; + + /** + * Similar to sleepDuration, but expressed as a frame count. Both the minimum sleep time + * and the minimum frame count must be elapsed before automation advances to the next test. + */ + int minFrameCount = 2; + + /** + * If true, test progress is dumped to the utils Log (info priority). + */ + bool verbose = true; + + /** + * If true, the tick function writes out a screenshot before advancing to the next test. + */ + bool exportScreenshots = false; + + /** + * If true, the tick function writes out a settings JSON file before advancing. + */ + bool exportSettings = false; + }; + + /** + * Collection of Filament objects that can be modified by the automation engine. + */ + struct ViewerContent { + View* view; + Renderer* renderer; + MaterialInstance* const* materials; + size_t materialCount; + LightManager* lightManager; + Scene* scene; + IndirectLight* indirectLight; + utils::Entity sunlight; + utils::Entity* assetLights; + size_t assetLightCount; + }; + + /** + * Creates an automation engine and places it in an idle state. + * + * @param spec Specifies a set of settings permutations (owned by the client). + * @param settings Client-owned settings object. This not only supplies the initial + * state, it also receives changes during tick(). This is useful when + * building automation into an application that has a settings UI. + * + * @see setOptions + * @see startRunning + */ + AutomationEngine(const AutomationSpec* spec, Settings* settings) : + mSpec(spec), mSettings(settings) {} + + /** + * Shortcut constructor that creates an automation engine from a JSON string. + * + * This constructor can be used if the user does not need to monitor how the settings + * change over time and does not need ownership over the AutomationSpec. + * + * An example of a JSON spec can be found by searching the repo for DEFAULT_AUTOMATION. + * This is documented using a JSON schema (look for viewer/schemas/automation.json). + * + * @param jsonSpec Valid JSON string that conforms to the automation schema. + * @param size Number of characters in the JSON string. + * @return Automation engine or null if unable to read the JSON. + */ + static AutomationEngine* createFromJSON(const char* jsonSpec, size_t size); + + /** + * Creates an automation engine for the sole purpose of pushing settings, or for executing + * the default test sequence. + * + * To see how the default test sequence is generated, search for DEFAULT_AUTOMATION. + */ + static AutomationEngine* createDefault(); + + /** + * Activates the automation test. During the subsequent call to tick(), the first test is + * applied and automation enters the running state. + */ + void startRunning(); + + /** + * Activates the automation test, but enters a paused state until the user calls + * signalBatchMode(). + */ + void startBatchMode(); + + /** + * Notifies the automation engine that time has passed, a new frame has been rendered. + * + * This is when settings get applied, screenshots are (optionally) exported, and the internal + * test counter is potentially incremented. + * + * @param content Contains the Filament View, Materials, and Renderer that get modified. + * @param deltaTime The amount of time that has passed since the previous tick in seconds. + */ + void tick(const ViewerContent& content, float deltaTime); + + /** + * Mutates a set of client-owned Filament objects according to a JSON string. + * + * This method is an alternative to tick(). It allows clients to use the automation engine as a + * remote control, as opposed to iterating through a predetermined test sequence. + * + * This updates the stashed Settings object, then pushes those settings to the given + * Filament objects. Clients can optionally call getColorGrading() after calling this method. + * + * @param json Contains the JSON string with a set of changes that need to be pushed. + * @param jsonLength Number of characters in the json string. + * @param content Contains a set of Filament objects that you want to mutate. + */ + void applySettings(const char* json, size_t jsonLength, const ViewerContent& content); + + /** + * Gets a color grading object that corresponds to the latest settings. + * + * This method either returns a cached instance, or it destroys the cached instance and creates + * a new one. + */ + ColorGrading* getColorGrading(Engine* engine); + + /** + * Gets the current viewer options. + * + * NOTE: Focal length here might be different from the user-specified value, due to DoF options. + */ + ViewerOptions getViewerOptions() const; + + /** + * Signals that batch mode can begin. Call this after all meshes and textures finish loading. + */ + void signalBatchMode() { mBatchModeAllowed = true; } + + /** + * Cancels an in-progress automation session. + */ + void stopRunning() { mIsRunning = false; } + + /** + * Signals that the application is closing, so all pending screenshots should be cancelled. + */ + void terminate(); + + /** + * Configures the automation engine for users who wish to set up a custom sleep time + * between tests, etc. + */ + void setOptions(Options options) { mOptions = options; } + + /** + * Returns true if automation is in batch mode and all tests have finished. + */ + bool shouldClose() const { return mShouldClose; } + + /** + * Convenience function that writes out a JSON file to disk containing all settings. + * + * @param Settings State vector to serialize. + * @param filename Desired JSON filename. + */ + static void exportSettings(const Settings& settings, const char* filename); + + Options getOptions() const { return mOptions; } + bool isRunning() const { return mIsRunning; } + size_t currentTest() const { return mCurrentTest; } + size_t testCount() const { return mSpec->size(); } + bool isBatchModeEnabled() const { return mBatchModeEnabled; } + const char* getStatusMessage() const; + ~AutomationEngine(); + +private: + AutomationSpec const * const mSpec; + Settings * const mSettings; + Options mOptions; + + Engine* mColorGradingEngine = nullptr; + ColorGrading* mColorGrading = nullptr; + ColorGradingSettings mColorGradingSettings = {}; + + size_t mCurrentTest; + float mElapsedTime; + int mElapsedFrames; + bool mIsRunning = false; + bool mBatchModeEnabled = false; + bool mRequestStart = false; + bool mShouldClose = false; + bool mBatchModeAllowed = false; + bool mTerminated = false; + bool mOwnsSettings = false; + +public: + // For internal use from a screenshot callback. + void requestClose() { mShouldClose = true; } + bool isTerminated() const { return mTerminated; } +}; + +} // namespace viewer +} // namespace filament + +#endif // VIEWER_AUTOMATION_ENGINE_H diff --git a/ios/include/viewer/AutomationSpec.h b/ios/include/viewer/AutomationSpec.h new file mode 100644 index 00000000..99d82f11 --- /dev/null +++ b/ios/include/viewer/AutomationSpec.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VIEWER_AUTOMATION_SPEC_H +#define VIEWER_AUTOMATION_SPEC_H + +#include + +#include + +namespace filament { +namespace viewer { + +/** + * Immutable list of Settings objects generated from a JSON spec. + * + * Each top-level item in the JSON spec is an object with "name", "base" and "permute". + * The "base" object specifies a single set of changes to apply to default settings. + * The optional "permute" object specifies a cross product of changes to apply to the base. + * + * The following example generates a total of 5 test cases. + * [{ + * "name": "simple", + * "base": { + * "view.dof.cocScale": 1.0, + * "view.bloom.strength": 0.5 + * }, + * "permute": { + * "view.bloom.enabled": [false, true], + * "view.dof.enabled": [false, true] + * } + * }, + * { + * "name": "ppoff", + * "base": { + * "view.postProcessingEnabled": false + * } + * }] + */ +class UTILS_PUBLIC AutomationSpec { +public: + + // Parses a JSON spec, then generates a list of Settings objects. + // Returns null on failure (see utils log for warnings and errors). + // Clients should release memory using "delete". + static AutomationSpec* generate(const char* jsonSpec, size_t size); + + // Generates a list of Settings objects using an embedded JSON spec. + static AutomationSpec* generateDefaultTestCases(); + + // Returns the number of generated Settings objects. + size_t size() const; + + // Gets a generated Settings object and copies it out. + // Returns false if the given index is out of bounds. + bool get(size_t index, Settings* out) const; + + // Returns the name of the JSON group for a given Settings object. + char const* getName(size_t index) const; + + // Frees all Settings objects and name strings. + ~AutomationSpec(); + +private: + struct Impl; + AutomationSpec(Impl*); + Impl* mImpl; +}; + +} // namespace viewer +} // namespace filament + +#endif // VIEWER_AUTOMATION_SPEC_H diff --git a/ios/include/viewer/RemoteServer.h b/ios/include/viewer/RemoteServer.h new file mode 100644 index 00000000..aba761c1 --- /dev/null +++ b/ios/include/viewer/RemoteServer.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VIEWER_REMOTE_SERVER_H +#define VIEWER_REMOTE_SERVER_H + +#include + +#include + +#include +#include + +class CivetServer; + +namespace filament { +namespace viewer { + +class MessageSender; +class MessageReceiver; + +/** + * Encapsulates a message sent from the web client. + * + * All instances of ReceivedMessage and their data / strings are owned by RemoteServer. + * These can be freed via RemoteServer::releaseReceivedMessage(). + */ +struct ReceivedMessage { + char* label; + char* buffer; + size_t bufferByteCount; + size_t messageUid; +}; + +/** + * Manages a tiny WebSocket server that can receive model data and viewer settings. + * + * Client apps can call peekReceivedMessage to check for new data, or acquireReceivedMessage + * to pop it off the small internal queue. When they are done examining the message contents + * they should call releaseReceivedMessage. + */ +class UTILS_PUBLIC RemoteServer { +public: + RemoteServer(int port = 8082); + ~RemoteServer(); + bool isValid() const { return mMessageSender; } + + /** + * Checks if a download is currently in progress and returns its label. + * Returns null if nothing is being downloaded. + */ + char const* peekIncomingLabel() const; + + /** + * Pops a message off the incoming queue or returns null if there are no unread messages. + * + * After examining its contents, users should free the message with releaseReceivedMessage. + */ + ReceivedMessage const* acquireReceivedMessage(); + + /** + * Frees the memory that holds the contents of a received message. + */ + void releaseReceivedMessage(ReceivedMessage const* message); + + void sendMessage(const Settings& settings); + void sendMessage(const char* label, const char* buffer, size_t bufsize); + + // For internal use (makes JNI simpler) + ReceivedMessage const* peekReceivedMessage() const; + +private: + void enqueueReceivedMessage(ReceivedMessage* message); + void setIncomingMessage(ReceivedMessage* message); + MessageSender* mMessageSender = nullptr; + MessageReceiver* mMessageReceiver = nullptr; + size_t mNextMessageUid = 0; + static const size_t kMessageCapacity = 4; + ReceivedMessage* mReceivedMessages[kMessageCapacity] = {}; + ReceivedMessage* mIncomingMessage = nullptr; + JsonSerializer mSerializer; + mutable std::mutex mReceivedMessagesMutex; + friend class MessageReceiver; +}; + +} // namespace viewer +} // namespace filament + +#endif // VIEWER_REMOTE_SERVER_H diff --git a/ios/include/viewer/Settings.h b/ios/include/viewer/Settings.h new file mode 100644 index 00000000..77fd9853 --- /dev/null +++ b/ios/include/viewer/Settings.h @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VIEWER_SETTINGS_H +#define VIEWER_SETTINGS_H + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include + +namespace filament { + +class Skybox; +class Renderer; + +namespace viewer { + +struct ColorGradingSettings; +struct DynamicLightingSettings; +struct MaterialSettings; +struct Settings; +struct ViewSettings; +struct LightSettings; +struct ViewerOptions; + +enum class ToneMapping : uint8_t { + LINEAR = 0, + ACES_LEGACY = 1, + ACES = 2, + FILMIC = 3, + GENERIC = 4, + DISPLAY_RANGE = 5, +}; + +using AmbientOcclusionOptions = filament::View::AmbientOcclusionOptions; +using AntiAliasing = filament::View::AntiAliasing; +using BloomOptions = filament::View::BloomOptions; +using DepthOfFieldOptions = filament::View::DepthOfFieldOptions; +using Dithering = filament::View::Dithering; +using FogOptions = filament::View::FogOptions; +using RenderQuality = filament::View::RenderQuality; +using ShadowType = filament::View::ShadowType; +using TemporalAntiAliasingOptions = filament::View::TemporalAntiAliasingOptions; +using VignetteOptions = filament::View::VignetteOptions; +using VsmShadowOptions = filament::View::VsmShadowOptions; +using LightManager = filament::LightManager; + +// These functions push all editable property values to their respective Filament objects. +void applySettings(const ViewSettings& settings, View* dest); +void applySettings(const MaterialSettings& settings, MaterialInstance* dest); +void applySettings(const LightSettings& settings, IndirectLight* ibl, utils::Entity sunlight, + utils::Entity* sceneLights, size_t sceneLightCount, LightManager* lm, Scene* scene); +void applySettings(const ViewerOptions& settings, Camera* camera, Skybox* skybox, + Renderer* renderer); + +// Creates a new ColorGrading object based on the given settings. +UTILS_PUBLIC +ColorGrading* createColorGrading(const ColorGradingSettings& settings, Engine* engine); + +class UTILS_PUBLIC JsonSerializer { +public: + JsonSerializer(); + ~JsonSerializer(); + + // Writes a human-readable JSON string into an internal buffer and returns the result. + const std::string& writeJson(const Settings& in); + + // Reads the given JSON blob and updates the corresponding fields in the given Settings object. + // - The given JSON blob need not specify all settings. + // - Returns true if successful. + // - This function writes warnings and error messages into the utils log. + bool readJson(const char* jsonChunk, size_t size, Settings* out); + +private: + class Context; + Context* context; +}; + +struct GenericToneMapperSettings { + float contrast = 1.4f; + float shoulder = 0.5f; + float midGrayIn = 0.18f; + float midGrayOut = 0.266f; + float hdrMax = 10.0f; + bool operator!=(const GenericToneMapperSettings &rhs) const { return !(rhs == *this); } + bool operator==(const GenericToneMapperSettings &rhs) const; +}; + +struct ColorGradingSettings { + bool enabled = true; + filament::ColorGrading::QualityLevel quality = filament::ColorGrading::QualityLevel::MEDIUM; + ToneMapping toneMapping = ToneMapping::ACES_LEGACY; + GenericToneMapperSettings genericToneMapper; + bool luminanceScaling = false; + float exposure = 0.0f; + float temperature = 0.0f; + float tint = 0.0f; + math::float3 outRed{1.0f, 0.0f, 0.0f}; + math::float3 outGreen{0.0f, 1.0f, 0.0f}; + math::float3 outBlue{0.0f, 0.0f, 1.0f}; + math::float4 shadows{1.0f, 1.0f, 1.0f, 0.0f}; + math::float4 midtones{1.0f, 1.0f, 1.0f, 0.0f}; + math::float4 highlights{1.0f, 1.0f, 1.0f, 0.0f}; + math::float4 ranges{0.0f, 0.333f, 0.550f, 1.0f}; + float contrast = 1.0f; + float vibrance = 1.0f; + float saturation = 1.0f; + math::float3 slope{1.0f}; + math::float3 offset{0.0f}; + math::float3 power{1.0f}; + math::float3 gamma{1.0f}; + math::float3 midPoint{1.0f}; + math::float3 scale{1.0f}; + bool linkedCurves = false; + bool operator!=(const ColorGradingSettings &rhs) const { return !(rhs == *this); } + bool operator==(const ColorGradingSettings &rhs) const; +}; + +struct DynamicLightingSettings { + float zLightNear = 5; + float zLightFar = 100; +}; + +// This defines fields in the same order as the setter methods in filament::View. +struct ViewSettings { + uint8_t sampleCount = 1; + AntiAliasing antiAliasing = AntiAliasing::FXAA; + TemporalAntiAliasingOptions taa; + ColorGradingSettings colorGrading; + AmbientOcclusionOptions ssao; + BloomOptions bloom; + FogOptions fog; + DepthOfFieldOptions dof; + VignetteOptions vignette; + Dithering dithering = Dithering::TEMPORAL; + RenderQuality renderQuality; + DynamicLightingSettings dynamicLighting; + ShadowType shadowType = ShadowType::PCF; + VsmShadowOptions vsmShadowOptions; + bool postProcessingEnabled = true; +}; + +template +struct MaterialProperty { std::string name; T value; }; + +// This struct has a fixed size for simplicity. Each non-empty property name is an override. +struct MaterialSettings { + static constexpr size_t MAX_COUNT = 4; + MaterialProperty scalar[MAX_COUNT]; + MaterialProperty float3[MAX_COUNT]; + MaterialProperty float4[MAX_COUNT]; +}; + +struct LightSettings { + bool enableShadows = true; + bool enableSunlight = true; + LightManager::ShadowOptions shadowOptions; + float sunlightIntensity = 100000.0f; + math::float3 sunlightDirection = {0.6, -1.0, -0.8};; + math::float3 sunlightColor = filament::Color::toLinear({ 0.98, 0.92, 0.89}); + float iblIntensity = 30000.0f; + float iblRotation = 0.0f; +}; + +struct ViewerOptions { + float cameraAperture = 16.0f; + float cameraSpeed = 125.0f; + float cameraISO = 100.0f; + float groundShadowStrength = 0.75f; + bool groundPlaneEnabled = false; + bool skyboxEnabled = true; + sRGBColor backgroundColor = { 0.0f }; + float cameraFocalLength = 28.0f; + float cameraFocusDistance = 10.0f; + bool autoScaleEnabled = true; +}; + +struct Settings { + ViewSettings view; + MaterialSettings material; + LightSettings lighting; + ViewerOptions viewer; +}; + +} // namespace viewer +} // namespace filament + +#endif // VIEWER_SETTINGS_H diff --git a/ios/include/viewer/SimpleViewer.h b/ios/include/viewer/SimpleViewer.h new file mode 100644 index 00000000..61caa14f --- /dev/null +++ b/ios/include/viewer/SimpleViewer.h @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VIEWER_SIMPLEVIEWER_H +#define VIEWER_SIMPLEVIEWER_H + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include +#include + +namespace filagui { + class ImGuiHelper; +} + +namespace filament { +namespace viewer { + +/** + * \class SimpleViewer SimpleViewer.h viewer/SimpleViewer.h + * \brief Manages the state for a simple glTF viewer with imgui controls and a tree view. + * + * This is a utility that can be used across multiple platforms, including web. + * + * \note If you don't need ImGui controls, there is no need to use this class, just use AssetLoader + * instead. + */ +class UTILS_PUBLIC SimpleViewer { +public: + using Animator = gltfio::Animator; + using FilamentAsset = gltfio::FilamentAsset; + using FilamentInstance = gltfio::FilamentInstance; + + static constexpr int DEFAULT_SIDEBAR_WIDTH = 350; + + /** + * Constructs a SimpleViewer that has a fixed association with the given Filament objects. + * + * Upon construction, the simple viewer may create some additional Filament objects (such as + * light sources) that it owns. + */ + SimpleViewer(filament::Engine* engine, filament::Scene* scene, filament::View* view, + int sidebarWidth = DEFAULT_SIDEBAR_WIDTH); + + /** + * Destroys the SimpleViewer and any Filament entities that it owns. + */ + ~SimpleViewer(); + + /** + * Adds the asset's ready-to-render entities into the scene. + * + * The viewer does not claim ownership over the asset or its entities. Clients should use + * AssetLoader and ResourceLoader to load an asset before passing it in. + * + * @param asset The asset to view. + * @param instanceToAnimate Optional instance from which to get the animator. + */ + void populateScene(FilamentAsset* asset, FilamentInstance* instanceToAnimate = nullptr); + + /** + * Removes the current asset from the viewer. + * + * This removes all the asset entities from the Scene, but does not destroy them. + */ + void removeAsset(); + + /** + * Sets or changes the current scene's IBL to allow the UI manipulate it. + */ + void setIndirectLight(filament::IndirectLight* ibl, filament::math::float3 const* sh3); + + /** + * Applies the currently-selected glTF animation to the transformation hierarchy and updates + * the bone matrices on all renderables. + */ + void applyAnimation(double currentTime); + + /** + * Constructs ImGui controls for the current frame and responds to everything that the user has + * changed since the previous frame. + * + * If desired this can be used in conjunction with the filagui library, which allows clients to + * render ImGui controls with Filament. + */ + void updateUserInterface(); + + /** + * Alternative to updateUserInterface that uses an internal instance of ImGuiHelper. + * + * This utility method is designed for clients that do not want to manage their own instance of + * ImGuiHelper (e.g., JavaScript clients). + * + * Behind the scenes this simply calls ImGuiHelper->render() and passes updateUserInterface into + * its callback. Note that the first call might be slower since it requires the creation of the + * internal ImGuiHelper instance. + */ + void renderUserInterface(float timeStepInSeconds, filament::View* guiView, float pixelRatio); + + /** + * Event-passing methods, useful only when SimpleViewer manages its own instance of ImGuiHelper. + * The key codes used in these methods are just normal ASCII/ANSI codes. + * @{ + */ + void mouseEvent(float mouseX, float mouseY, bool mouseButton, float mouseWheelY, bool control); + void keyDownEvent(int keyCode); + void keyUpEvent(int keyCode); + void keyPressEvent(int charCode); + /** @}*/ + + /** + * Retrieves the current width of the ImGui "window" which we are using as a sidebar. + * Clients can monitor this value to adjust the size of the view. + */ + int getSidebarWidth() const { return mSidebarWidth; } + + /** + * Allows clients to inject custom UI. + */ + void setUiCallback(std::function callback) { mCustomUI = callback; } + + /** + * Draws the bounding box of each renderable. + * Defaults to false. + */ + void enableWireframe(bool b) { mEnableWireframe = b; } + + /** + * Enables a built-in light source (useful for creating shadows). + * Defaults to true. + */ + void enableSunlight(bool b) { mSettings.lighting.enableSunlight = b; } + + /** + * Enables dithering on the view. + * Defaults to true. + */ + void enableDithering(bool b) { + mSettings.view.dithering = b ? Dithering::TEMPORAL : Dithering::NONE; + } + + /** + * Enables FXAA antialiasing in the post-process pipeline. + * Defaults to true. + */ + void enableFxaa(bool b) { + mSettings.view.antiAliasing = b ? AntiAliasing::FXAA : AntiAliasing::NONE; + } + + /** + * Enables hardware-based MSAA antialiasing. + * Defaults to true. + */ + void enableMsaa(bool b) { mSettings.view.sampleCount = b ? 4 : 1; } + + /** + * Enables screen-space ambient occlusion in the post-process pipeline. + * Defaults to true. + */ + void enableSSAO(bool b) { mSettings.view.ssao.enabled = b; } + + /** + * Enables Bloom. + * Defaults to true. + */ + void enableBloom(bool bloom) { mSettings.view.bloom.enabled = bloom; } + + /** + * Adjusts the intensity of the IBL. + * See also filament::IndirectLight::setIntensity(). + * Defaults to 30000.0. + */ + void setIBLIntensity(float brightness) { mSettings.lighting.iblIntensity = brightness; } + + /** + * Updates the transform at the root node according to the autoScaleEnabled setting. + */ + void updateRootTransform(); + + /** + * Gets a modifiable reference to stashed state. + */ + Settings& getSettings() { return mSettings; } + + void stopAnimation() { mCurrentAnimation = 0; } + + int getCurrentCamera() const { return mCurrentCamera; } + +private: + void updateIndirectLight(); + + // Immutable properties set from the constructor. + filament::Engine* const mEngine; + filament::Scene* const mScene; + filament::View* const mView; + const utils::Entity mSunlight; + + // Lazily instantiated fields. + filagui::ImGuiHelper* mImGuiHelper = nullptr; + + // Properties that can be changed from the application. + FilamentAsset* mAsset = nullptr; + Animator* mAnimator = nullptr; + filament::IndirectLight* mIndirectLight = nullptr; + std::function mCustomUI; + + // Properties that can be changed from the UI. + int mCurrentAnimation = 1; + bool mResetAnimation = true; + bool mEnableWireframe = false; + int mVsmMsaaSamplesLog2 = 1; + Settings mSettings; + int mSidebarWidth; + uint32_t mFlags; + + // 0 is the default "free camera". Additional cameras come from the gltf file (1-based index). + int mCurrentCamera = 0; + + // Color grading UI state. + float mToneMapPlot[1024]; + float mRangePlot[1024 * 3]; + float mCurvePlot[1024 * 3]; +}; + +UTILS_PUBLIC +filament::math::mat4f fitIntoUnitCube(const filament::Aabb& bounds, float zoffset); + +} // namespace viewer +} // namespace filament + +#endif // VIEWER_SIMPLEVIEWER_H diff --git a/ios/mimetic_filament.podspec b/ios/mimetic_filament.podspec new file mode 100644 index 00000000..cf2947e0 --- /dev/null +++ b/ios/mimetic_filament.podspec @@ -0,0 +1,30 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint mimetic_filament.podspec` to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'mimetic_filament' + s.version = '0.0.1' + s.summary = 'A new flutter plugin project.' + s.description = <<-DESC +A new flutter plugin project. + DESC + s.homepage = 'http://example.com' + s.license = { :file => '../LICENSE' } + s.author = { 'Your Company' => 'email@example.com' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*', 'src/*.*' + s.dependency 'Filament', '~> 1.12.3' + s.dependency 'Flutter' + s.platform = :ios, '12.1' + s.static_framework = true + + # Flutter.framework does not contain a i386 slice. + s.pod_target_xcconfig = { + 'DEFINES_MODULE' => 'YES', + 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386', + 'ALWAYS_SEARCH_USER_PATHS' => 'YES', + 'USER_HEADER_SEARCH_PATHS' => '"${PODS_ROOT}/../.symlinks/plugins/mimetic_filament/ios/include"', + 'OTHER_CFLAGS' => '-fmodules -fcxx-modules' } + s.swift_version = '5.0' +end diff --git a/ios/src/FilamentViewer.cpp b/ios/src/FilamentViewer.cpp new file mode 100644 index 00000000..40f6d90a --- /dev/null +++ b/ios/src/FilamentViewer.cpp @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + + +#include "FilamentViewer.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#include + +using namespace filament; +using namespace filament::math; +using namespace gltfio; +using namespace utils; + +namespace filament { + class IndirectLight; + class LightManager; +} + +namespace mimetic { + +const double kNearPlane = 0.05; // 5 cm +const double kFarPlane = 1000.0; // 1 km +const float kScaleMultiplier = 100.0f; +const float kAperture = 16.0f; +const float kShutterSpeed = 1.0f / 125.0f; +const float kSensitivity = 100.0f; + +MaterialProvider* createGPUShaderLoader(Engine* engine); + +FilamentViewer::FilamentViewer( + void* layer, + LoadResource loadResource, + FreeResource freeResource) : _layer(layer), + _loadResource(loadResource), + _freeResource(freeResource) { + _engine = Engine::create(Engine::Backend::OPENGL); + + _renderer = _engine->createRenderer(); + _scene = _engine->createScene(); + Entity camera = EntityManager::get().create(); + _mainCamera = _engine->createCamera(camera); + _view = _engine->createView(); + _view->setScene(_scene); + _view->setCamera(_mainCamera); + + _cameraFocalLength = 28.0f; + _mainCamera->setExposure(kAperture, kShutterSpeed, kSensitivity); + + _swapChain = _engine->createSwapChain(_layer); + +// _materialProvider = createGPUShaderLoader(_engine); + _materialProvider = createUbershaderLoader(_engine); + EntityManager& em = EntityManager::get(); + _ncm = new NameComponentManager(em); + _assetLoader = AssetLoader::create({_engine, _materialProvider, _ncm, &em}); + _resourceLoader = new ResourceLoader( + {.engine = _engine, .normalizeSkinningWeights = true, .recomputeBoundingBoxes = false}); + + _manipulator = + Manipulator::Builder().orbitHomePosition(0.0f, 0.0f, 0.0f).targetPosition(0.0f, 0.0f, 0).build(Mode::ORBIT); + //Manipulator::Builder().orbitHomePosition(0.0f, 0.0f, 0.0f).targetPosition(0.0f, 0.0f, 0).build(Mode::ORBIT); + _asset = nullptr; +} + +FilamentViewer::~FilamentViewer() { + +} + +void FilamentViewer::loadResources() { + const char* const* const resourceUris = _asset->getResourceUris(); + const size_t resourceUriCount = _asset->getResourceUriCount(); + for (size_t i = 0; i < resourceUriCount; i++) { + const char* const uri = resourceUris[i]; + ResourceBuffer buf = _loadResource(uri); + ResourceLoader::BufferDescriptor b( + buf.data, buf.size, (ResourceLoader::BufferDescriptor::Callback)&_freeResource, nullptr); + _resourceLoader->addResourceData(uri, std::move(b)); + } + + _resourceLoader->loadResources(_asset); + const Entity* entities = _asset->getEntities(); + RenderableManager& rm = _engine->getRenderableManager(); + for(int i =0; i< _asset->getEntityCount(); i++) { + Entity e = entities[i]; + auto inst = rm.getInstance(e); + rm.setCulling(inst, false); + } + + _animator = _asset->getAnimator(); + _asset->releaseSourceData(); + + _scene->addEntities(_asset->getEntities(), _asset->getEntityCount()); +}; + +void FilamentViewer::loadGltf(const char* const uri) { + _resourceLoader->asyncCancelLoad(); + _resourceLoader->evictResourceData(); + if(_asset) { + _assetLoader->destroyAsset(_asset); + } + + ResourceBuffer rbuf = _loadResource(uri); + + // Parse the glTF file and create Filament entities. + _asset = _assetLoader->createAssetFromJson((uint8_t*)rbuf.data, rbuf.size); + + if (!_asset) { + std::cerr << "Unable to parse asset" << std::endl; + exit(1); + } + + loadResources(); +} + + +void FilamentViewer::loadSkybox(const char* const skyboxPath, const char* const iblPath) { + ResourceBuffer skyboxBuffer = _loadResource(skyboxPath); + + image::KtxBundle* skyboxBundle = + new image::KtxBundle(static_cast(skyboxBuffer.data), + static_cast(skyboxBuffer.size)); + _skyboxTexture = image::ktx::createTexture(_engine, skyboxBundle, false); + _skybox = filament::Skybox::Builder().environment(_skyboxTexture).build(*_engine); + _scene->setSkybox(_skybox); + + // Load IBL. + ResourceBuffer iblBuffer = _loadResource(iblPath); + + image::KtxBundle* iblBundle = new image::KtxBundle( + static_cast(iblBuffer.data), static_cast(iblBuffer.size)); + math::float3 harmonics[9]; + iblBundle->getSphericalHarmonics(harmonics); + _iblTexture = image::ktx::createTexture(_engine, iblBundle, false); + _indirectLight = IndirectLight::Builder() + .reflections(_iblTexture) + .irradiance(3, harmonics) + .intensity(30000.0f) + .build(*_engine); + _scene->setIndirectLight(_indirectLight); + + // Always add a direct light source since it is required for shadowing. + _sun = EntityManager::get().create(); + LightManager::Builder(LightManager::Type::DIRECTIONAL) + .color(Color::cct(6500.0f)) + .intensity(100000.0f) + .direction(math::float3(0.0f, -1.0f, 0.0f)) + .castShadows(true) + .build(*_engine, _sun); + _scene->addEntity(_sun); +} + +void FilamentViewer::cleanup() { + _resourceLoader->asyncCancelLoad(); + _assetLoader->destroyAsset(_asset); + _materialProvider->destroyMaterials(); + AssetLoader::destroy(&_assetLoader); +}; + + +} diff --git a/ios/src/FilamentViewer.hpp b/ios/src/FilamentViewer.hpp new file mode 100644 index 00000000..d7adcb0f --- /dev/null +++ b/ios/src/FilamentViewer.hpp @@ -0,0 +1,95 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include + +using namespace std; +using namespace filament; +using namespace filament::math; +using namespace gltfio; +using namespace utils; +using namespace camutils; + + +namespace mimetic { + + struct ResourceBuffer { + ResourceBuffer(const void* data, const uint32_t size) : data(data), size(size) {}; + const void* data; + const uint32_t size; + }; + + using LoadResource = std::function; + using FreeResource = std::function; + + class FilamentViewer { + public: + FilamentViewer(void* layer, LoadResource loadResource, FreeResource freeResource); + ~FilamentViewer(); + void loadGltf(const char* const uri); + void loadSkybox(const char* const skyboxUri, const char* const iblUri); + private: + void loadResources(); + void cleanup(); + void* _layer; + LoadResource _loadResource; + FreeResource _freeResource; + + Scene* _scene; + View* _view; + Engine* _engine; + Camera* _mainCamera; + Renderer* _renderer; + + SwapChain* _swapChain; + + Animator* _animator; + + Manipulator* _manipulator; + + AssetLoader* _assetLoader; + FilamentAsset* _asset = nullptr; + NameComponentManager* _ncm; + + Entity _sun; + Texture* _skyboxTexture; + Skybox* _skybox; + Texture* _iblTexture; + IndirectLight* _indirectLight; + + MaterialProvider* _materialProvider; + + gltfio::ResourceLoader* _resourceLoader = nullptr; + bool _recomputeAabb = false; + + bool _actualSize = false; + + float _cameraFocalLength = 0.0f; + + + }; +} diff --git a/lib/filament_controller.dart b/lib/filament_controller.dart new file mode 100644 index 00000000..6135995d --- /dev/null +++ b/lib/filament_controller.dart @@ -0,0 +1,33 @@ +import 'package:flutter/services.dart'; + +abstract class FilamentController { + void onFilamentViewCreated(int id); + Future initialize(); + Future loadSkybox(String skyboxPath, String lightingPath); + Future loadGlb(String path); +} + +class MimeticFilamentController extends FilamentController { + late int _id; + late MethodChannel _channel; + + @override + void onFilamentViewCreated(int id) async { + _id = id; + _channel = MethodChannel("mimetic.app/filament_view_$id"); + } + + @override + Future initialize() async { + await _channel.invokeMethod("initialize"); + } + + @override + Future loadSkybox(String path) { + throw Exception(); + } + + Future loadGlb(String path) { + throw Exception(); + } +} diff --git a/lib/mimetic_filament.dart b/lib/mimetic_filament.dart new file mode 100644 index 00000000..ee57e71a --- /dev/null +++ b/lib/mimetic_filament.dart @@ -0,0 +1,13 @@ + +import 'dart:async'; + +import 'package:flutter/services.dart'; + +class MimeticFilament { + static const MethodChannel _channel = MethodChannel('mimetic_filament'); + + static Future get platformVersion async { + final String? version = await _channel.invokeMethod('getPlatformVersion'); + return version; + } +} diff --git a/lib/mimetic_filament_plugin.dart b/lib/mimetic_filament_plugin.dart new file mode 100644 index 00000000..f5b8e0e1 --- /dev/null +++ b/lib/mimetic_filament_plugin.dart @@ -0,0 +1,19 @@ +// You have generated a new plugin project without +// specifying the `--platforms` flag. A plugin project supports no platforms is generated. +// To add platforms, run `flutter create -t plugin --platforms .` under the same +// directory. You can also find a detailed instruction on how to add platforms in the `pubspec.yaml` at https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin-platforms. + +import 'dart:async'; + +import 'package:flutter/services.dart'; + +class MimeticFilamentPlugin { + static const MethodChannel _channel = MethodChannel('mimetic_filament'); + + static Future get platformVersion async { + final String? version = await _channel.invokeMethod('getPlatformVersion'); + return version; + } + + static void initialize() {} +} diff --git a/lib/view/filament_view.dart b/lib/view/filament_view.dart new file mode 100644 index 00000000..11956a3f --- /dev/null +++ b/lib/view/filament_view.dart @@ -0,0 +1,58 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; + +import '../../filament_controller.dart'; +import 'filament_view_platform.dart'; + +class FilamentView extends FilamentViewPlatform { + static const FILAMENT_VIEW_ID = 'mimetic.app/filament_view'; + + @override + Widget buildView( + int creationId, + FilamentViewCreatedCallback onFilamentViewCreated, + ) { + switch (defaultTargetPlatform) { + case TargetPlatform.android: + return PlatformViewLink( + viewType: FILAMENT_VIEW_ID, + surfaceFactory: + (BuildContext context, PlatformViewController controller) { + return AndroidViewSurface( + controller: controller as AndroidViewController, + gestureRecognizers: const < + Factory>{}, + hitTestBehavior: PlatformViewHitTestBehavior.opaque, + ); + }, + onCreatePlatformView: (PlatformViewCreationParams params) { + return PlatformViewsService.initSurfaceAndroidView( + id: params.id, + viewType: FILAMENT_VIEW_ID, + layoutDirection: TextDirection.ltr, + creationParams: {}, + creationParamsCodec: StandardMessageCodec(), + ) + ..addOnPlatformViewCreatedListener((int id) { + onFilamentViewCreated(id); + params.onPlatformViewCreated(id); + }) + ..create(); + }, + ); + case TargetPlatform.iOS: + return UiKitView( + viewType: FILAMENT_VIEW_ID, + onPlatformViewCreated: (int id) { + onFilamentViewCreated(id); + }, + ); + default: + return Text( + '$defaultTargetPlatform is not yet implemented by Filament plugin.'); + } + } +} diff --git a/lib/view/filament_view_platform.dart b/lib/view/filament_view_platform.dart new file mode 100644 index 00000000..97f05ece --- /dev/null +++ b/lib/view/filament_view_platform.dart @@ -0,0 +1,22 @@ +import 'package:flutter/widgets.dart'; +import 'package:mimetic_filament/view/filament_view.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import '../../filament_controller.dart'; + +typedef void FilamentViewCreatedCallback(int id); + +abstract class FilamentViewPlatform extends PlatformInterface { + FilamentViewPlatform() : super(token: _token); + + static final Object _token = Object(); + static FilamentViewPlatform _instance = FilamentView(); + + static FilamentViewPlatform get instance => _instance; + + Widget buildView( + int creationId, + FilamentViewCreatedCallback onFilamentViewCreated, + ) { + throw UnimplementedError('buildView() has not been implemented.'); + } +} diff --git a/lib/view/filament_widget.dart b/lib/view/filament_widget.dart new file mode 100644 index 00000000..1f31b928 --- /dev/null +++ b/lib/view/filament_widget.dart @@ -0,0 +1,25 @@ +import 'package:flutter/widgets.dart'; + +import '../filament_controller.dart'; +import 'filament_view_platform.dart'; + +int _nextFilamentCreationId = 0; + +class FilamentWidget extends StatefulWidget { + final FilamentController controller; + + const FilamentWidget({Key? key, required this.controller}) : super(key: key); + + @override + _FilamentWidgetState createState() => _FilamentWidgetState(); +} + +class _FilamentWidgetState extends State { + final _viewId = _nextFilamentCreationId++; + + @override + Widget build(BuildContext context) { + return FilamentViewPlatform.instance + .buildView(_viewId, widget.controller.onFilamentViewCreated); + } +} diff --git a/mimetic_avatar.iml b/mimetic_avatar.iml new file mode 100644 index 00000000..429df7da --- /dev/null +++ b/mimetic_avatar.iml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mimetic_filament.iml b/mimetic_filament.iml new file mode 100644 index 00000000..429df7da --- /dev/null +++ b/mimetic_filament.iml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 00000000..dc002d71 --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,168 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.8.2" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.15.0" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.4" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + lints: + dependency: transitive + description: + name: lints + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.11" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.7.0" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0" + plugin_platform_interface: + dependency: "direct main" + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.1" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.4.3" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" +sdks: + dart: ">=2.12.0 <3.0.0" + flutter: ">=1.20.0" diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 00000000..b8cc9294 --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,27 @@ +name: mimetic_filament +description: The 3D rendering layer for the Mimetic app. +version: 0.0.1 +homepage: + +environment: + sdk: ">=2.12.0 <3.0.0" + flutter: ">=1.20.0" + +dependencies: + flutter: + sdk: flutter + plugin_platform_interface: ^2.0.0 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^1.0.0 + +flutter: + plugin: + platforms: + #android: + #package: com.thepipecat.flutter.filament + #pluginClass: FilamentPlugin + ios: + pluginClass: MimeticFilamentPlugin \ No newline at end of file diff --git a/test/mimetic_avatar_test.dart b/test/mimetic_avatar_test.dart new file mode 100644 index 00000000..e5d688ce --- /dev/null +++ b/test/mimetic_avatar_test.dart @@ -0,0 +1,23 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mimetic_filament/mimetic_filament.dart'; + +void main() { + const MethodChannel channel = MethodChannel('mimetic_filament'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + channel.setMockMethodCallHandler((MethodCall methodCall) async { + return '42'; + }); + }); + + tearDown(() { + channel.setMockMethodCallHandler(null); + }); + + test('getPlatformVersion', () async { + expect(await MimeticAvatar.platformVersion, '42'); + }); +} diff --git a/test/mimetic_filament_test.dart b/test/mimetic_filament_test.dart new file mode 100644 index 00000000..037f4e3c --- /dev/null +++ b/test/mimetic_filament_test.dart @@ -0,0 +1,23 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mimetic_filament/mimetic_filament.dart'; + +void main() { + const MethodChannel channel = MethodChannel('mimetic_filament'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + channel.setMockMethodCallHandler((MethodCall methodCall) async { + return '42'; + }); + }); + + tearDown(() { + channel.setMockMethodCallHandler(null); + }); + + test('getPlatformVersion', () async { + expect(await MimeticFilament.platformVersion, '42'); + }); +}